From 74b5b683d0925cab7e6e25b0c82c3dacd1155739 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 25 Mar 2026 18:04:23 +0100 Subject: [PATCH 01/48] Apply new templates --- .badgery.yaml | 12 +- .copier-answers.yml | 27 + .github/actions/download-artifact/action.yml | 50 + .github/actions/github-script/action.yml | 19 + .../actions/setup-easyscience-bot/action.yml | 40 + .github/actions/setup-pixi/action.yml | 44 + .github/actions/upload-artifact/action.yml | 49 + .github/actions/upload-codecov/action.yml | 42 + .github/configs/pages-deployment.json | 6 + .github/configs/rulesets-develop.json | 37 + .github/configs/rulesets-gh-pages.json | 19 + .github/configs/rulesets-master.json | 30 + .github/scripts/backmerge-conflict-issue.js | 69 + .github/workflows/backmerge.yaml | 66 - .github/workflows/backmerge.yml | 109 + .../workflows/{cleanup.yaml => cleanup.yml} | 18 +- .../workflows/{coverage.yaml => coverage.yml} | 81 +- .../{dashboard.yaml => dashboard.yml} | 85 +- .github/workflows/{docs.yaml => docs.yml} | 131 +- .github/workflows/issues-labels.yml | 42 + .github/workflows/lint-format.yml | 132 + .../workflows/{labels.yaml => pr-labels.yml} | 19 +- .github/workflows/pypi-publish.yaml | 43 - .github/workflows/pypi-publish.yml | 46 + .github/workflows/pypi-test.yaml | 97 - .github/workflows/pypi-test.yml | 80 + .github/workflows/quality.yaml | 123 - .../{release-notes.yaml => release-notes.yml} | 23 +- .github/workflows/release-pr.yaml | 57 - .github/workflows/release-pr.yml | 55 + .github/workflows/security.yaml | 39 - .github/workflows/security.yml | 93 + .../{test-trigger.yaml => test-trigger.yml} | 16 +- .github/workflows/test.yaml | 259 -- .github/workflows/test.yml | 268 ++ ...rigger.yaml => tutorial-tests-trigger.yml} | 16 +- ...tutorial-tests.yaml => tutorial-tests.yml} | 49 +- .gitignore | 31 +- .pre-commit-config.yaml | 61 +- .prettierignore | 11 + CONTRIBUTING.md | 494 ++- LICENSE | 2 +- README.md | 59 +- codecov.yml | 5 - docs/{ => docs}/api-reference/analysis.md | 0 docs/{ => docs}/api-reference/core.md | 0 .../api-reference/crystallography.md | 0 .../api-reference/datablocks/experiment.md | 0 .../api-reference/datablocks/structure.md | 0 docs/{ => docs}/api-reference/display.md | 0 docs/{ => docs}/api-reference/index.md | 0 docs/{ => docs}/api-reference/io.md | 0 docs/{ => docs}/api-reference/project.md | 0 docs/{ => docs}/api-reference/summary.md | 0 docs/{ => docs}/api-reference/utils.md | 0 docs/docs/assets/images/favicon.png | Bin 0 -> 34184 bytes docs/docs/assets/images/logo_dark.svg | 41 + docs/docs/assets/images/logo_light.svg | 41 + .../data-acquisition_2d-raw-data.jpg | Bin .../data-acquisition_instrument.png | Bin .../images/user-guide/data-analysis_model.png | Bin .../user-guide/data-analysis_refinement.png | Bin .../user-guide/data-reduction_1d-pattern.png | Bin docs/docs/assets/javascripts/extra.js | 27 + docs/docs/assets/javascripts/mathjax.js | 33 + docs/docs/assets/stylesheets/extra.css | 359 ++ docs/{ => docs}/index.md | 0 .../installation-and-setup/index.md | 0 docs/{ => docs}/introduction/index.md | 0 {tutorials => docs/docs/tutorials}/ed-1.py | 0 {tutorials => docs/docs/tutorials}/ed-10.py | 0 {tutorials => docs/docs/tutorials}/ed-11.py | 0 {tutorials => docs/docs/tutorials}/ed-12.py | 0 {tutorials => docs/docs/tutorials}/ed-13.py | 0 {tutorials => docs/docs/tutorials}/ed-14.py | 0 {tutorials => docs/docs/tutorials}/ed-15.py | 0 {tutorials => docs/docs/tutorials}/ed-16.py | 0 {tutorials => docs/docs/tutorials}/ed-2.py | 0 {tutorials => docs/docs/tutorials}/ed-3.py | 0 {tutorials => docs/docs/tutorials}/ed-4.py | 0 {tutorials => docs/docs/tutorials}/ed-5.py | 0 {tutorials => docs/docs/tutorials}/ed-6.py | 0 {tutorials => docs/docs/tutorials}/ed-7.py | 0 {tutorials => docs/docs/tutorials}/ed-8.py | 0 {tutorials => docs/docs/tutorials}/ed-9.py | 0 {tutorials => docs/docs/tutorials}/index.json | 0 docs/{ => docs}/tutorials/index.md | 0 .../user-guide/analysis-workflow/analysis.md | 0 .../analysis-workflow/experiment.md | 0 .../user-guide/analysis-workflow/index.md | 0 .../user-guide/analysis-workflow/model.md | 0 .../user-guide/analysis-workflow/project.md | 0 .../user-guide/analysis-workflow/summary.md | 0 docs/{ => docs}/user-guide/concept.md | 0 docs/{ => docs}/user-guide/data-format.md | 0 docs/{ => docs}/user-guide/first-steps.md | 0 docs/{ => docs}/user-guide/glossary.md | 0 docs/{ => docs}/user-guide/index.md | 0 docs/{ => docs}/user-guide/parameters.md | 0 .../parameters/_diffrn_radiation.md | 0 .../_diffrn_radiation_wavelength.md | 0 .../user-guide/parameters/_exptl_crystal.md | 0 .../user-guide/parameters/_extinction.md | 0 .../user-guide/parameters/_pd_calib.md | 0 .../user-guide/parameters/atom_site.md | 0 .../user-guide/parameters/background.md | 0 docs/{ => docs}/user-guide/parameters/cell.md | 0 .../user-guide/parameters/expt_type.md | 0 .../user-guide/parameters/instrument.md | 0 .../user-guide/parameters/linked_phases.md | 0 .../user-guide/parameters/pd_meas.md | 0 docs/{ => docs}/user-guide/parameters/peak.md | 0 .../user-guide/parameters/space_group.md | 0 docs/includes/abbreviations.md | 15 + docs/mkdocs.yml | 168 +- docs/overrides/.icons/app.svg | 4 + docs/overrides/.icons/easydiffraction.svg | 3 + docs/overrides/.icons/easyscience.svg | 20 + docs/overrides/.icons/google-colab.svg | 7 + docs/overrides/main.html | 39 + docs/overrides/partials/logo.html | 15 + pixi.lock | 1931 +++++----- pixi.toml | 244 +- prettierrc.toml | 19 +- pyproject.toml | 361 +- pytest.ini | 13 - tools/add_assets_to_docs.sh | 16 - tools/add_license_headers.py | 151 + tools/check_license_headers.py | 45 + tools/cleanup_docs.sh | 10 - tools/create_mkdocs_yml.py | 158 - tools/remove_license_headers.py | 41 + tools/test_scripts.py | 10 +- tools/update_docs_assets.py | 91 + tools/update_github_labels.py | 341 ++ tutorials/data/ed-3.xye | 3099 ----------------- 136 files changed, 4853 insertions(+), 5503 deletions(-) create mode 100644 .copier-answers.yml create mode 100644 .github/actions/download-artifact/action.yml create mode 100644 .github/actions/github-script/action.yml create mode 100644 .github/actions/setup-easyscience-bot/action.yml create mode 100644 .github/actions/setup-pixi/action.yml create mode 100644 .github/actions/upload-artifact/action.yml create mode 100644 .github/actions/upload-codecov/action.yml create mode 100644 .github/configs/pages-deployment.json create mode 100644 .github/configs/rulesets-develop.json create mode 100644 .github/configs/rulesets-gh-pages.json create mode 100644 .github/configs/rulesets-master.json create mode 100644 .github/scripts/backmerge-conflict-issue.js delete mode 100644 .github/workflows/backmerge.yaml create mode 100644 .github/workflows/backmerge.yml rename .github/workflows/{cleanup.yaml => cleanup.yml} (88%) rename .github/workflows/{coverage.yaml => coverage.yml} (50%) rename .github/workflows/{dashboard.yaml => dashboard.yml} (66%) rename .github/workflows/{docs.yaml => docs.yml} (66%) create mode 100644 .github/workflows/issues-labels.yml create mode 100644 .github/workflows/lint-format.yml rename .github/workflows/{labels.yaml => pr-labels.yml} (70%) delete mode 100644 .github/workflows/pypi-publish.yaml create mode 100644 .github/workflows/pypi-publish.yml delete mode 100644 .github/workflows/pypi-test.yaml create mode 100644 .github/workflows/pypi-test.yml delete mode 100644 .github/workflows/quality.yaml rename .github/workflows/{release-notes.yaml => release-notes.yml} (81%) delete mode 100644 .github/workflows/release-pr.yaml create mode 100644 .github/workflows/release-pr.yml delete mode 100644 .github/workflows/security.yaml create mode 100644 .github/workflows/security.yml rename .github/workflows/{test-trigger.yaml => test-trigger.yml} (61%) delete mode 100644 .github/workflows/test.yaml create mode 100644 .github/workflows/test.yml rename .github/workflows/{tutorial-tests-trigger.yaml => tutorial-tests-trigger.yml} (61%) rename .github/workflows/{tutorial-tests.yaml => tutorial-tests.yml} (55%) rename docs/{ => docs}/api-reference/analysis.md (100%) rename docs/{ => docs}/api-reference/core.md (100%) rename docs/{ => docs}/api-reference/crystallography.md (100%) rename docs/{ => docs}/api-reference/datablocks/experiment.md (100%) rename docs/{ => docs}/api-reference/datablocks/structure.md (100%) rename docs/{ => docs}/api-reference/display.md (100%) rename docs/{ => docs}/api-reference/index.md (100%) rename docs/{ => docs}/api-reference/io.md (100%) rename docs/{ => docs}/api-reference/project.md (100%) rename docs/{ => docs}/api-reference/summary.md (100%) rename docs/{ => docs}/api-reference/utils.md (100%) create mode 100644 docs/docs/assets/images/favicon.png create mode 100644 docs/docs/assets/images/logo_dark.svg create mode 100644 docs/docs/assets/images/logo_light.svg rename docs/{ => docs}/assets/images/user-guide/data-acquisition_2d-raw-data.jpg (100%) rename docs/{ => docs}/assets/images/user-guide/data-acquisition_instrument.png (100%) rename docs/{ => docs}/assets/images/user-guide/data-analysis_model.png (100%) rename docs/{ => docs}/assets/images/user-guide/data-analysis_refinement.png (100%) rename docs/{ => docs}/assets/images/user-guide/data-reduction_1d-pattern.png (100%) create mode 100644 docs/docs/assets/javascripts/extra.js create mode 100644 docs/docs/assets/javascripts/mathjax.js create mode 100644 docs/docs/assets/stylesheets/extra.css rename docs/{ => docs}/index.md (100%) rename docs/{ => docs}/installation-and-setup/index.md (100%) rename docs/{ => docs}/introduction/index.md (100%) rename {tutorials => docs/docs/tutorials}/ed-1.py (100%) rename {tutorials => docs/docs/tutorials}/ed-10.py (100%) rename {tutorials => docs/docs/tutorials}/ed-11.py (100%) rename {tutorials => docs/docs/tutorials}/ed-12.py (100%) rename {tutorials => docs/docs/tutorials}/ed-13.py (100%) rename {tutorials => docs/docs/tutorials}/ed-14.py (100%) rename {tutorials => docs/docs/tutorials}/ed-15.py (100%) rename {tutorials => docs/docs/tutorials}/ed-16.py (100%) rename {tutorials => docs/docs/tutorials}/ed-2.py (100%) rename {tutorials => docs/docs/tutorials}/ed-3.py (100%) rename {tutorials => docs/docs/tutorials}/ed-4.py (100%) rename {tutorials => docs/docs/tutorials}/ed-5.py (100%) rename {tutorials => docs/docs/tutorials}/ed-6.py (100%) rename {tutorials => docs/docs/tutorials}/ed-7.py (100%) rename {tutorials => docs/docs/tutorials}/ed-8.py (100%) rename {tutorials => docs/docs/tutorials}/ed-9.py (100%) rename {tutorials => docs/docs/tutorials}/index.json (100%) rename docs/{ => docs}/tutorials/index.md (100%) rename docs/{ => docs}/user-guide/analysis-workflow/analysis.md (100%) rename docs/{ => docs}/user-guide/analysis-workflow/experiment.md (100%) rename docs/{ => docs}/user-guide/analysis-workflow/index.md (100%) rename docs/{ => docs}/user-guide/analysis-workflow/model.md (100%) rename docs/{ => docs}/user-guide/analysis-workflow/project.md (100%) rename docs/{ => docs}/user-guide/analysis-workflow/summary.md (100%) rename docs/{ => docs}/user-guide/concept.md (100%) rename docs/{ => docs}/user-guide/data-format.md (100%) rename docs/{ => docs}/user-guide/first-steps.md (100%) rename docs/{ => docs}/user-guide/glossary.md (100%) rename docs/{ => docs}/user-guide/index.md (100%) rename docs/{ => docs}/user-guide/parameters.md (100%) rename docs/{ => docs}/user-guide/parameters/_diffrn_radiation.md (100%) rename docs/{ => docs}/user-guide/parameters/_diffrn_radiation_wavelength.md (100%) rename docs/{ => docs}/user-guide/parameters/_exptl_crystal.md (100%) rename docs/{ => docs}/user-guide/parameters/_extinction.md (100%) rename docs/{ => docs}/user-guide/parameters/_pd_calib.md (100%) rename docs/{ => docs}/user-guide/parameters/atom_site.md (100%) rename docs/{ => docs}/user-guide/parameters/background.md (100%) rename docs/{ => docs}/user-guide/parameters/cell.md (100%) rename docs/{ => docs}/user-guide/parameters/expt_type.md (100%) rename docs/{ => docs}/user-guide/parameters/instrument.md (100%) rename docs/{ => docs}/user-guide/parameters/linked_phases.md (100%) rename docs/{ => docs}/user-guide/parameters/pd_meas.md (100%) rename docs/{ => docs}/user-guide/parameters/peak.md (100%) rename docs/{ => docs}/user-guide/parameters/space_group.md (100%) create mode 100644 docs/includes/abbreviations.md create mode 100644 docs/overrides/.icons/app.svg create mode 100644 docs/overrides/.icons/easydiffraction.svg create mode 100644 docs/overrides/.icons/easyscience.svg create mode 100644 docs/overrides/.icons/google-colab.svg create mode 100644 docs/overrides/main.html create mode 100644 docs/overrides/partials/logo.html delete mode 100644 pytest.ini delete mode 100755 tools/add_assets_to_docs.sh create mode 100644 tools/add_license_headers.py create mode 100644 tools/check_license_headers.py delete mode 100755 tools/cleanup_docs.sh delete mode 100644 tools/create_mkdocs_yml.py create mode 100644 tools/remove_license_headers.py create mode 100644 tools/update_docs_assets.py create mode 100644 tools/update_github_labels.py delete mode 100644 tutorials/data/ed-3.xye diff --git a/.badgery.yaml b/.badgery.yaml index f22752fa..99bcdc1c 100644 --- a/.badgery.yaml +++ b/.badgery.yaml @@ -5,19 +5,18 @@ cards: - group: Tests type: gh_action title: Code/package tests (GitHub) - file: test.yaml + file: test.yml enabled: true - - group: Tests type: gh_action title: Tutorial tests (GitHub) - file: tutorial-tests.yaml + file: tutorial-tests.yml enabled: true - group: Tests type: gh_action title: Package tests (PyPI) - file: pypi-test.yaml + file: pypi-test.yml enabled: true - group: Code Quality @@ -60,15 +59,14 @@ cards: title: Docstring coverage (interrogate) report: reports/{branch}/coverage-docstring.txt enabled: true - - group: Build & Release type: gh_action title: Publishing (PyPI) - workflow: pypi-publish.yaml + workflow: pypi-publish.yml enabled: true - group: Build & Release type: gh_action title: Docs build/deployment - workflow: docs.yaml + workflow: docs.yml enabled: true diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 00000000..778744fa --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,27 @@ +# WARNING: Do not edit this file manually. +# Any changes will be overwritten by Copier. +_commit: v0.10.1-25-ga5301e9 +_src_path: gh:easyscience/templates +app_docs_url: https://easyscience.github.io/diffraction-app +app_doi: 10.5281/zenodo.18163581 +app_package_name: easydiffraction_app +app_python: '3.13' +app_repo_name: diffraction-app +home_page_url: https://easyscience.github.io/diffraction +home_repo_name: diffraction +lib_docs_url: https://easyscience.github.io/diffraction-lib +lib_doi: 10.5281/zenodo.18163581 +lib_package_name: easydiffraction +lib_python_max: '3.13' +lib_python_min: '3.11' +lib_repo_name: diffraction-lib +project_contact_email: support@easydiffraction.org +project_copyright_years: 2021-2026 +project_extended_description: A software for calculating neutron powder diffraction + patterns based on a structural model and refining its parameters against experimental + data +project_name: EasyDiffraction +project_short_description: Diffraction data analysis +project_shortcut: ED +project_type: both +template_type: lib diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml new file mode 100644 index 00000000..e4fd62f5 --- /dev/null +++ b/.github/actions/download-artifact/action.yml @@ -0,0 +1,50 @@ +name: 'Download artifact' +description: 'Generic wrapper for actions/download-artifact' +inputs: + name: + description: 'Name of the artifact to download' + required: true + + path: + description: 'Destination path' + required: false + default: '.' + + pattern: + description: 'Glob pattern to match artifact names (optional)' + required: false + default: '' + + merge-multiple: + description: 'Merge multiple artifacts into the same directory' + required: false + default: 'false' + + github-token: + description: 'GitHub token for cross-repo download (optional)' + required: false + default: '' + + repository: + description: 'owner/repo for cross-repo download (optional)' + required: false + default: '' + + run-id: + description: 'Workflow run ID for cross-run download (optional)' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} + pattern: ${{ inputs.pattern }} + merge-multiple: ${{ inputs.merge-multiple }} + github-token: ${{ inputs.github-token }} + repository: ${{ inputs.repository }} + run-id: ${{ inputs.run-id }} diff --git a/.github/actions/github-script/action.yml b/.github/actions/github-script/action.yml new file mode 100644 index 00000000..ab32da56 --- /dev/null +++ b/.github/actions/github-script/action.yml @@ -0,0 +1,19 @@ +name: 'GitHub Script' +description: 'Wrapper for actions/github-script' +inputs: + script: + description: 'JavaScript to run' + required: true + + github-token: + description: 'GitHub token (defaults to github.token)' + required: false + default: ${{ github.token }} + +runs: + using: 'composite' + steps: + - uses: actions/github-script@v8 + with: + script: ${{ inputs.script }} + github-token: ${{ inputs.github-token }} diff --git a/.github/actions/setup-easyscience-bot/action.yml b/.github/actions/setup-easyscience-bot/action.yml new file mode 100644 index 00000000..4b28eaf8 --- /dev/null +++ b/.github/actions/setup-easyscience-bot/action.yml @@ -0,0 +1,40 @@ +name: 'Setup EasyScience bot for pushing' +description: 'Create GitHub App token and configure git identity + origin remote' +inputs: + app-id: + description: 'GitHub App ID' + required: true + private-key: + description: 'GitHub App private key (PEM)' + required: true + repositories: + description: 'Additional repositories to grant access to (newline-separated)' + required: false + default: '' + +outputs: + token: + description: 'Installation access token' + value: ${{ steps.app-token.outputs.token }} + +runs: + using: 'composite' + steps: + - name: Create GitHub App installation token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ inputs.app-id }} + private-key: ${{ inputs.private-key }} + repositories: ${{ inputs.repositories }} + + - name: Configure git for pushing + shell: bash + run: | + git config user.name "easyscience[bot]" + git config user.email "${{ inputs.app-id }}+easyscience[bot]@users.noreply.github.com" + + - name: Configure origin remote to use the bot token + shell: bash + run: | + git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git diff --git a/.github/actions/setup-pixi/action.yml b/.github/actions/setup-pixi/action.yml new file mode 100644 index 00000000..167ee623 --- /dev/null +++ b/.github/actions/setup-pixi/action.yml @@ -0,0 +1,44 @@ +name: 'Setup Pixi Environment' +description: 'Sets up pixi with common configuration' +inputs: + environments: + description: 'Pixi environments to setup' + required: false + default: 'default' + activate-environment: + description: 'Environment to activate' + required: false + default: 'default' + run-install: + description: 'Whether to run pixi install' + required: false + default: 'true' + locked: + description: 'Whether to run pixi install --locked' + required: false + default: 'false' + frozen: + description: 'Whether to run pixi install --frozen' + required: false + default: 'true' + cache: + description: 'Whether to use cache' + required: false + default: 'false' + post-cleanup: + description: 'Whether to run post cleanup' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - uses: prefix-dev/setup-pixi@v0.9.4 + with: + environments: ${{ inputs.environments }} + activate-environment: ${{ inputs.activate-environment }} + run-install: ${{ inputs.run-install }} + locked: ${{ inputs.locked }} + frozen: ${{ inputs.frozen }} + cache: ${{ inputs.cache }} + post-cleanup: ${{ inputs.post-cleanup }} diff --git a/.github/actions/upload-artifact/action.yml b/.github/actions/upload-artifact/action.yml new file mode 100644 index 00000000..825ac396 --- /dev/null +++ b/.github/actions/upload-artifact/action.yml @@ -0,0 +1,49 @@ +name: 'Upload artifact' +description: 'Generic wrapper for actions/upload-artifact' +inputs: + name: + description: 'Artifact name' + required: true + + path: + description: 'File(s)/dir(s)/glob(s) to upload (newline-separated)' + required: true + + include-hidden-files: + description: 'Include hidden files' + required: false + default: 'true' + + if-no-files-found: + description: 'warn | error | ignore' + required: false + default: 'error' + + compression-level: + description: '0-9 (0 = no compression)' + required: false + default: '0' + + retention-days: + description: 'Retention in days (optional)' + required: false + default: '' + + overwrite: + description: 'Overwrite an existing artifact with the same name' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} + include-hidden-files: ${{ inputs.include-hidden-files }} + if-no-files-found: ${{ inputs.if-no-files-found }} + compression-level: ${{ inputs.compression-level }} + retention-days: ${{ inputs.retention-days }} + overwrite: ${{ inputs.overwrite }} diff --git a/.github/actions/upload-codecov/action.yml b/.github/actions/upload-codecov/action.yml new file mode 100644 index 00000000..37d6298a --- /dev/null +++ b/.github/actions/upload-codecov/action.yml @@ -0,0 +1,42 @@ +name: 'Upload coverage to Codecov' +description: 'Generic wrapper for codecov/codecov-action@v5' + +inputs: + name: + description: 'Codecov upload name' + required: true + + flags: + description: 'Codecov flags' + required: false + default: '' + + files: + description: 'Coverage report files' + required: true + + fail_ci_if_error: + description: 'Fail CI if upload fails' + required: false + default: 'true' + + verbose: + description: 'Enable verbose output' + required: false + default: 'true' + + token: + description: 'Codecov token' + required: true + +runs: + using: composite + steps: + - uses: codecov/codecov-action@v5 + with: + name: ${{ inputs.name }} + flags: ${{ inputs.flags }} + files: ${{ inputs.files }} + fail_ci_if_error: ${{ inputs.fail_ci_if_error }} + verbose: ${{ inputs.verbose }} + token: ${{ inputs.token }} diff --git a/.github/configs/pages-deployment.json b/.github/configs/pages-deployment.json new file mode 100644 index 00000000..c0d3fbee --- /dev/null +++ b/.github/configs/pages-deployment.json @@ -0,0 +1,6 @@ +{ + "source": { + "branch": "gh-pages", + "path": "/" + } +} diff --git a/.github/configs/rulesets-develop.json b/.github/configs/rulesets-develop.json new file mode 100644 index 00000000..04489e52 --- /dev/null +++ b/.github/configs/rulesets-develop.json @@ -0,0 +1,37 @@ +{ + "name": "develop branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["refs/heads/develop"], + "exclude": [] + } + }, + "bypass_actors": [ + { + "actor_id": 2476259, + "actor_type": "Integration", + "bypass_mode": "always" + } + ], + "rules": [ + { + "type": "non_fast_forward" + }, + { + "type": "deletion" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["squash"], + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_approving_review_count": 0, + "required_review_thread_resolution": false + } + } + ] +} diff --git a/.github/configs/rulesets-gh-pages.json b/.github/configs/rulesets-gh-pages.json new file mode 100644 index 00000000..ebf38928 --- /dev/null +++ b/.github/configs/rulesets-gh-pages.json @@ -0,0 +1,19 @@ +{ + "name": "gh-pages branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["refs/heads/gh-pages"], + "exclude": [] + } + }, + "rules": [ + { + "type": "non_fast_forward" + }, + { + "type": "deletion" + } + ] +} diff --git a/.github/configs/rulesets-master.json b/.github/configs/rulesets-master.json new file mode 100644 index 00000000..f658a5c6 --- /dev/null +++ b/.github/configs/rulesets-master.json @@ -0,0 +1,30 @@ +{ + "name": "master branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["~DEFAULT_BRANCH"], + "exclude": [] + } + }, + "rules": [ + { + "type": "non_fast_forward" + }, + { + "type": "deletion" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["merge"], + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_approving_review_count": 0, + "required_review_thread_resolution": false + } + } + ] +} diff --git a/.github/scripts/backmerge-conflict-issue.js b/.github/scripts/backmerge-conflict-issue.js new file mode 100644 index 00000000..f6bd98b5 --- /dev/null +++ b/.github/scripts/backmerge-conflict-issue.js @@ -0,0 +1,69 @@ +module.exports = async ({ github, context, core }) => { + // Repo context + const owner = context.repo.owner + const repo = context.repo.repo + + // Link to the exact workflow run that detected the conflict + const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}` + + // We use a *stable title* so we can find/reuse the same "conflict tracker" issue + // instead of creating a new issue on every failed run. + const title = 'Backmerge conflict: master → develop' + + // Comment/issue body includes the run URL so maintainers can jump straight to logs. + const body = [ + 'Automatic backmerge failed due to merge conflicts.', + '', + `Workflow run: ${runUrl}`, + '', + 'Manual resolution required.', + ].join('\n') + + // Label applied to the tracker issue (assumed to already exist in the repo). + const label = '[bot] backmerge' + + // Search issues by title across *open and closed* issues. + // Why: if the conflict was resolved previously and the issue was closed, + // we prefer to reopen it and append a new comment instead of creating duplicates. + const q = `repo:${owner}/${repo} is:issue in:title "${title}"` + const search = await github.rest.search.issuesAndPullRequests({ + q, + per_page: 10, + }) + + // Pick the first exact-title match (search can return partial matches). + const existing = search.data.items.find((i) => i.title === title) + + if (existing) { + // If a tracker issue exists, reuse it: + // - reopen it if needed + // - add a comment with the new run URL + if (existing.state === 'closed') { + await github.rest.issues.update({ + owner, + repo, + issue_number: existing.number, + state: 'open', + }) + } + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: existing.number, + body, + }) + + core.notice(`Conflict issue updated: #${existing.number}`) + return + } + + // No tracker issue exists yet -> create the first one. + await github.rest.issues.create({ + owner, + repo, + title, + body, + labels: [label], + }) +} diff --git a/.github/workflows/backmerge.yaml b/.github/workflows/backmerge.yaml deleted file mode 100644 index f69b5379..00000000 --- a/.github/workflows/backmerge.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# This workflow automatically merges `master` into `develop` whenever a new version tag is pushed (v*). -# -# Key points: -# - Directly merges master into develop without creating a PR. -# - Skips CI on the merge commit using [skip ci] in the commit message. -# - The code being merged has already been tested as part of the release process. -# - This ensures develop stays up-to-date with release changes (version bumps, etc.). -# -# Required repo config: -# https://github.com/organizations/easyscience/settings/secrets/actions -# https://github.com/organizations/easyscience/settings/variables/actions -# - Actions secret: EASYSCIENCE_APP_KEY (GitHub App private key PEM) -# - Actions variable: EASYSCIENCE_APP_ID (GitHub App ID) -# The GitHub App must be added to the develop branch ruleset bypass list. - -name: Backmerge (master -> develop) - -on: - push: - tags: ['v*'] - -permissions: - contents: write - -jobs: - backmerge: - runs-on: ubuntu-latest - - steps: - - name: Create GitHub App installation token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.EASYSCIENCE_APP_ID }} - private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} - - - name: Checkout repository - uses: actions/checkout@v5 - with: - fetch-depth: 0 - token: ${{ steps.app-token.outputs.token }} - - - name: Configure git for pushing - run: | - git config user.name "easyscience[bot]" - git config user.email "${{ vars.EASYSCIENCE_APP_ID }}+easyscience[bot]@users.noreply.github.com" - - - name: Merge master into develop - run: | - set -euo pipefail - - TAG='${{ github.ref_name }}' - - # Ensure local develop branch exists and is up-to-date with origin - git fetch origin develop:develop - # Switch to develop branch - git checkout develop - - # Merge master into develop (no fast-forward to preserve history) - # Use [skip ci] to avoid triggering CI - the code was already tested on master - git merge origin/master --no-ff -m "Backmerge: ${TAG} from master into develop [skip ci]" - - # Push the merge commit to develop - git push origin develop - - echo "✅ Successfully merged master (${TAG}) into develop" diff --git a/.github/workflows/backmerge.yml b/.github/workflows/backmerge.yml new file mode 100644 index 00000000..47b3384a --- /dev/null +++ b/.github/workflows/backmerge.yml @@ -0,0 +1,109 @@ +# This workflow automatically merges `master` into `develop` whenever a +# new version release with a tag is published. It can also be triggered +# manually via workflow_dispatch for cases where an automatic backmerge +# is needed outside of the standard release process. +# If a merge conflict occurs, the workflow creates an issue to notify +# maintainers for manual resolution. + +name: Backmerge (master → develop) + +on: + release: + types: [published, prereleased] + workflow_dispatch: + +permissions: + contents: write + issues: write + +concurrency: + group: backmerge-master-into-develop + cancel-in-progress: false + +jobs: + backmerge: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout repository (for local actions) + uses: actions/checkout@v5 + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + repositories: ${{ github.event.repository.name }} + + - name: Checkout repository (with bot token) + uses: actions/checkout@v5 + with: + fetch-depth: 0 + token: ${{ steps.bot.outputs.token }} + + - name: Configure git identity + run: | + git config user.name "easyscience[bot]" + git config user.email "${{ vars.EASYSCIENCE_APP_ID }}+easyscience[bot]@users.noreply.github.com" + + - name: Set merge message + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + MESSAGE="Backmerge: master into develop (manual) [skip ci]" + else + TAG="${{ github.event.release.tag_name }}" + MESSAGE="Backmerge: master (${TAG}) into develop [skip ci]" + fi + + echo "MESSAGE=$MESSAGE" >> "$GITHUB_ENV" + echo "message=$MESSAGE" >> "$GITHUB_OUTPUT" + echo "📝 Merge message: $MESSAGE" | tee -a "$GITHUB_STEP_SUMMARY" + + - name: Prepare branches + run: | + git fetch origin master develop + git checkout -B develop origin/develop + + - name: Check if develop is already up-to-date + id: up_to_date + run: | + if git merge-base --is-ancestor origin/master develop; then + echo "value=true" >> "$GITHUB_OUTPUT" + echo "ℹ️ Develop is already up-to-date with master" | tee -a "$GITHUB_STEP_SUMMARY" + else + echo "value=false" >> "$GITHUB_OUTPUT" + fi + + - name: Try merge master into develop + id: merge + if: steps.up_to_date.outputs.value == 'false' + continue-on-error: true + run: | + if ! git merge origin/master --no-ff -m "${MESSAGE}"; then + echo "conflict=true" >> "$GITHUB_OUTPUT" + echo "❌ Backmerge conflict detected." | tee -a "$GITHUB_STEP_SUMMARY" + git status --porcelain || true + exit 0 + fi + + echo "conflict=false" >> "$GITHUB_OUTPUT" + echo "✅ Merge commit created." | tee -a "$GITHUB_STEP_SUMMARY" + + - name: Push to develop (if merge succeeded) + if: + steps.up_to_date.outputs.value == 'false' && steps.merge.outputs.conflict == + 'false' + run: | + git push origin develop + echo "🚀 Backmerge successful: master → develop" | tee -a "$GITHUB_STEP_SUMMARY" + + - name: Create issue (if merge failed with conflicts) + if: steps.merge.outputs.conflict == 'true' + uses: ./.github/actions/github-script + with: + github-token: ${{ steps.bot.outputs.token }} + script: | + const run = require('./.github/scripts/backmerge-conflict-issue.js') + await run({ github, context, core }) diff --git a/.github/workflows/cleanup.yaml b/.github/workflows/cleanup.yml similarity index 88% rename from .github/workflows/cleanup.yaml rename to .github/workflows/cleanup.yml index eef184db..7305679b 100644 --- a/.github/workflows/cleanup.yaml +++ b/.github/workflows/cleanup.yml @@ -22,8 +22,8 @@ on: default: 6 delete_workflow_pattern: description: - 'The name or filename of the workflow. if not set then it will target - all workflows.' + 'The name or filename of the workflow. if not set then it will target all + workflows.' required: false delete_workflow_by_state_pattern: description: @@ -40,8 +40,8 @@ on: - disabled_manually delete_run_by_conclusion_pattern: description: - 'Remove workflow by conclusion: action_required, cancelled, failure, - skipped, success' + 'Remove workflow by conclusion: action_required, cancelled, failure, skipped, + success' required: true default: 'All' type: choice @@ -53,8 +53,13 @@ on: - skipped - success dry_run: - description: 'Only log actions, do not perform any delete operations.' + description: 'Only log actions, do not perform any delete operations (dry run).' required: false + default: 'false' + type: choice + options: + - 'false' + - 'true' jobs: del-runs: @@ -71,8 +76,7 @@ jobs: repository: ${{ github.repository }} retain_days: ${{ github.event.inputs.days }} keep_minimum_runs: ${{ github.event.inputs.minimum_runs }} - delete_workflow_pattern: - ${{ github.event.inputs.delete_workflow_pattern }} + delete_workflow_pattern: ${{ github.event.inputs.delete_workflow_pattern }} delete_workflow_by_state_pattern: ${{ github.event.inputs.delete_workflow_by_state_pattern }} delete_run_by_conclusion_pattern: diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yml similarity index 50% rename from .github/workflows/coverage.yaml rename to .github/workflows/coverage.yml index 96ae3386..cd9ff1e0 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yml @@ -3,6 +3,8 @@ name: Coverage checks on: # Trigger the workflow on push push: + # Do not run on version tags (those are handled by other workflows) + tags-ignore: ['v*'] # Trigger the workflow on pull request pull_request: # Allows you to run this workflow manually from the Actions tab @@ -16,8 +18,7 @@ permissions: # Allow only one concurrent workflow, skipping runs queued between the run # in-progress and latest queued. And cancel in-progress runs. concurrency: - group: - ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true # Set the environment variables to be used in all jobs defined in this workflow @@ -34,18 +35,7 @@ jobs: uses: actions/checkout@v5 - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: pixi run dev + uses: ./.github/actions/setup-pixi - name: Run docstring coverage run: pixi run docstring-coverage @@ -59,34 +49,21 @@ jobs: uses: actions/checkout@v5 - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: pixi run dev + uses: ./.github/actions/setup-pixi - name: Run unit tests with coverage run: pixi run unit-tests-coverage --cov-report=xml:coverage-unit.xml - name: Upload unit tests coverage to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@v5 + uses: ./.github/actions/upload-codecov with: name: unit-tests-job flags: unittests files: ./coverage-unit.xml - fail_ci_if_error: true - verbose: true token: ${{ secrets.CODECOV_TOKEN }} - # Job 3: Run integration tests with coverage and upload to Codecov + # Job 2: Run integration tests with coverage and upload to Codecov integration-tests-coverage: runs-on: ubuntu-latest @@ -95,53 +72,23 @@ jobs: uses: actions/checkout@v5 - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: pixi run dev + uses: ./.github/actions/setup-pixi - name: Run integration tests with coverage run: - pixi run integration-tests-coverage - --cov-report=xml:coverage-integration.xml + pixi run integration-tests-coverage --cov-report=xml:coverage-integration.xml - name: Upload integration tests coverage to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@v5 + uses: ./.github/actions/upload-codecov with: name: integration-tests-job flags: integration files: ./coverage-integration.xml - fail_ci_if_error: true - verbose: true token: ${{ secrets.CODECOV_TOKEN }} - # Job 4: Trigger dashboard build - dashboard-build-trigger: + # Job 4: Build and publish dashboard (reusable workflow) + run-reusable-workflows: needs: [docstring-coverage, unit-tests-coverage, integration-tests-coverage] # depend on the previous jobs - - runs-on: ubuntu-latest - - steps: - - name: Check-out repository - uses: actions/checkout@v5 - - - name: Trigger dashboard build - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: "dashboard.yaml", - ref: "${{ env.CI_BRANCH }}" - }); + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.github/workflows/dashboard.yaml b/.github/workflows/dashboard.yml similarity index 66% rename from .github/workflows/dashboard.yaml rename to .github/workflows/dashboard.yml index 71d64401..5159f71b 100644 --- a/.github/workflows/dashboard.yaml +++ b/.github/workflows/dashboard.yml @@ -4,12 +4,8 @@ on: workflow_dispatch: workflow_call: -# Allow only one concurrent workflow, skipping runs queued between the run -# in-progress and latest queued. And cancel in-progress runs. -concurrency: - group: - ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true +permissions: + contents: read # Set the environment variables to be used in all jobs defined in this workflow env: @@ -24,78 +20,83 @@ jobs: runs-on: ubuntu-latest steps: - # Create GitHub App token for pushing to external dashboard repo. - # The 'repositories' parameter is required to grant access to repos - # other than the one where the workflow is running. - - name: Create GitHub App installation token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.EASYSCIENCE_APP_ID }} - private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} - repositories: | - ${{ github.event.repository.name }} - dashboard - - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies + uses: ./.github/actions/setup-pixi + + - name: Install badgery shell: bash - run: | - pixi run dev - pixi add --pypi --git https://github.com/enhantica/badgery badgery + run: pixi add --pypi --git https://github.com/enhantica/badgery badgery - name: Run docstring coverage and code complexity/maintainability checks run: | - for BRANCH in ${{ env.DEFAULT_BRANCH }} ${{ env.DEVELOP_BRANCH }} ${{ env.CI_BRANCH }}; do - echo "=== Processing branch $BRANCH ===" + for BRANCH in $DEFAULT_BRANCH $DEVELOP_BRANCH $CI_BRANCH; do + echo + echo "🔹🔸🔹🔸🔹 Processing branch $BRANCH 🔹🔸🔹🔸🔹" if [ -d "../$BRANCH" ]; then echo "Branch $BRANCH already processed, skipping" continue fi + git worktree add ../$BRANCH origin/$BRANCH mkdir -p reports/$BRANCH + echo "Docstring coverage for branch $BRANCH" pixi run interrogate -c pyproject.toml --fail-under=0 ../$BRANCH/src > reports/$BRANCH/coverage-docstring.txt + echo "Cyclomatic complexity for branch $BRANCH" pixi run radon cc -s -j ../$BRANCH/src > reports/$BRANCH/cyclomatic-complexity.json + echo "Maintainability index for branch $BRANCH" pixi run radon mi -j ../$BRANCH/src > reports/$BRANCH/maintainability-index.json + echo "Raw metrics for branch $BRANCH" pixi run radon raw -s -j ../$BRANCH/src > reports/$BRANCH/raw-metrics.json done - name: Generate dashboard HTML run: > - pixi run python -m badgery --config .badgery.yaml --repo ${{ - github.repository }} --branch ${{ env.CI_BRANCH }} --output index.html + pixi run python -m badgery --config .badgery.yaml --repo ${{ github.repository + }} --branch ${{ env.CI_BRANCH }} --output index.html - name: Prepare publish directory run: | mkdir -p _dashboard_publish/${{ env.REPO_NAME }}/${{ env.CI_BRANCH }} cp index.html _dashboard_publish/${{ env.REPO_NAME }}/${{ env.CI_BRANCH }} + # Create GitHub App token for pushing to external dashboard repo. + # The 'repositories' parameter is required to grant access to repos + # other than the one where the workflow is running. + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + repositories: | + ${{ github.event.repository.name }} + dashboard + + # Publish to external dashboard repository with retry logic. + # Retry is needed to handle transient GitHub API/authentication issues + # that occasionally cause 403 errors when multiple workflows push concurrently. + # Uses personal_token (not github_token) as GITHUB_TOKEN cannot access external repos. - name: Publish to main branch of ${{ github.repository }} - uses: peaceiris/actions-gh-pages@v3 + uses: Wandalen/wretry.action@v3.8.0 with: - external_repository: ${{ env.REPO_OWNER }}/dashboard - publish_branch: ${{ env.DEFAULT_BRANCH }} - personal_token: ${{ steps.app-token.outputs.token }} - publish_dir: ./_dashboard_publish - keep_files: true + attempt_limit: 3 + attempt_delay: 15000 # 15 seconds between retries + action: peaceiris/actions-gh-pages@v4 + with: | + publish_dir: ./_dashboard_publish + keep_files: true + external_repository: ${{ env.REPO_OWNER }}/dashboard + publish_branch: master + personal_token: ${{ steps.bot.outputs.token }} - name: Add dashboard link to summary run: | diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yml similarity index 66% rename from .github/workflows/docs.yaml rename to .github/workflows/docs.yml index f9bddf67..66b06e1d 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yml @@ -14,16 +14,16 @@ name: Docs build and deployment on: - # Trigger the workflow on pull request - pull_request: - # Selected branches - branches: [master, main, develop] # Trigger the workflow on push push: # Selected branches - branches: [master, main, develop] + branches: [develop] # master and main are already verified in PR # Runs on creating a new tag starting with 'v', e.g. 'v1.0.3' tags: ['v*'] + # Trigger the workflow on pull request + pull_request: + # Selected branches + branches: [master, main, develop] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -53,11 +53,7 @@ jobs: # Single job that builds and deploys documentation. # Uses macOS runner for consistent Plotly chart rendering. build-deploy-docs: - strategy: - matrix: - os: [macos-14] - - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest # macos-latest permissions: contents: write # Required for pushing to the gh-pages branch @@ -83,22 +79,11 @@ jobs: fi echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "$GITHUB_ENV" echo "DOCS_VERSION=${DOCS_VERSION}" >> "$GITHUB_ENV" - echo "DEPLOYMENT_URL=https://easyscience.github.io/${{ github.event.repository.name }}/${DOCS_VERSION}" >> "$GITHUB_ENV" - - # Create GitHub App token for pushing to gh-pages as easyscience[bot]. - - name: Create GitHub App installation token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.EASYSCIENCE_APP_ID }} - private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} # Check out the repository source code. # Note: The gh-pages branch is fetched separately later for mike deployment. - - name: Check-out repository + - name: Checkout repository uses: actions/checkout@v5 - with: - token: ${{ steps.app-token.outputs.token }} # Activate dark mode to create documentation with Plotly charts in dark mode # Need a better solution to automatically switch the chart colour theme based on the mkdocs material switcher @@ -114,83 +99,53 @@ jobs: # Set up the pixi package manager and install dependencies from pixi.toml. # Uses frozen lockfile to ensure reproducible builds. - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - # Install additional development dependencies (e.g., pre-commit hooks, dev tools). - - name: Install and setup development dependencies - shell: bash - run: pixi run dev - - # Clone shared documentation assets and branding resources from external repositories. - # These contain common MkDocs configuration, templates, stylesheets, and images. - - name: Clone easyscience/assets-docs and easyscience/assets-branding - run: | - cd .. - git clone https://github.com/easyscience/assets-docs.git - git clone https://github.com/easyscience/assets-branding.git - - # Copy assets from the cloned repositories into the docs/ directory. - # This includes stylesheets, images, templates, and other shared resources. - - name: Add files from cloned repositories - run: pixi run docs-assets - - # Convert Python scripts in the tutorials/ directory to Jupyter notebooks. - # This step also strips any existing output from the notebooks and prepares - # them for documentation. - - name: Convert tutorial scripts to notebooks - run: pixi run notebook-prepare - - # Pre-import the main package to trigger Matplotlib font cache building. - # This avoids "Matplotlib is building the font cache" messages during notebook execution. + uses: ./.github/actions/setup-pixi + # Pre-import the main package to exclude info messages from the docs + # E.g., Matplotlib may print messages to stdout/stderr when first + # imported. This step allows to avoid "Matplotlib is building the font + # cache" messages during notebook execution. - name: Pre-build site step run: pixi run python -c "import easydiffraction" + # Prepare the Jupyter notebooks for documentation (strip output, etc.). + - name: Prepare notebooks + run: pixi run notebook-prepare + # Execute all Jupyter notebooks to generate output cells (plots, tables, etc.). # Uses multiple cores for parallel execution to speed up the process. - name: Run notebooks - # if: false # Temporarily disabled to speed up the docs build + # if: false # Temporarily disabled to speed up the docs build run: pixi run notebook-exec - # Move the executed notebooks to docs/tutorials/ directory - # so they can be included in the documentation site. - - name: Move notebooks to docs/tutorials - run: pixi run docs-notebooks - - # Create the mkdocs.yml configuration file - # The file is created by merging two files: - # - assets-docs/mkdocs.yml - the common configuration (theme, plugins, etc.) - # - docs/mkdocs.yml - the project-specific configuration (project name, TOC, etc.) - - name: Create mkdocs.yml file - run: pixi run docs-config - # Build the static files for the documentation site for local inspection # Input: docs/ directory containing the Markdown files # Output: site/ directory containing the generated HTML files - name: Build site for local check - run: pixi run docs-local + run: pixi run docs-build-local # Upload the static files from the site/ directory to be used for # local check - name: Upload built site as artifact - uses: actions/upload-artifact@v4 + uses: ./.github/actions/upload-artifact + with: + name: site-local_easydiffraction-lib-${{ env.RELEASE_VERSION }} + path: docs/site/ + + # Create GitHub App token for pushing to gh-pages as easyscience[bot]. + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot with: - name: site-local_edl-${{ env.RELEASE_VERSION }} - path: site/ - if-no-files-found: 'error' - compression-level: 0 + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} - # Configure git user for mike to commit and push to gh-pages branch. + # Configure git identity and remote URL so mike pushes as easyscience[bot]. - name: Configure git for pushing run: | + set -euo pipefail git config user.name "easyscience[bot]" git config user.email "${{ vars.EASYSCIENCE_APP_ID }}+easyscience[bot]@users.noreply.github.com" + git remote set-url origin "https://x-access-token:${{ steps.bot.outputs.token }}@github.com/${{ github.repository }}.git" # Fetch the gh-pages branch to ensure mike has the latest remote state. # This is required because the checkout step only fetches the source branch, @@ -206,10 +161,24 @@ jobs: # Also sets 'latest' as the default version for the version selector. - name: Rebuild and deploy docs with mike run: | + # Exit on error (-e), undefined vars (-u), and pipeline failures (pipefail) + set -euo pipefail + + REPO_NAME="${{ github.event.repository.name }}" + BASE_URL="https://easyscience.github.io/${REPO_NAME}" + + # Deploy the release version and update the "latest" alias if [[ "${IS_RELEASE_TAG}" == "true" ]]; then - pixi run docs-deploy "${RELEASE_VERSION#v}" latest + pixi run docs-deploy-pre "${RELEASE_VERSION#v}" latest + pixi run docs-set-default-pre latest + DEPLOYMENT_URL="${BASE_URL}/latest" + + # Deploy/update the "dev" alias (or whatever your convention is) else - pixi run docs-deploy dev + pixi run docs-deploy-pre dev + DEPLOYMENT_URL="${BASE_URL}/dev" + fi - pixi run docs-set-default latest - echo "🔗 deployment url [${{ env.DEPLOYMENT_URL }}](${{ env.DEPLOYMENT_URL }})" >> $GITHUB_STEP_SUMMARY + + # Add links to the action summary page for easy access + echo "🔗 deployment url [${DEPLOYMENT_URL}](${DEPLOYMENT_URL})" >> "${GITHUB_STEP_SUMMARY}" diff --git a/.github/workflows/issues-labels.yml b/.github/workflows/issues-labels.yml new file mode 100644 index 00000000..3a60cdd7 --- /dev/null +++ b/.github/workflows/issues-labels.yml @@ -0,0 +1,42 @@ +# Verifies if an issue has at least one of the `[scope]` and one of the +# `[priority]` labels. If not, the bot adds labels with a warning emoji +# to indicate that those labels need to be added. + +name: Issue labels check + +on: + issues: + types: [opened, labeled, unlabeled] + +permissions: + issues: write + +jobs: + check-labels: + runs-on: ubuntu-latest + + steps: + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + - name: Check for required [scope] label + uses: trstringer/require-label-prefix@v1 + with: + secret: ${{ steps.bot.outputs.token }} + prefix: '[scope]' + labelSeparator: ' ' + addLabel: true + defaultLabel: '[scope] ⚠️ label needed' + + - name: Check for required [priority] label + uses: trstringer/require-label-prefix@v1 + with: + secret: ${{ steps.bot.outputs.token }} + prefix: '[priority]' + labelSeparator: ' ' + addLabel: true + defaultLabel: '[priority] ⚠️ label needed' diff --git a/.github/workflows/lint-format.yml b/.github/workflows/lint-format.yml new file mode 100644 index 00000000..457d67f4 --- /dev/null +++ b/.github/workflows/lint-format.yml @@ -0,0 +1,132 @@ +# The workflow checks +# - the validity of pyproject.toml, +# - the presence and correctness of SPDX license headers, +# - linting and formatting of Python code, +# - linting and formatting of docstrings in Python code, +# - formatting of non-Python files (like markdown and toml). +# - linting of Python code in Jupyter notebooks (for library template). +# +# A summary of the checks is added to the GitHub Actions summary. + +name: Lint and format checks + +on: + # Trigger the workflow on push + push: + branches-ignore: [master, main] # Already verified in PR + # Do not run this workflow on creating a new tag starting with + # 'v', e.g. 'v1.0.3' (see publish-pypi.yml) + tags-ignore: ['v*'] + # Trigger the workflow on pull request + pull_request: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Allow only one concurrent workflow, skipping runs queued between the run +# in-progress and latest queued. And cancel in-progress runs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + lint-format: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Run post-install developer steps + run: pixi run post-install + + - name: Check validity of pyproject.toml + id: pyproject + continue-on-error: true + shell: bash + run: pixi run pyproject-check + + - name: Check SPDX license headers + id: license_headers + continue-on-error: true + shell: bash + run: pixi run license-check + + - name: Check linting of Python code + id: py_lint + continue-on-error: true + shell: bash + run: pixi run py-lint-check + + - name: Check formatting of Python code + id: py_format + continue-on-error: true + shell: bash + run: pixi run py-format-check + + - name: Check linting of docstrings in Python code + id: docstring_lint + continue-on-error: true + shell: bash + run: pixi run docstring-lint-check + + - name: Check formatting of docstrings in Python code + id: docstring_format + continue-on-error: true + shell: bash + run: pixi run docstring-format-check + + - name: Check formatting of non-Python files (md, toml, etc.) + id: nonpy_format + continue-on-error: true + shell: bash + run: pixi run nonpy-format-check + + - name: Check linting of Python code in Jupyter notebooks (ipynb) + id: notebook_lint + continue-on-error: true + shell: bash + run: pixi run notebook-lint-check + + # Add summary + - name: Add quality checks summary + if: always() + shell: bash + run: | + { + echo "## 🧪 Checks Summary" + echo "" + echo "| Check | Status |" + echo "|-------|--------|" + echo "| pyproject.toml | ${{ steps.pyproject.outcome == 'success' && '✅' || '❌' }} |" + echo "| license headers | ${{ steps.license_headers.outcome == 'success' && '✅' || '❌' }} |" + echo "| py lint | ${{ steps.py_lint.outcome == 'success' && '✅' || '❌' }} |" + echo "| py format | ${{ steps.py_format.outcome == 'success' && '✅' || '❌' }} |" + echo "| docstring lint | ${{ steps.docstring_lint.outcome == 'success' && '✅' || '❌' }} |" + echo "| docstring format | ${{ steps.docstring_format.outcome == 'success' && '✅' || '❌' }} |" + echo "| nonpy format | ${{ steps.nonpy_format.outcome == 'success' && '✅' || '❌' }} |" + echo "| notebooks lint | ${{ steps.notebook_lint.outcome == 'success' && '✅' || '❌' }} |" + } >> "$GITHUB_STEP_SUMMARY" + + # Fail job if any check failed + - name: Fail job if any check failed + if: | + steps.pyproject.outcome == 'failure' + || steps.license_headers.outcome == 'failure' + || steps.py_lint.outcome == 'failure' + || steps.py_format.outcome == 'failure' + || steps.docstring_lint.outcome == 'failure' + || steps.docstring_format.outcome == 'failure' + || steps.nonpy_format.outcome == 'failure' + || steps.notebook_lint.outcome == 'failure' + shell: bash + run: exit 1 diff --git a/.github/workflows/labels.yaml b/.github/workflows/pr-labels.yml similarity index 70% rename from .github/workflows/labels.yaml rename to .github/workflows/pr-labels.yml index 1e8bd846..642cd318 100644 --- a/.github/workflows/labels.yaml +++ b/.github/workflows/pr-labels.yml @@ -1,7 +1,16 @@ # Verifies if a pull request has at least one label from a set of valid # labels before it can be merged. +# +# NOTE: +# This workflow may be triggered twice in quick succession when a PR is +# created: +# 1) `opened` — when the pull request is initially created +# 2) `labeled` — if labels are added immediately after creation +# (e.g. by manual labeling, another workflow, or GitHub App). +# +# These are separate GitHub events, so two workflow runs can be started. -name: PR label checks +name: PR labels check on: pull_request_target: @@ -11,15 +20,17 @@ permissions: pull-requests: read jobs: - require-label: + check-labels: runs-on: ubuntu-latest + steps: - - name: Validate required labels + - name: Check for valid labels run: | PR_LABELS=$(echo '${{ toJson(github.event.pull_request.labels.*.name) }}' | jq -r '.[]') + echo "Current PR labels: $PR_LABELS" VALID_LABELS=( - "[maintainer] auto-pull-request" + "[bot] release" "[scope] bug" "[scope] documentation" "[scope] enhancement" diff --git a/.github/workflows/pypi-publish.yaml b/.github/workflows/pypi-publish.yaml deleted file mode 100644 index 7767f8d2..00000000 --- a/.github/workflows/pypi-publish.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# Builds a Python package and publish it to PyPI when a new tag is -# created. - -name: PyPI publishing - -on: - # Runs on creating a new tag starting with 'v', e.g. 'v1.0.3' - push: - tags: ['v*'] - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - pypi-publish: - runs-on: ubuntu-latest - - steps: - - name: Check-out repository - uses: actions/checkout@v5 - with: - fetch-depth: '0' # full history with tags to get the version number by versioningit - - - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: pixi run dev - - - name: Create Python package - run: pixi run python -m build - - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml new file mode 100644 index 00000000..e9986cac --- /dev/null +++ b/.github/workflows/pypi-publish.yml @@ -0,0 +1,46 @@ +# Builds a Python package and publish it to PyPI when a new tag is +# created. + +name: PyPI publishing + +on: + # Runs on creating a new tag starting with 'v', e.g. 'v1.0.3' + push: + tags: ['v*'] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + pypi-publish: + runs-on: ubuntu-latest + + permissions: + contents: read + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + + steps: + - name: Check-out repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 # full history with tags to get the version number by versioningit + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + # Build the Python package (to dist/ folder) + - name: Create Python package + run: pixi run default-build + + # Publish the package to PyPI (from dist/ folder) + # Instead of publishing with personal access token, we use + # GitHub Actions OIDC to get a short-lived token from PyPI. + # New publisher must be previously configured in PyPI at + # https://pypi.org/manage/project/easydiffraction/settings/publishing/ + # Use the following data: + # Owner: easyscience + # Repository name: diffraction-lib + # Workflow name: pypi-publish.yml + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: 'dist' diff --git a/.github/workflows/pypi-test.yaml b/.github/workflows/pypi-test.yaml deleted file mode 100644 index e083846e..00000000 --- a/.github/workflows/pypi-test.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: PyPI package tests - -on: - # Run daily, at 00:00. - schedule: - - cron: '0 0 * * *' - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Allow only one concurrent workflow, skipping runs queued between the run -# in-progress and latest queued. And cancel in-progress runs. -concurrency: - group: - ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -# Set the environment variables to be used in all jobs defined in this workflow -env: - CI_BRANCH: ${{ github.head_ref || github.ref_name }} - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - -jobs: - # Job 1: Test installation from PyPI on multiple OS - pypi-package-tests: - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - - runs-on: ${{ matrix.os }} - - steps: - - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - run-install: false - cache: false - post-cleanup: false - - - name: - Download the pixi configuration file from the ${{ env.CI_BRANCH}} - branch - shell: bash - run: | - curl -LO https://raw.githubusercontent.com/easyscience/diffraction-lib/${CI_BRANCH}/pixi.toml - - - name: Download the tests from the ${{ env.DEFAULT_BRANCH }} branch - shell: bash - run: | - curl -LO https://github.com/easyscience/diffraction-lib/archive/refs/heads/${DEFAULT_BRANCH}.zip - unzip ${DEFAULT_BRANCH}.zip -d . - mkdir -p tests - cp -r diffraction-lib-${DEFAULT_BRANCH}/tests/* tests/ - cp diffraction-lib-${DEFAULT_BRANCH}/pytest.ini . - rm -rf ${DEFAULT_BRANCH}.zip diffraction-lib-${DEFAULT_BRANCH} - - - name: Create the environment and install dependencies - run: pixi install - - - name: Run unit tests to verify the installation - run: pixi run unit-tests - - - name: Run integration tests to verify the installation - run: pixi run integration-tests - - # Github token to avoid hitting the unauthenticated API rate limit - - name: List and fetch the EasyDiffraction tutorials - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - pixi run easydiffraction --version - pixi run easydiffraction list-tutorials - pixi run easydiffraction download-all-tutorials - - - name: Test tutorials as notebooks - run: pixi run notebook-tests - - # Job 2: Trigger dashboard build - dashboard-build-trigger: - needs: pypi-package-tests - - runs-on: ubuntu-latest - - steps: - - name: Check-out repository - uses: actions/checkout@v5 - - - name: Trigger dashboard build - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: "dashboard.yaml", - ref: "${{ env.CI_BRANCH }}" - }); diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml new file mode 100644 index 00000000..6493e64f --- /dev/null +++ b/.github/workflows/pypi-test.yml @@ -0,0 +1,80 @@ +name: PyPI package tests + +on: + # Run daily, at 00:00. + schedule: + - cron: '0 0 * * *' + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Allow only one concurrent workflow, skipping runs queued between the run +# in-progress and latest queued. And cancel in-progress runs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + +jobs: + # Job 1: Test installation from PyPI on multiple OS + pypi-package-tests: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + with: + environments: '' + activate-environment: '' + run-install: false + frozen: false + + - name: Init pixi project + run: pixi init easydiffraction + + - name: Add Python 3.13 from Conda + working-directory: easydiffraction + run: pixi add "python=3.13" + + - name: Add other Conda dependencies + working-directory: easydiffraction + run: pixi add gsl + + - name: Add easydiffraction from PyPI + working-directory: easydiffraction + run: pixi add --pypi "easydiffraction" + + - name: Add dev dependencies from PyPI + working-directory: easydiffraction + run: pixi add --pypi pytest pytest-xdist + + - name: Add Pixi task as a shortcut + working-directory: easydiffraction + run: pixi task add easydiffraction "python -m easydiffraction" + + - name: Run unit tests to verify the installation + working-directory: easydiffraction + run: pixi run python -m pytest ../tests/unit/ --color=yes -v + + - name: Run integration tests to verify the installation + working-directory: easydiffraction + run: pixi run python -m pytest ../tests/integration/ --color=yes -n auto + + # Job 2: Build and publish dashboard (reusable workflow) + run-reusable-workflows: + needs: pypi-package-tests # depend on previous job + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml deleted file mode 100644 index a3c1b72a..00000000 --- a/.github/workflows/quality.yaml +++ /dev/null @@ -1,123 +0,0 @@ -# The workflow is divided into several steps to ensure code quality: -# - Check the validity of pyproject.toml -# - Check code linting -# - Check code formatting -# - Check formatting of docstrings in the code -# - Check formatting of Markdown, YAML, TOML, etc. files - -name: Code quality checks - -on: - # Trigger the workflow on push - push: - # Every branch - branches: ['**'] - # Do not run this workflow on creating a new tag starting with - # 'v', e.g. 'v1.0.3' (see publish-pypi.yml) - tags-ignore: ['v*'] - # Trigger the workflow on pull request - pull_request: - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Allow only one concurrent workflow, skipping runs queued between the run -# in-progress and latest queued. And cancel in-progress runs. -concurrency: - group: - ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -# Set the environment variables to be used in all jobs defined in this workflow -env: - CI_BRANCH: ${{ github.head_ref || github.ref_name }} - -jobs: - code-quality: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: pixi run dev - - # Check the validity of pyproject.toml - - name: Check validity of pyproject.toml - id: check_pyproject - continue-on-error: true - shell: bash - run: pixi run pyproject-check - - # Check code linting with Ruff in the project root - - name: Check code linting - id: check_code_linting - continue-on-error: true - shell: bash - run: pixi run py-lint-check - - # Check code formatting with Ruff in the project root - - name: Check code formatting - id: check_code_formatting - continue-on-error: true - shell: bash - run: pixi run py-format-check - - # Check formatting of docstrings in the code with docformatter - - name: Check formatting of docstrings in the code - id: check_docs_formatting - continue-on-error: true - shell: bash - run: pixi run docs-format-check - - # Check formatting of MD, YAML, TOML, etc. files with Prettier in - # the project root - - name: Check formatting of MD, YAML, TOML, etc. files - id: check_others_formatting - continue-on-error: true - shell: bash - run: pixi run nonpy-format-check - - # Check formatting of Jupyter Notebooks in the tutorials folder - - name: Convert tutorial scripts to notebooks and check formatting - id: check_notebooks_formatting - continue-on-error: true - shell: bash - run: | - pixi run notebook-prepare - pixi run notebook-format-check - - # Add summary - - name: Add quality checks summary - if: always() - shell: bash - run: | - { - echo "## 🧪 Code Quality Checks Summary" - echo "" - echo "| Check | Status |" - echo "|-------|--------|" - echo "| pyproject.toml | ${{ steps.check_pyproject.outcome == 'success' && '✅' || '❌' }} |" - echo "| py lint | ${{ steps.check_code_linting.outcome == 'success' && '✅' || '❌' }} |" - echo "| py format | ${{ steps.check_code_formatting.outcome == 'success' && '✅' || '❌' }} |" - echo "| docstring format | ${{ steps.check_docs_formatting.outcome == 'success' && '✅' || '❌' }} |" - echo "| nonpy format | ${{ steps.check_others_formatting.outcome == 'success' && '✅' || '❌' }} |" - echo "| notebooks format | ${{ steps.check_notebooks_formatting.outcome == 'success' && '✅' || '❌' }} |" - } >> "$GITHUB_STEP_SUMMARY" - - # Fail job requirement - - name: Fail job if any check failed - if: failure() - shell: bash - run: exit 1 diff --git a/.github/workflows/release-notes.yaml b/.github/workflows/release-notes.yml similarity index 81% rename from .github/workflows/release-notes.yaml rename to .github/workflows/release-notes.yml index 73006ff2..cb3e28d6 100644 --- a/.github/workflows/release-notes.yaml +++ b/.github/workflows/release-notes.yml @@ -25,18 +25,14 @@ jobs: fetch-depth: 0 # full history with tags to get the version number - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false + uses: ./.github/actions/setup-pixi - - name: Install and setup development dependencies - shell: bash - run: pixi run dev + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} - name: Drafts the next release notes id: draft @@ -61,9 +57,8 @@ jobs: labels: ['[scope] bug'] - title: 'Changed' labels: ['[scope] maintenance', '[scope] documentation'] - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.bot.outputs.token }} - name: Create GitHub draft release uses: softprops/action-gh-release@v2 @@ -73,4 +68,4 @@ jobs: name: ${{ steps.draft.outputs.release_name }} body: ${{ steps.draft.outputs.release_notes }} env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.bot.outputs.token }} diff --git a/.github/workflows/release-pr.yaml b/.github/workflows/release-pr.yaml deleted file mode 100644 index eda67884..00000000 --- a/.github/workflows/release-pr.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# This workflow creates an automated release PR from `develop` into `master`. -# -# Usage: -# - Triggered manually via workflow_dispatch. -# - Creates a PR titled "Release: merge develop into master". -# - Adds the label "[maintainer] auto-pull-request" so it is excluded from changelogs. -# - The PR body makes clear that this is automation only (no review needed). -# -# Required repo config: -# https://github.com/organizations/easyscience/settings/secrets/actions -# https://github.com/organizations/easyscience/settings/variables/actions -# - Actions secret: EASYSCIENCE_APP_KEY (GitHub App private key PEM) -# - Actions variable: EASYSCIENCE_APP_ID (GitHub App ID) - -name: Release PR (develop -> master) - -on: - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -permissions: - contents: read - pull-requests: write - -# Set the environment variables to be used in all jobs defined in this workflow -env: - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - -jobs: - create-pull-request: - runs-on: ubuntu-latest - steps: - - name: Create GitHub App installation token - id: app-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.EASYSCIENCE_APP_ID }} - private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} - - - name: Checkout develop branch - uses: actions/checkout@v5 - with: - ref: develop - token: ${{ steps.app-token.outputs.token }} - - - name: Create PR from develop to ${{ env.DEFAULT_BRANCH }} - run: | - gh pr create \ - --base ${{ env.DEFAULT_BRANCH }} \ - --head develop \ - --title "Release: merge develop into ${{ env.DEFAULT_BRANCH }}" \ - --label "[maintainer] auto-pull-request" \ - --body "This PR is created automatically to trigger the release pipeline. It merges the accumulated changes from \`develop\` into \`${{ env.DEFAULT_BRANCH }}\`. - - It is labeled \`[maintainer] auto-pull-request\` and is excluded from release notes and version bump logic." - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml new file mode 100644 index 00000000..7e6fda49 --- /dev/null +++ b/.github/workflows/release-pr.yml @@ -0,0 +1,55 @@ +# This workflow creates an automated release PR from a source branch into the default branch. +# +# Usage: +# - Triggered manually via workflow_dispatch. +# - Creates a PR titled "Release: merge into ". +# - Adds the label "[bot] release" so it is excluded from changelogs. +# - The PR body makes clear that this is automation only (no review needed). + +name: 'Release PR (develop → master)' + +on: + workflow_dispatch: + inputs: + source_branch: + description: 'Source branch to create PR from' + required: false + default: 'develop' + type: string + +permissions: + contents: read + pull-requests: write + +env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + SOURCE_BRANCH: ${{ inputs.source_branch || 'develop' }} + +jobs: + create-pull-request: + runs-on: ubuntu-latest + steps: + - name: Checkout ${{ env.SOURCE_BRANCH }} branch + uses: actions/checkout@v5 + with: + ref: ${{ env.SOURCE_BRANCH }} + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + - name: Create PR from ${{ env.SOURCE_BRANCH }} to ${{ env.DEFAULT_BRANCH }} + env: + GH_TOKEN: ${{ steps.bot.outputs.token }} + run: | + gh pr create \ + --base ${{ env.DEFAULT_BRANCH }} \ + --head ${{ env.SOURCE_BRANCH }} \ + --title "Release: merge ${{ env.SOURCE_BRANCH }} into ${{ env.DEFAULT_BRANCH }}" \ + --label "[bot] release" \ + --body "This PR is created automatically to trigger the release pipeline. It merges the accumulated changes from \`${{ env.SOURCE_BRANCH }}\` into \`${{ env.DEFAULT_BRANCH }}\`. + + ⚠️ It is labeled \`[bot] release\` and is excluded from release notes and version bump logic." diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml deleted file mode 100644 index b837b879..00000000 --- a/.github/workflows/security.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# Integrates a collection of open source static analysis tools with -# GitHub code scanning. -# https://github.com/github/ossar-action - -name: Security scans - -on: - # Trigger the workflow on pull request - pull_request: - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - scan-security-ossar: - # OSSAR runs on windows-latest. - # ubuntu-latest and macos-latest support coming soon - runs-on: windows-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - - name: Run open source static analysis tools - uses: github/ossar-action@main - id: ossar - - - name: Upload results to Security tab - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ${{ steps.ossar.outputs.sarifFile }} diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000..9b34cccf --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,93 @@ +# Code scanning (CodeQL) for vulnerabilities and insecure coding patterns. +# +# What this workflow does +# - Runs GitHub CodeQL analysis and uploads results to your repository's Security tab. +# - Triggers on PRs (so findings appear as PR checks) and on pushes to `develop`. +# - Runs on a weekly schedule. +# +# Where to find results on GitHub +# - Repository → Security → Code scanning alerts +# (You can filter by tool = CodeQL and by branch.) +# +# Where to configure on GitHub +# - Repository → Settings → Advanced Security +# Enable "GitHub Advanced Security" (if available) and configure CodeQL there. +# - Repository → Security → Code scanning alerts +# This page shows findings produced by this workflow. +# +# Notes about the scheduled run +# - Scheduled workflows are triggered from the repository's *default branch*. +# If your default branch is `master` but you want the scheduled scan to analyze +# `develop`, this workflow checks out `develop` explicitly for scheduled runs. +# +# References +# - CodeQL Action: https://github.com/github/codeql-action +# - Advanced setup docs: https://docs.github.com/en/code-security/code-scanning + +name: Security scans with CodeQL + +on: + # Run on pull requests so results show up as PR checks and code + # scanning alerts. + pull_request: + branches: [master, main, develop] + + # Run on pushes (e.g., after merging PRs). + push: + branches: [master, main, develop] + + # Run weekly. (Cron is in UTC.) + schedule: + - cron: '0 3 * * 1' + +permissions: + contents: read + security-events: write + +jobs: + codeql: + name: Code scanning + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Keep this list tight to avoid noise and speed up runs. + language: [python, actions] + + steps: + # Scheduled workflows run from the default branch. + # We explicitly analyze `develop` on the schedule to keep the scan + # focused on the active dev branch. + - name: Checkout repository (scheduled → develop) + if: ${{ github.event_name == 'schedule' }} + uses: actions/checkout@v5 + with: + ref: develop + + - name: Checkout repository + if: ${{ github.event_name != 'schedule' }} + uses: actions/checkout@v5 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + + print-link: + name: Print results link + runs-on: ubuntu-latest + + needs: codeql + permissions: {} # no special perms needed just to print links + + steps: + - name: Add Code Scanning link to job summary + run: | + echo "## 🔎 CodeQL Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "View Code Scanning alerts here:" >> $GITHUB_STEP_SUMMARY + echo "${{ github.server_url }}/${{ github.repository }}/security/code-scanning" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test-trigger.yaml b/.github/workflows/test-trigger.yml similarity index 61% rename from .github/workflows/test-trigger.yaml rename to .github/workflows/test-trigger.yml index dedfbd91..ecf6b40c 100644 --- a/.github/workflows/test-trigger.yaml +++ b/.github/workflows/test-trigger.yml @@ -7,6 +7,9 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +permissions: + contents: read + jobs: code-tests-trigger: runs-on: ubuntu-latest @@ -17,14 +20,21 @@ jobs: with: ref: develop + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + - name: Dispatch code tests workflow - uses: actions/github-script@v7 + uses: ./.github/actions/github-script with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ steps.bot.outputs.token }} script: | await github.rest.actions.createWorkflowDispatch({ owner: context.repo.owner, repo: context.repo.repo, - workflow_id: "test.yaml", + workflow_id: "test.yml", ref: "develop" }); diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index 11b06f14..00000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,259 +0,0 @@ -# This is the main workflow for testing the code before and after -# packaging it. -# The workflow is divided into three jobs: -# 1. env-prepare: -# - Prepare the environment for testing -# 2. source-test: -# - Test the code base against the latest code in the repository -# - Create the Python package -# - Upload the Python package for the next job -# 3. package-test: -# - Download the Python package (including extra files) from the previous job -# - Install the downloaded Python package -# - Test the code base against the installed package -# 4. dashboard-build-trigger: -# - Trigger the dashboard build workflow to update the code quality -# metrics on the dashboard - -name: Code and package tests - -on: - # Trigger the workflow on push - push: - # Every branch - branches: ['**'] - # But do not run this workflow on creating a new tag starting with - # 'v', e.g. 'v1.0.3' (see publish-pypi.yml) - tags-ignore: ['v*'] - # Trigger the workflow on pull request - pull_request: - branches: ['**'] - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Need permissions to trigger the dashboard build workflow -permissions: - actions: write - contents: read - -# Allow only one concurrent workflow, skipping runs queued between the run -# in-progress and latest queued. And cancel in-progress runs. -concurrency: - group: - ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -# Set the environment variables to be used in all jobs defined in this workflow -env: - CI_BRANCH: ${{ github.head_ref || github.ref_name }} - -jobs: - # Job 1: Prepare environment - env-prepare: - runs-on: [ubuntu-latest] - - outputs: - pytest-marks: ${{ steps.set-mark.outputs.pytest_marks }} - - steps: - # Determine if integration tests should be run fully or only the fast ones - # (to save time on branches other than master and develop) - - name: Set mark for integration tests - id: set-mark - run: | - if [[ "${{ env.CI_BRANCH }}" == "master" || "${{ env.CI_BRANCH }}" == "develop" ]]; then - echo "pytest_marks=" >> $GITHUB_OUTPUT - else - echo "pytest_marks=-m fast" >> $GITHUB_OUTPUT - fi - - # Job 2: Test code - source-test: - needs: env-prepare # depend on previous job - - strategy: - fail-fast: false - matrix: - os: [ubuntu-24.04, macos-14, windows-2022] - - runs-on: ${{ matrix.os }} - - env: - PIXI_ENVS: 'py311-dev py313-dev' - - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - fetch-depth: '0' # full history with tags to get the version number by versioningit - - - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: ${{ env.PIXI_ENVS }} - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - pixi run --environment $env dev - echo "PYTHONPATH:" - pixi run printenv PYTHONPATH || true - pixi run --environment $env easydiffraction --version - done - - - name: Run unit tests - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - pixi run --environment $env unit-tests - done - - - name: - Run integration tests ${{ needs.env-prepare.outputs.pytest-marks }} - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - pixi run --environment $env integration-tests ${{ needs.env-prepare.outputs.pytest-marks }} - done - - # Delete all local tags when not on a tagged commit to force versioningit - # to fall back to the configured default-tag, which is '999.0.0' in our case. - # This is needed for testing the package in the next job, as its version - # must be higher than the PyPI version for pip to prefer the local version. - - name: Force using versioningit default tag (non tagged release) - if: startsWith(github.ref , 'refs/tags/v') != true - run: git tag --delete $(git tag) - - - name: Create Python package - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - pixi run -e $env dist-build - env_prefix="${env%%-*}" - echo "📦 Moving built wheel to dist/$env_prefix/" - pixi run mkdir -p dist/$env_prefix - pixi run mv dist/*.whl dist/$env_prefix/ - done - - - name: Remove local easydiffraction from pixi.toml - shell: bash - run: pixi remove --pypi easydiffraction - - - name: Remove Python cache files before uploading - shell: bash - run: pixi run clean-pycache - - # More than one file/dir need to be specified in 'path', to preserve the - # structure of the dist/ directory, not only its contents. - - name: Upload Python package for the next job - uses: actions/upload-artifact@v4 - with: - name: edl_${{ matrix.os }}_${{ runner.arch }} - path: | - dist/ - tests/ - pytest.ini - pixi.toml - pixi.lock - if-no-files-found: 'error' - compression-level: 0 - - # Job 3: Test the package - package-test: - needs: [env-prepare, source-test] # depend on previous jobs - - strategy: - fail-fast: false - matrix: - os: [ubuntu-24.04, macos-14, windows-2022] - - runs-on: ${{ matrix.os }} - - env: - PIXI_ENVS: 'py311-dev py313-dev' - - steps: - - name: - Download zipped Python package (incl. extra files) from previous job - uses: actions/download-artifact@v4 - with: # name or path are taken from the upload step of the previous job - name: edl_${{ matrix.os }}_${{ runner.arch }} - path: . # directory to extract downloaded zipped artifacts - - - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: ${{ env.PIXI_ENVS }} - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - pixi run --environment $env wheel - done - - - name: Install easydiffraction package from the built wheel - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - env_prefix="${env%%-*}" - echo "📦 Looking for wheel in dist/$env_prefix/" - whl_path="$(find dist/${env_prefix} -name '*.whl' | head -1)" - echo "📦 Installing easydiffraction from: $whl_path" - pixi run --environment $env python -m uv pip install "${whl_path}[all]" --reinstall-package easydiffraction - pixi run --environment $env easydiffraction --version - done - - - name: Run unit tests - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - pixi run --environment $env unit-tests - done - - - name: - Run integration tests ${{ needs.env-prepare.outputs.pytest-marks }} - shell: bash - run: | - for env in ${{ env.PIXI_ENVS }}; do - echo "🔹🔸🔹🔸🔹 Current env: $env 🔹🔸🔹🔸🔹" - pixi run --environment $env integration-tests ${{ needs.env-prepare.outputs.pytest-marks }} - done - - # Job 4: Trigger dashboard build - dashboard-build-trigger: - needs: [source-test, package-test] # depend on previous jobs - - runs-on: ubuntu-latest - - steps: - - name: Check-out repository - uses: actions/checkout@v5 - - - name: Trigger dashboard build - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: "dashboard.yaml", - ref: "${{ env.CI_BRANCH }}" - }); diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..4ecae0b4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,268 @@ +# This is the main workflow for testing the code before and after +# packaging it. +# The workflow is divided into three jobs: +# 1. env-prepare: +# - Prepare the environment for testing +# 2. source-test: +# - Test the code base against the latest code in the repository +# - Create the Python package +# - Upload the Python package for the next job +# 3. package-test: +# - Download the Python package (including extra files) from the previous job +# - Install the downloaded Python package +# - Test the code base against the installed package +# 4. dashboard-build-trigger: +# - Trigger the dashboard build workflow to update the code quality +# metrics on the dashboard + +name: Code and package tests + +on: + # Trigger the workflow on push + push: + branches-ignore: [master, main] # Already verified in PR + # But do not run this workflow on creating a new tag starting with + # 'v', e.g. 'v1.0.3' (see publish-pypi.yml) + tags-ignore: ['v*'] + # Trigger the workflow on pull request + pull_request: + branches: ['**'] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Need permissions to trigger the dashboard build workflow +permissions: + actions: write + contents: read + +# Allow only one concurrent workflow, skipping runs queued between the run +# in-progress and latest queued. And cancel in-progress runs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + PY_VERSIONS: '3.11 3.13' + PIXI_ENVS: 'py-311-env py-313-env' + +jobs: + # Job 1: Set up environment variables + env-prepare: + runs-on: [ubuntu-latest] + + outputs: + pytest-marks: ${{ steps.set-mark.outputs.pytest_marks }} + + steps: + # Determine if integration tests should be run fully or only the fast ones + # (to save time on branches other than master and develop) + - name: Set mark for integration tests + id: set-mark + run: | + if [[ "${{ env.CI_BRANCH }}" == "master" || "${{ env.CI_BRANCH }}" == "develop" ]]; then + echo "pytest_marks=" >> $GITHUB_OUTPUT + else + echo "pytest_marks=-m fast" >> $GITHUB_OUTPUT + fi + + # Job 2: Test code + source-test: + needs: env-prepare # depend on previous job + + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-15, windows-2022] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + with: + environments: ${{ env.PIXI_ENVS }} + + - name: Run unit tests + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + env="py-$(echo $py_ver | tr -d .)-env" # Converts 3.11 -> py-311-env + + echo "Running tests in environment: $env" + pixi run --environment $env unit-tests + done + + - name: Run integration tests ${{ needs.env-prepare.outputs.pytest-marks }} + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + env="py-$(echo $py_ver | tr -d .)-env" # Converts 3.11 -> py-311-env + + echo "Running tests in environment: $env" + pixi run --environment $env integration-tests ${{ needs.env-prepare.outputs.pytest-marks }} + done + + # Delete all local tags when not on a tagged commit to force versioningit + # to fall back to the configured default-tag, which is '999.0.0' in our case. + # This is needed for testing the package in the next job, as its version + # must be higher than the PyPI version for pip to prefer the local version. + - name: Force using versioningit default tag (non tagged release) + if: startsWith(github.ref , 'refs/tags/v') != true + run: git tag --delete $(git tag) + + - name: Build package wheels for all Python versions + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + env="py-$(echo $py_ver | tr -d .)-env" # Converts 3.11 -> py-311-env + + echo "Building wheel in environment: $env" + pixi run --environment $env dist-build + + echo "Moving built wheel to dist/py$py_ver/" + pixi run mkdir -p dist/py$py_ver + pixi run mv dist/*.whl dist/py$py_ver/ + done + + - name: Remove Python cache files before uploading + shell: bash + run: pixi run clean-pycache + + # More than one file/dir need to be specified in 'path', to preserve the + # structure of the dist/ directory, not only its contents. + - name: Upload package (incl. extras) for next job + uses: ./.github/actions/upload-artifact + with: + name: easydiffraction_${{ matrix.os }}_${{ runner.arch }} + path: dist/ + + # Job 3: Test the package + package-test: + needs: source-test # depend on previous job + + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-15, windows-2022] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Download package (incl. extras) from previous job + uses: ./.github/actions/download-artifact + with: + # name and path should be taken from the upload step of the previous job + name: easydiffraction_${{ matrix.os }}_${{ runner.arch }} + path: dist/ + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + with: + environments: '' + activate-environment: '' + run-install: false + frozen: false + + - name: Install easydiffraction from the built wheel + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + echo "Initializing pixi project" + pixi init easydiffraction_py$py_ver + cd easydiffraction_py$py_ver + + echo "Adding Python $py_ver" + pixi add "python=$py_ver" + + echo "Setting macOS 14.0 as minimum required" + pixi project system-requirements add macos 14.0 + + echo "Looking for wheel in ../dist/py$py_ver/" + ls -l "../dist/py$py_ver/" + + whl_path=(../dist/"py$py_ver"/*.whl) + if [[ ! -f "${whl_path[0]}" ]]; then + echo "❌ No wheel found in ../dist/py$py_ver/" + exit 1 + fi + + whl_url="file://$(python -c 'import os,sys; print(os.path.abspath(sys.argv[1]))' "${whl_path[0]}")" + + echo "Adding easydiffraction from: $whl_url" + pixi add --pypi "easydiffraction[dev] @ ${whl_url}" + + echo "Exiting pixi project directory" + cd .. + done + + - name: Run unit tests + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + echo "Entering pixi project directory easydiffraction_py$py_ver" + cd easydiffraction_py$py_ver + + echo "Running tests" + pixi run python -m pytest ../tests/unit/ --color=yes -v + + echo "Exiting pixi project directory" + cd .. + done + + - name: Run integration tests ${{ needs.env-prepare.outputs.pytest-marks }} + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + echo "Entering pixi project directory easydiffraction_py$py_ver" + cd easydiffraction_py$py_ver + + echo "Running tests" + pixi run python -m pytest ../tests/integration/ --color=yes -n auto -v ${{ needs.env-prepare.outputs.pytest-marks }} + + echo "Exiting pixi project directory" + cd .. + done + + # Job 4: Build and publish dashboard (reusable workflow) + run-reusable-workflows: + needs: package-test # depend on previous job + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.github/workflows/tutorial-tests-trigger.yaml b/.github/workflows/tutorial-tests-trigger.yml similarity index 61% rename from .github/workflows/tutorial-tests-trigger.yaml rename to .github/workflows/tutorial-tests-trigger.yml index ea32eef2..1bc27f4f 100644 --- a/.github/workflows/tutorial-tests-trigger.yaml +++ b/.github/workflows/tutorial-tests-trigger.yml @@ -7,6 +7,9 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +permissions: + contents: read + jobs: tutorial-tests-trigger: runs-on: ubuntu-latest @@ -17,14 +20,21 @@ jobs: with: ref: develop + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + - name: Dispatch tutorial tests workflow - uses: actions/github-script@v7 + uses: ./.github/actions/github-script with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ steps.bot.outputs.token }} script: | await github.rest.actions.createWorkflowDispatch({ owner: context.repo.owner, repo: context.repo.repo, - workflow_id: "tutorial-tests.yaml", + workflow_id: "tutorial-tests.yml", ref: "develop" }); diff --git a/.github/workflows/tutorial-tests.yaml b/.github/workflows/tutorial-tests.yml similarity index 55% rename from .github/workflows/tutorial-tests.yaml rename to .github/workflows/tutorial-tests.yml index 301b43d3..4c9244d0 100644 --- a/.github/workflows/tutorial-tests.yaml +++ b/.github/workflows/tutorial-tests.yml @@ -4,7 +4,7 @@ on: # Trigger the workflow on push push: # Selected branches - branches: [master, main, develop] + branches: [develop] # master and main are already verified in PR # Trigger the workflow on pull request pull_request: branches: ['**'] @@ -15,11 +15,13 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +permissions: + contents: read + # Allow only one concurrent workflow, skipping runs queued between the run # in-progress and latest queued. And cancel in-progress runs. concurrency: - group: - ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true # Set the environment variables to be used in all jobs defined in this workflow @@ -41,24 +43,13 @@ jobs: uses: actions/checkout@v5 - name: Set up pixi - uses: prefix-dev/setup-pixi@v0.9.3 - with: - environments: default - activate-environment: default - run-install: true - frozen: true - cache: false - post-cleanup: false - - - name: Install and setup development dependencies - shell: bash - run: pixi run dev + uses: ./.github/actions/setup-pixi - name: Test tutorials as python scripts shell: bash run: pixi run script-tests - - name: Convert tutorial scripts to notebooks + - name: Prepare notebooks shell: bash run: pixi run notebook-prepare @@ -66,24 +57,8 @@ jobs: shell: bash run: pixi run notebook-tests - # Job 2: Trigger dashboard build - dashboard-build-trigger: - needs: tutorial-tests - - runs-on: ubuntu-latest - - steps: - - name: Check-out repository - uses: actions/checkout@v5 - - - name: Trigger dashboard build - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - await github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: "dashboard.yaml", - ref: "${{ env.CI_BRANCH }}" - }); + # Job 2: Build and publish dashboard (reusable workflow) + run-reusable-workflows: + needs: tutorial-tests # depend on previous job + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.gitignore b/.gitignore index 2c43d2b8..0500ede3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,19 @@ # Python -__pycache__ -.venv +__pycache__/ +.venv/ .coverage .pyc -# PyTest -.pytest_cache - -# MyPy -.mypy_cache - # Pixi -.pixi +.pixi/ -# Ruff -.ruff_cache +# PyInstaller +dist/ +build/ +*.spec + +# MkDocs +docs/site/ # Jupyter Notebooks .ipynb_checkpoints @@ -24,16 +23,10 @@ node_modules/ # QtCreator *.autosave - -# QtCreator Qml *.qmlproject.user *.qmlproject.user.* - -# QtCreator Python *.pyproject.user *.pyproject.user.* - -# QtCreator CMake CMakeLists.txt.user* # PyCharm @@ -46,3 +39,7 @@ CMakeLists.txt.user* .DS_Store *.app *.dmg + +# Misc +*.log +*.zip diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3d471cd..5fa0c2cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,57 +1,68 @@ repos: - repo: local hooks: - # ----------------- - # Pre-commit checks - # ----------------- + # ------------- + # Manual checks + # ------------- - id: pixi-pyproject-check name: pixi run pyproject-check entry: pixi run pyproject-check language: system pass_filenames: false - stages: [pre-commit] + stages: [manual] - - id: pixi-py-lint-check-staged - name: pixi run py-lint-check-staged - entry: pixi run py-lint-check-pre + - id: license-headers-check + name: pixi run license-check + entry: pixi run license-check language: system pass_filenames: false - stages: [pre-commit] + stages: [manual] - - id: pixi-py-format-check-staged - name: pixi run py-format-check-staged - entry: pixi run py-format-check-pre + - id: pixi-py-lint-check + name: pixi run py-lint-check + entry: pixi run py-lint-check language: system pass_filenames: false - stages: [pre-commit] + stages: [manual] - - id: pixi-nonpy-format-check-modified - name: pixi run nonpy-format-check-modified - entry: pixi run nonpy-format-check-modified + - id: pixi-py-format-check + name: pixi run py-format-check + entry: pixi run py-format-check language: system pass_filenames: false - stages: [pre-commit] + stages: [manual] - - id: pixi-docs-format-check - name: pixi run docs-format-check - entry: pixi run docs-format-check + - id: pixi-docstring-lint-check + name: pixi run docstring-lint-check + entry: pixi run docstring-lint-check language: system pass_filenames: false - stages: [pre-commit] + stages: [manual] + + - id: pixi-docstring-format-check + name: pixi run docstring-format-check + entry: pixi run docstring-format-check + language: system + pass_filenames: false + stages: [manual] - # ---------------- - # Pre-push checks - # ---------------- - id: pixi-nonpy-format-check name: pixi run nonpy-format-check entry: pixi run nonpy-format-check language: system pass_filenames: false - stages: [pre-push] + stages: [manual] + + - id: pixi-notebook-lint-check + name: pixi run notebook-lint-check + entry: pixi run notebook-lint-check + language: system + pass_filenames: false + stages: [manual] - id: pixi-unit-tests name: pixi run unit-tests entry: pixi run unit-tests language: system pass_filenames: false - stages: [pre-push] + stages: [manual] diff --git a/.prettierignore b/.prettierignore index 4bd61611..788ec8fa 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,17 @@ +# Copier +.copier-answers*.yml + # Pixi .pixi pixi.lock +# MkDocs +docs/overrides/ +docs/site/ +docs/docs/assets/ + +# Python +.pytest_cache/ + # MyPy .mypy_cache diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f05c041f..fb06f9de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,94 +1,446 @@ -# Contributing +# Contributing to EasyDiffraction -When contributing, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a -change. +Thank you for your interest in contributing to **EasyDiffraction**! -Please note we have a code of conduct, please follow it in all your interactions -with the project. +This guide explains how you can: -## Pull Request Process +- Report issues +- Contribute code +- Improve documentation +- Suggest enhancements +- Interact with the EasyScience community -1. Ensure any install or build dependencies are removed before the end of the - layer when doing a build. -2. Update the README.md with details of changes to the interface, this includes - new environment variables, exposed ports, useful file locations and container - parameters. -3. Increase the version numbers in any example files and the README.md to the - new version that this Pull Request would represent. The versioning scheme we - use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other - developers, or if you do not have permission to do that, you may request the - second reviewer to merge it for you. +Whether you are an experienced developer or contributing for the first +time, this document walks you through the entire process step by step. -## Code of Conduct +Please make sure you follow the EasyScience organization-wide +[Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md). -### Our Pledge +--- -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to make participation in our project and our -community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of -experience, nationality, personal appearance, race, religion, or sexual identity -and orientation. +## Table of Contents -### Our Standards +- [How to Interact With This Project](#how-to-interact-with-this-project) +- [1. Understanding the Development Model](#1-understanding-the-development-model) +- [2. Getting the Code](#2-getting-the-code) +- [3. Setting Up the Development Environment](#3-setting-up-the-development-environment) +- [4. Creating a Branch](#4-creating-a-branch) +- [5. Implementing Your Changes](#5-implementing-your-changes) +- [6. Code Quality Checks](#6-code-quality-checks) +- [7. Opening a Pull Request](#7-opening-a-pull-request) +- [8. Continuous Integration (CI)](#8-continuous-integration-ci) +- [9. Code Review](#9-code-review) +- [10. Documentation Contributions](#10-documentation-contributions) +- [11. Reporting Issues](#11-reporting-issues) +- [12. Security Issues](#12-security-issues) +- [13. Releases](#13-releases) -Examples of behavior that contributes to creating a positive environment -include: +--- -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community +## How to Interact With This Project -Examples of unacceptable behavior by participants include: +If you are not planning to contribute code, you may want to: -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting +- 🐞 Report a bug — see [Reporting Issues](#11-reporting-issues) +- 🛡 Report a security issue — + see [Security Issues](#12-security-issues) +- 💬 Ask a question or start a discussion at + [Project Discussions](https://github.com/easyscience/diffraction-lib/discussions) -### Our Responsibilities +If you plan to contribute code or documentation, continue below. -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +--- -Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, or to ban temporarily or permanently any -contributor for other behaviors that they deem inappropriate, threatening, -offensive, or harmful. +## 1. Understanding the Development Model -### Scope +Before you start coding, it is important to understand how development +works in this project. -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +### Branching Strategy -### Enforcement +We use the following branches: -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at suport@easydiffraction.org. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an -incident. Further details of specific enforcement policies may be posted -separately. +- `master` — stable releases only +- `develop` — active development branch +- Short-lived branches — feature or fix branches created for a single + contribution and deleted after merge -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +> [!IMPORTANT] +> +> All normal contributions must target the `develop` branch. +> +> - Do **not** open Pull Requests against `master` +> - Always create your branch from `develop` +> - Always target `develop` when opening a Pull Request -### Attribution +See ADR easyscience/.github#12 for more details on the branching +strategy. -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 1.4, available at [http://contributor-covenant.org/version/1/4][version] +--- -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +## 2. Getting the Code + +### 2.1. If You Are an External Contributor + +If you are not a core maintainer of this repository, follow these steps. + +1. Open the repository page: `https://github.com/easyscience/diffraction-lib` + +2. Click the **Fork** button (top-right corner). This creates your own + copy of the repository. + +3. Clone your fork locally: + + ```bash + git clone https://github.com//diffraction-lib.git + cd diffraction-lib + ``` + +4. Add the original repository as `upstream`: + + ```bash + git remote add upstream https://github.com/easyscience/diffraction-lib.git + ``` + +5. Switch to the `develop` branch and update it: + + ```bash + git fetch upstream + git checkout develop + git pull upstream develop + ``` + +If you have contributed before, make sure your local `develop` branch is +up to date before starting new work. You can update it with: + +```bash +git fetch upstream +git pull upstream develop +``` + +This ensures you are working on the latest version of the project. + +### 2.2. If You Are a Core Team Member + +Core team members can create branches directly in this repository and +therefore do not need to fork it, but the rest of the workflow remains +the same. + +--- + +## 3. Setting Up the Development Environment + +You need: + +- Git +- Pixi + +EasyScience projects use **Pixi** to manage the development environment. + +To install Pixi, follow the official instructions: +https://pixi.prefix.dev/latest/installation/ + +You do **not** need to manually install Python. Pixi automatically: + +- Creates the correct Python environment +- Installs all required dependencies +- Installs development tools (linters, formatters, test tools) + +Set up the environment: + +```bash +pixi install +pixi run post-install # Install additional tooling +``` + +After this step, your development environment is ready. + +See ADR easyscience/.github#63 for more details about using Pixi for +development. + +--- + +## 4. Creating a Branch + +Never work directly on `develop`. + +Create a new branch: + +```bash +git checkout -b my-change develop +``` + +> [!IMPORTANT] +> +> Use a clear and descriptive name, for example: +> +> - `improve-solver-speed` +> - `fix-boundary-condition` +> - `add-tutorial-example` + +Clear branch names make reviews and history easier to understand. + +--- + +## 5. Implementing Your Changes + +While developing, make small, logical commits with clear messages. + +Example: + +```bash +git add . +git commit -m "Improve performance of time integrator for large systems" +``` + +--- + +## 6. Code Quality Checks + +> [!IMPORTANT] +> +> When adding new functionality or making changes, make sure to add or +> update the following as needed: +> +> - 📘 docstrings +> - 🧪 unit tests + +Before opening a Pull Request, always run: + +```bash +pixi run check +``` + +This command: + +- Validates the pyproject.toml file +- Checks for licence headers in code files +- Identifies linting and formatting issues in Python code +- Checks docstring linting and formatting issues in Python code +- Detects formatting issues in non-Python files (MD, YAML, TOML etc.) +- Checks linting issues in Jupyter notebooks (if applicable) +- Runs unit tests + +A successful run should look like this: + +```bash +pixi run pyproject-check.......................Passed +pixi run license-check.........................Passed +pixi run py-lint-check.........................Passed +pixi run py-format-check.......................Passed +pixi run docstring-lint-check..................Passed +pixi run docstring-format-check................Passed +pixi run nonpy-format-check....................Passed +pixi run notebook-lint-check...................Passed +pixi run unit-tests............................Passed +``` + +If something fails, read the error message carefully and fix the issue. + +You can run individual checks, for example, to run only unit tests: + +```bash +pixi run unit-tests +``` + +or to run only Python linting checks: + +```bash +pixi run py-lint-check +``` + +Some formatting issues can be fixed automatically: + +```bash +pixi run fix +``` + +If everything is correctly formatted, you will see: + +```text +✅ All auto-formatting steps completed successfully! +``` + +This indicates that the auto-formatting pipeline completed successfully. +If you do not see this message and no error messages appear, try running +the command again. + +If errors are reported, resolve them and re-run: + +```bash +pixi run check +``` + +> [!IMPORTANT] +> +> All checks must pass before your Pull Request can be merged. + +If you are unsure how to fix an issue, ask for help in your Pull Request +discussion. + +--- + +## 7. Opening a Pull Request + +Push your branch: + +```bash +git push origin my-change +``` + +On GitHub: + +- Click **Compare & Pull Request** +- Ensure the base branch is `develop` +- Write a clear and concise title +- Add a description explaining what changed and why +- Add the required `[scope]` label + +### Pull Request Title + +> [!IMPORTANT] +> +> The PR title appears in release notes and changelogs. It should be +> concise and informative. + +Good examples: + +- Improve performance of time integrator for large systems +- Fix incorrect boundary condition handling in solver +- Add adaptive step-size control to ODE solver +- Add tutorial for custom model configuration +- Refactor solver API for improved readability + +### Required `[scope]` Label + +> [!IMPORTANT] +> +> Each Pull Request must include a `[scope]` label, which is used for +> automatically suggesting version bumps when preparing a new release. + +The available scopes are: + +| Label | Description | +| ----------------------- | ----------------------------------------------------------------------- | +| `[scope] bug` | Bug report or fix (major.minor.**PATCH**) | +| `[scope] documentation` | Documentation-only changes (major.minor.patch.**POST**) | +| `[scope] enhancement` | Adds or improves features (major.**MINOR**.patch) | +| `[scope] maintenance` | Code/tooling cleanup without feature or bug fix (major.minor.**PATCH**) | +| `[scope] significant` | Breaking or major changes (**MAJOR**.minor.patch) | + +See ADR easyscience/.github#33 for more details on the standardized +labeling scheme. + +--- + +## 8. Continuous Integration (CI) + +After opening a Pull Request: + +- Automated checks run automatically +- You will see green checkmarks or red crosses + +If checks fail: + +1. Open the failing check +2. Read the logs +3. Fix the issue locally +4. Run `pixi run check` +5. Push your changes + +The Pull Request updates automatically. + +--- + +## 9. Code Review + +All Pull Requests are reviewed by at least one core team member. + +Code review is collaborative and aims to improve quality. + +Do not take comments personally — they are meant to help. + +To update your PR: + +```bash +git add . +git commit -m "Address review comments" +git push +``` + +--- + +## 10. Documentation Contributions + +> [!IMPORTANT] +> +> If your change affects user-facing functionality, update the project +> documentation accordingly — specifically the `nav:` (navigation) +> structure in `mkdocs.yml` and the relevant documentation Markdown +> files in `docs/docs/`. +> +> ```text +> 📁 docs +> ├── 📁 docs - Markdown files for documentation +> │ └── ... +> └── 📄 mkdocs.yml - Configuration file (navigation, theme, etc.) +> ``` + +This may include: + +- API documentation +- Examples +- Tutorials +- Jupyter notebooks + +Preview documentation locally: + +```bash +pixi run docs-serve +``` + +Open the URL shown in the terminal to review your changes. + +--- + +## 11. Reporting Issues + +If you find a bug but cannot work on a fix, please consider opening an +issue. + +When reporting an issue, it helps to: + +- Search existing issues first. +- Provide clear reproduction steps. +- Include logs, screenshots, and environment details. + +Clear and detailed reports help maintainers investigate and resolve +issues more effectively. + +--- + +## 12. Security Issues + +> [!IMPORTANT] +> +> Please do **not** report security vulnerabilities publicly. + +If you discover a potential vulnerability, please contact the +maintainers privately so the issue can be investigated and addressed +responsibly. + +--- + +## 13. Releases + +Once your contribution is merged into `develop`, it will eventually be +included in the next stable release. + +When enough changes have accumulated in `develop`, core team members +merge `develop` into `master` to prepare a new release. The release is +then tagged and published on GitHub and PyPI. + +--- + +Thank you for contributing to EasyDiffraction and the EasyScience +ecosystem! diff --git a/LICENSE b/LICENSE index 4debe9f4..c4e3e48e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2021-2025 EasyDiffraction Python Library contributors +Copyright (c) 2021-2026 EasyScience contributors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index 6278d78f..c4965e42 100644 --- a/README.md +++ b/README.md @@ -9,45 +9,40 @@

-**EasyDiffraction** is a Python package for calculating neutron powder -diffraction patterns based on a structural model and refining its parameters -against experimental data. +**EasyDiffraction** is a software for calculating neutron powder diffraction patterns based on a structural model and refining its parameters against experimental data. -**EasyDiffraction** is built upon the [EasyScience] framework, which provides -essential tools for developing scientific libraries and applications. + + + +**EasyDiffraction** is developed both as a Python library and as a +cross-platform desktop application. + + +Here, we focus on the Python library. For the graphical user interface +(GUI), please see the corresponding [GUI resources](https://github.com/easyscience/diffraction-app). -## Useful Links -- [Main Website] - Learn more about EasyDiffraction. -- [Documentation] - Access the full documentation. -- [Discussions] - Ask questions or share ideas. -- [Issue Tracker] - Report bugs or request new features. -- [Source Code] - Explore the source code repository. -## Contributing -We welcome contributions! Our vision is for **EasyDiffraction** to be a -community-driven, open-source project supported by a diverse group of -contributors. -The project is currently maintained by the [European Spallation Source (ESS)]. +License: [BSD 3-Clause](https://github.com/easyscience/diffraction-lib/blob/master/LICENSE) + +## Useful Links + +### For Users + +- 📖 [Documentation](https://easyscience.github.io/diffraction-lib/latest) +- 🚀 [Getting Started](https://easyscience.github.io/diffraction-lib/latest/introduction) +- 🧪 [Tutorials](https://easyscience.github.io/diffraction-lib/latest/tutorials) +- 💬 [Get in Touch](https://easyscience.github.io/diffraction-lib/latest/introduction/#get-in-touch) +- 🧾 [Citation](https://easyscience.github.io/diffraction-lib/latest/introduction/#citation) -If you'd like to contribute, please refer to our [Contributing Guidelines] for -information about our code of conduct and instructions on submitting pull -requests. +### For Contributors -## License +- 🧑‍💻 [Source Code](https://github.com/easyscience/diffraction-lib) +- 🐞 [Issue Tracker](https://github.com/easyscience/diffraction-lib/issues) +- 💡 [Discussions](https://github.com/easyscience/diffraction-lib/discussions) +- 🤝 [Contributing Guide](https://github.com/easyscience/diffraction-lib/blob/master/CONTRIBUTING.md) +- 🛡 [Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md) -**EasyDiffraction** is licensed under the [BSD 3-Clause License]. - -[BSD 3-Clause License]: https://github.com/easyscience/diffraction-lib/blob/master/LICENSE -[Contributing Guidelines]: https://github.com/easyscience/diffraction-lib/blob/master/CONTRIBUTING.md -[EasyScience]: https://easyscience.software -[European Spallation Source (ESS)]: https://ess.eu -[Main Website]: https://easydiffraction.org -[Documentation]: https://docs.easydiffraction.org/lib -[Discussions]: https://github.com/easyscience/diffraction-lib/discussions -[Issue Tracker]: https://github.com/easyscience/diffraction-lib/issues -[Source Code]: https://github.com/easyscience/diffraction-lib - diff --git a/codecov.yml b/codecov.yml index ffe29da9..f62b13ab 100644 --- a/codecov.yml +++ b/codecov.yml @@ -11,8 +11,3 @@ coverage: default: # Require patch coverage but with threshold threshold: 1% - -ignore: - # Vendored third-party code - not our code to test - - 'src/easydiffraction/utils/_vendored/**' - - 'src/easydiffraction/utils/_vendored/jupyter_dark_detect/**' diff --git a/docs/api-reference/analysis.md b/docs/docs/api-reference/analysis.md similarity index 100% rename from docs/api-reference/analysis.md rename to docs/docs/api-reference/analysis.md diff --git a/docs/api-reference/core.md b/docs/docs/api-reference/core.md similarity index 100% rename from docs/api-reference/core.md rename to docs/docs/api-reference/core.md diff --git a/docs/api-reference/crystallography.md b/docs/docs/api-reference/crystallography.md similarity index 100% rename from docs/api-reference/crystallography.md rename to docs/docs/api-reference/crystallography.md diff --git a/docs/api-reference/datablocks/experiment.md b/docs/docs/api-reference/datablocks/experiment.md similarity index 100% rename from docs/api-reference/datablocks/experiment.md rename to docs/docs/api-reference/datablocks/experiment.md diff --git a/docs/api-reference/datablocks/structure.md b/docs/docs/api-reference/datablocks/structure.md similarity index 100% rename from docs/api-reference/datablocks/structure.md rename to docs/docs/api-reference/datablocks/structure.md diff --git a/docs/api-reference/display.md b/docs/docs/api-reference/display.md similarity index 100% rename from docs/api-reference/display.md rename to docs/docs/api-reference/display.md diff --git a/docs/api-reference/index.md b/docs/docs/api-reference/index.md similarity index 100% rename from docs/api-reference/index.md rename to docs/docs/api-reference/index.md diff --git a/docs/api-reference/io.md b/docs/docs/api-reference/io.md similarity index 100% rename from docs/api-reference/io.md rename to docs/docs/api-reference/io.md diff --git a/docs/api-reference/project.md b/docs/docs/api-reference/project.md similarity index 100% rename from docs/api-reference/project.md rename to docs/docs/api-reference/project.md diff --git a/docs/api-reference/summary.md b/docs/docs/api-reference/summary.md similarity index 100% rename from docs/api-reference/summary.md rename to docs/docs/api-reference/summary.md diff --git a/docs/api-reference/utils.md b/docs/docs/api-reference/utils.md similarity index 100% rename from docs/api-reference/utils.md rename to docs/docs/api-reference/utils.md diff --git a/docs/docs/assets/images/favicon.png b/docs/docs/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..27628988594ba1ed4a6ece48d766e31457481367 GIT binary patch literal 34184 zcmW(+1yEbv5)KyJ-QAtyZY>mdcXur=!J);AdnxWv+^x8z6nD4c9{lBhlguP@?_|!+ zoIShy$$nK+kwZrzK>+{&=%3|fH2?q@=vNp35)k@f1F&P0X&Yf1RBGX;tLuo(#+9HkbICPVeY+e#Z)X*O%w(;nql$7Yy%!71ky1*{$sf6hdQGxL5fSc89#mZ~5F^%=mgH?%X#`85={u_X+IDfX zC*9nxqh#2%pU_Zr2>j9yeH`@5i8A9r?aw*G)z+hmXxs5&UD(@0u$I~(DL4UgvKO|m zv7iO^OGZwyEhC*!Mm+J#O3M3GIWn**0ru+*?t-AY4_sS6U>JB2xUe7X!u@#H_!eP3 z8blKqPUY$ld;jQuJEMI1>#Y|{%CP_9rRrPNw=zhQt`<67me7Y`1c>^OE=j>1<>p(D z+5BkdO|m@;SdT#Lb6Tzqbo87UWB!88N0eF?WmS7$g`ul*L2*a+#O6}D`MZivg~1#9 zHgV{k$8Vc90F!6a-u392kGDWGqA+J9;>H`xq=twtYpyK*ETt?wr|>Z6?=2Bc)Oy3f za3ms`+rX#+#8fRmy_w$2G)%#K@syTg{mM>(%zh+jq@ck_Jjv&A8gblUx)!Xv5?^aa zjY@p-ZSE6~>a)jdfb&K1S}dEp)^qV^n=`ImFeW(RpdTsQ@#QrtIcCkTJ8Ow5=8u1| zoP{W1|8*WtYDH(zVDQvWdDYKpuO3obv7)j5-higGtR^u)B=fV->Ej|_sw&G~!-m5V zQK=t|4&khlzuN7`A89|MnTM)t5J@d)oac9UKhk$-aQ{IJ-qj}rV+9r%ji>4o(`<5F z-r@M}+zpu$Ie%WPUAj(9D;8W235+JB?Pt;>>P_^PsXuaCTa4S-U@d$({T!vCaa&Z`rI?0U zWk(<0-`j3&7+6so01bTqEmJYA1N2rry^3etbZS%Qn83t7QrS_j48>pi-oFDDb#)9W z4#(N^4eYO-Du$%jhpvW|FF%_BN|WSilL|ZJ3(Lb338{*8+ zlzwRH=39i$6sBjcu`KZ735 z_nA3*KAkq&GfPC)w~_if@9e{i8j^??E;1VgtZNYHUN~&2h)zICQ3-c?AJiZ*4W3P` zM`V)VVECi@6X}WpCv-V(DE81}O`zb=j{Dgke6l>#>MQp+_v?e&ie&Z z%L!*vy8+!IdAQZz@E!U4PT{zk#3n7AVDb<8*Dcs%NIy$U+zir85#J;t=ew*g4>*g8 z203%GdUjJ4CvIxKHr*L$LR4w4e}t@q7XNrjKs+XP8}@+(9UA-X8m#p@K5f!;bnACM zCz^I)BZCTX+0`}XnUaT;+H9I_el%08m;`=TP6=VoUnY^Z*PVm*%CD(S@6PhUjA;GO zGpjwKb76#{aK5W!1g)y8xtz}eSy3+`%-8Gi59cywH-C??-VA1UXG(DR0;|GnCcVy z#!%``vA7$p+EuTLYVrqdD??QXqQGKF`u+#!(H+}wY&f^*)m|jWzkUBIGLodA%V^(X zepv~U6bFf>e`$aK?M@2@=f8d@`eX$=r~s1_j6O~bB;zRd-4CVVT-W`P$wLz#S?Hwd zx1<|nI58^$X8_$U)C}Y zd;bVWx=`oBfabcwY4UH0f!f|P45`v-R*RIb*#Sq+##kyXMeBf*or6Xcp?k1x6^Ve z`2MBmY(d74=Yf>ndfOFgpA1JZMan3nASpO+DnZ}r1l)Is`UNxl_{(9bm4^5uL~J~| z&PU-}Rcv4}2UTV+&7zE(?`A7sw5F~koN{)$_YnwcPZ0(I!8h&q%0iiZAA|e2ATVG5 z+;#T)Ai@wfS<`jgX5@F=$C*DTu$qP!*d(=jV=A% zH+~y#R^P)TwE}%|0`#@!H!ro{t5_yl`Y?MXMUhRh(6)3{1NLU62WL9jWrDG{NsGyO zM9!Z{VDU!|Kj>Bl-bzOfMXg4ri7CCfw?r4xx*lP0g*xjX@Ja<#PTDw|_*i|{C1d*D zDzJ)ZQGZzR&-C#!J$n!rm)V!v7suoQZ7a`KG*Ek%e~%PBx%I1??)T3DC}p~&fm@F} zjJl-0IZANqva2Iiu2egefIFTYTaw#s*Pp9F+!q$lWFPT~zQf$MEQ)gra;NS0=aja2 zv4m59=i;DAe6EzJtpp+LypEi_v|V=C0HA%&;8k;G!`>z|`yojU#Hs1|tRv`3~bqml&=>bF_C+a1)uQk>wp=|THTcmX?-@xTE z<8VPx*P%t?%Z2+o)=!Yd&PDindGN~G1b;-A^Y>PCkCx91JoRY(IQ`l~cWE6bnx3MS z33Btt->C2igm?*n*04pDu&a{*=~c89oUPI^G*izT-d5E^E;|2zC*^3@%sq&Z&l9Ph z`%xo?re9u-`FhTY`Z&dR-2#Vq9F{@t_M>&kUBVklq(@Kt>0oH=KQ0aNXpd|vITgjc ztt!3fPnelbAE;3eISrR)@_aS~mX7*2p4_<3Mjf&$gUi;g^&A9^s+;u6g+kpMGl&|zGH`I>%!KlK}vIJtoMho2FR_PXrdD+u^dKhmn#JKgMQD!0kKQ| zBw$5XPs7(h`#pU2_HCg_IcUObPw>k+SBPogaR1$W{$%F+B-Z1{P`CzMTsP5uOQ&Qc|4-E%+DiV7p&dm~$@?JZ%A{I+{_>JVO*;h1W; zaUczcZ3G_mHpVBCLO)`!9JdFcLSH*a$F`xHK4ZF)CylJHh&z14F!eIi5N z#zE9qmv^T|?}{YBMDxylIvxCQ$i4FR4Dxh|S2eDM_rGMYAbmKOpt_1~a{T57T|D66 zTFJJW-IIjyi@%9|n`npL{-gsflHOlk5~5_}-`pW>9fR^Pw2HJ+t1Zl;Zf?--f`3YO z#lRMpd$Xjt(p7hV5zk>UeD+ViE#N}%Z0A|&z-iYOZTlUu`04ohP=WpJb`k~J0rZco zNV^S^yaBOaQ*c2W{B5qLp}4d9Y%;^&SL-OhSm5Uy)ZUatqRgjD(BUqzYnwgKg`7*s z!liAIy5{Z6bLZ`){-_vtt@@`*9F-dElvsduY7hKBSTQY|O~2xwO}c=gX<&w*F{dyo zzb3TR)h}4m|ixeFD=1?(z9o)dmp{e!k@Io~ULHjGLsx-?PC%TS{ZR1GZ!zqNo?tmHZ&} z6uZaq5sp+y-%Uy1O848Z&2!4IC~}0t2hDR2Sw9%^4xyGLhl#Qo$Bo_5`iqfUb`pr-0 zoeke(12P*(q-Hyt|6Ll80P`|j!BA@tb1D$=?i3Tq80zkgYWMtc{_gSQQ0MXY5Q5)V{iC|_#}fRh~MsJ@Ye!Usau zzrDrO83mb5?`k)i4Z20D(&)e(a8)}7n8ugh$8d?5zty{-KX zU*C7}Ul}zRTm^-P-U3ChSYbjb$NG#Tw>GFCp1zRmmo9p9Em243vVZh3M31Sp1U#IxUf#7%bLTZYAuwofy(S#KC)tmdU`hl; zVg|pe-=mx|123mPP*iq+dEz$lwkZc0Toev3Fs@9in|$Ao@l|T(a7|W~`vAk;&ks$u zqyvtas%|G=S~#!3E0@=w+t?9_EoBhoQOHI40tL0h!FB!LXQPn;Wx6M#UmE7xjfG5T zjf?^ezd05No9`}BGZed72u}#C^Mm`PD_y@>Xa^NkzJ@_&^6;_v-j zdQVqk1Mv5<15MB@St0b6N`38({m6l=VtVv?+&4B4e7~s}^4*~%jjKD$`mb%hCgT3> z3V2ZNTbztfgizbrPGhVD9m~E%3w-pwGD^sbYgV2{3b5HcrkE=Q(QH0U?pm~uJ?Hm~ zH?rRS5!5`9sDus7Fu&vnFIE(QI+|B;f%ky_H@Oo@7UI?~P9kPy4Oz%1Pl*6k_C-JN zY^+g*&=opn<7iZ6<6G}i3mW!${)DE}bEX`h*Ji|Z_Az=gDE9~aS&z5yk(|Xiz9xas z)B%?;D_yb`UTExI4B5{PI^X7aG`TH<=Aq|;p79S8%^a&0E(V>>T<~vc_M>1Hyx_O2 zbz9iZalnc&Xo1(asxJqduQPe!^ZT6CW)Z6P#?37a>T8SyHd;N?6Zz{M9D2eF6#7}Z zX02<7)Mes0E$7F#&L*-;NpP}YiRAC55Z~{A!jkb01Ni)Sq^=I1!f`|CnbETLIc^ppe+QliN*UusK2BLR33LFnAIo@c#|?X!&K+X(PzKdb)R zZAd44jPdIA!yiujWcd{x+IUbqM-~|(Cp?&lAsjm|Jq|u8l3rFa3uY#qb2<~}W{!Is z7sd$h6Oqf&@`<7i3@^NY%9`&Od>8VIXC$mj=Fh)hX~BOBnw+h`R-j`?o$#MXwEzL;ImLm?9(s&5p;}y_iVZ-X4x3GvceWM*E;7 z@(YTI5b-Ug_K_VFjS9P%i9p0~KKyWxx9_W?maf>owq+#rwGlFdRrhA2q8(7Ra^4l; zY2@#{ygiqP6`2NX1plSa;DMr?3uT%v^vrK$xqV~%zzdP~re$R>5wlzo4FNl>B5}gP zXV%c6_X9S=SE3f;z%4xjmnj^fmRx6#a;~GP7U%D6QSEfxf3u0F6Pi}sAdM-DzTPDS zq`M!(o8S3uhaOq*A^vqDBnmfkzqXf;4IN6%y8Nf+tAZwToQ zp~7x1%?%Xw^n&PiHx1~?Tjf+2vUl{QFcNoGo^oYPMQBvcVTUK!GXJ$73BL>a9z9|o z@{RT!YWO0!E9!O~ucZ>(4xj+d=mLGYD-d=a1oA(WeXr*xlSga`QA3su-<{}kK1Js< z`$HXv-vGzUP_O2ERmNE7QSsNv$9CPnt0rV4{IZHlPUB!2WnW}r6%@QMr0Wqis`~YE zxSoexu3#v8q9E5`N26p&k8n|i2oS))o7OOD@agC;cSz2!{*wIlh*bDpBttX>taK-q z?(htKeriYAd8Z#!mPIEqVwPTE11*NdeDed5wstNdFJw z2|iUcnYU)7_pX4a^`FUDzmMM=Z8QA5oBQ*Z^nX$n!nmit;B4Ss>1p&uO(L9eIv7C1D~jV+KWn-=79nVxJj(eST5_M_?u!w1upMzoD5E6dgB z)*2{w%tx0c*6yCg@)N0dT?a=`Nhj4^HlR8}i?iP%vV{>9YG355|Z#w&a-Xj{F~jtS{6!) z)=5j$eGa22f0V8{tl*yglewCdRZ^S4RiZxV=9g?&64AH<-^}%h>6hriY1uD#%lIU< zz35RL@Yci4zgi*h;pXytwGQ9TD~#vk4z!7A){1a?nywya6jd zY&{=kd*0jUD_?%^u9l%Vp~Cr&Fe7S4?4oU>9wrV!7!1~ zk;fDHEBcteUxZ)^=1GQ8Z#@d=^({$8d|>4hzh1*(m$>Xv`_dS$7V;0SPZj1l9QArP zy5U1eYu=Y~p;~JsiI~eLWzJ5)P}qVFqn$?Gl}`zUc5WAn0Cvu`e%+7`Lio!y=Y9jE zm|lVp>7|Dc@&2B0z;I3zlU>w|#g+)w(``7@ipy~A5rNiSJGW}IE!zTz*z=PDcQdNaiQ8DX66i`+^=7CPLzcmgc%eY{3l?N*N&?> z(|ZJgrY+bEPqfuw^NV|VE8XQmlYL;fJ(6pXMVFFU^|`U%YTw;meas6yc1ED=kGX5e zYU4!e{mNO7dw2`66a{)akp9NjFqMZVLapdkHN?OyQEfCU9-*Z-r7nw>-Z>>!G0zXcuEW;7s_}&2J z7{t#sT-rZzI|udl!F6AVxb)=L1-`tHK#_AmE<}Wu;@k^ShlGP2%s4r+iZCs(b^mfc zJ-0~ZUoY1tzjPg_lVgl#rsl$@qJVlV;z&Aa&311cemX|{K|W_A0QRJ6r%AIs!+-*H zo*))Iy0kOaBC~HfjijKXejr=`j6l6HsMw4C) z>({~dsLEJ8Nj7B@T=aTd%{)SufI+?W?EEdN&J5NLWA2U!QaX3K@0{Te`Knd!8vc(+O0FSED1nJfD(JOzLAe4t?Ap`VcZ+|`bMZme5v1>NHu_yik0qU{Fp>=L0|hHoF#@Zzuv1-t>r7bi7qiu(q;TL`@RTXCJn274p(M zHFrR~o>^dnt}h%TvPyb)A8eF|kJCYwS>M|IU-& z>GkMPkZ7e0x0>|Z80?FLlW#PL6<#-%eWJ`;{@wHI^B$Z_0>3?OU|E*3!2#=2R{z z+hM%(0x)+$!_zgfV%ViMq9=i;eM%TPmN*l;DRXNelsV6E;9zCPG<`n&+y#A%(PkW% z>X{lsM8m^|W`&oRG9H3W_Xl$ff)CC)l1K6&3Cn96Rrj|7k*HX<52?+&augNZX)RdE zi0H`$bTrkiRpXnv#s^BS*VFDeW=2-|Ql4-wwibWxled(HEm&~RgQ+FgqhsT#P%o7n z0&BC$^B%KU0xjl-)* z2M^iPQV}DocNq%AsE}P&(=G%1O<2_E1nakp^f1|cd`^h`;5P?pwcee68P?1G65n1p z>bAni%jWS<-dloezt7&_`WK8n87w`TGQZWBBdzh}9aLc+3US+~aKl+#1;BxsPWUss z=%Lq9{wWn(xH|$`mG+s=wUmB)l@a^e6izPB8>li#m9Tm)G}XO`jNG6^e;X76qE7-g z+*kT}(3}P#2B89x@#H_w{YAf+$48w+cC7;=&^6rl3hhP<#(sB|L`@LZ>DWDzC`Kq# zigyA*-t2-5)z75AbLkG;%Z%1B|Gh8sLDmfZxES@agAZeMS=j_yX6^)FiUefa+B0d$ zEiqbU+dv!>x~CIKtyX3dAr-0pj+j>Mj>6=mT7s@QYrM(i7WqX~pY7Vj3%dW{Gt$Dl z!kV(vnC`N^?2Pp%A|Tp0hO!iY{Erj*)h+YSZJ&{#D%YUmEUD8~x_6E4PMCtml5}O^ ze!7-_RG)!UM6K@|!3CggEy9s~$u!FFmm3|NrZ?8eX3`Yd8spMFN$}Q^XCaL3KeuLl zvPZAzT%;55T^grJ)#aLjD&U*@zc*xa7VBS}jzbEI50k}yV@f2R@~gjK?PYw20)ZpC z`TC9@cMi>{r#v0KDnav{`Gl>yNiA z`)Z{P`KW#|%}-`#H_iS7`o=+AkdGm=jPG(_6jYvVj%r2FRY;w?m;u-eK^?Q zsjd637`7@Mj>%Dm_VjCR*nN9>1O--4bFQZt84$<7=%xR;=C#eaUYF!>Qh*ipsF}Rk zr#nosTx&M6*e_(T5l=B8pam$x94myg}{w}?-b5whqPpr z00XOU7gd`>yU6>U{I-9yZ{yM{n!>?@VGHg*Jp=NH&yfn-brwOJ1PiSs;x8}%$sC4v z6M|jwUJsiNuLbhN$up_Sa0&QMz|ukZB>u=86z}4NY<~3XihNlNOMR19mFx)CG@vS{ z4*x#!seK_*U@J?OlnNV8(F`yAL=s|19R_*ubM7;$u7ch7!Cmt>S@+HhuK2hvy`q1^ zahzfG1dbe(V!gw*92pCV$Rkro!p^n)#^M3(zvaMn4SJ|E_OxF?xg)_dNr|IUKO~3I zrf*tf>nPxyTr@FY2BY`GqLb!#p&Mxs7p9e@a6*aFUUHrU%K2E#;_= zd%EbLX3B(FK(JkF;%^9--%kj!xcaQez<9T6(RV!gj?wscToGmMbb+A!XOy3S!(ZOYZuk{@L z*yhQhZ9e|UU?TD(Ij1{4)f=5)TqPyV=OIe{@s?nB>R@Edg@=&u=DpIe858K!9J=iR z?{wJe=!v8>o2QVMaaD==y)sk03+kl|7^ZJeld+?%AJgnR#1oBJcOQj`;D~%5l>eH* z_oK5S(B;d_i5`LBsr+mIB99-dJNV}sI?~{r%C=AweZqqLoi&Sg3q*%C@jkJU{UTA@ z$85Rw8ai4un$R0oM&eiKpmnKjNvj<|uDd1+k%tZ6p1cWy9{8R6c8g-V;!EfBLeLh3 z?beZV0eI;NZot2{K#(KSj@0W#%IjVV!*g-}PH@6MEONeO zhq=EyEPHk1EQca3U2f{mU3kXjSsyzE3v`qeKygp|2IB)lFbL*Fv}^m;7~hUZlK&}M zVwNgPd2d2p61N9$`>~lMVWP^k|`$WDdn@S-GaYu!q3Ih%6v}nA{r%ppWd1MQRuw)u*y-X7hj6HoUTh{nVS0 zKAP9(I6#mPQ?KXa>tC_L*Xv%LtI|M?U3%70h*&CjeA5^-;o>W8-)k^BCR zDW;MSiHSWe`<~H@hn;dfA*RCnTZr*N>~36@YqPQ1I^WmGNLxwj?Wf>DdQoci$_JkO zBMg7n_|p#VkI`C{ON7LeZ9p6`q`kq@tBP?l7j}a@p)f?|bE>@$U#}?mqLZRuV|q;~ zgMO@C+xrLaF+3X$75+;QX!O{k+;%S{p`cO^$==oQe5$@nreCoNH?CkZKlo(KX~Ws5pBObt(oCsRukIea5)bYOMM+0cLR316K(Ulsb!Dror0Gqt=K$T zq{gw(mYvvjU@Z;gd`z7EjcP=ydAsksA0jNj<~q)eNotAYB`!PGkmAJ`@hk`qVEQFT zKoEJpJF^?Qf(mk-o&4DQGzNF)3}Q2;6Bvbn@H?-XRqW#`@VnT0l|tC<6w$B1B4S)s zjV{?C7HidSzX>LWK16x5w%c|!UiBd;ih?jvDtrsztboC)Uxj`-&;;UM%_Q*r@2V~(+~3!gZGJAc#C#;7H z|Js6=J}O-g^Cbwq{8)Ax0M|HM30~p;iv~Db?aji9C&;v) zguW>P)2Y|E^LEeMMcd)G3!|kRSMW@D^R|a(i8XR)l;ir~g?ll^kvd{!c_K0klwq#E zTS!_`4DqJ!c2wEek+T$Zq?*0zN0c8WhR22t{(K2ayo6>rN@(2=YX1TP9fohxW);&$ubAgLjLx#qnoD+H0Ppwte@Zj8*94E%# z{=F>;1Wrb#ejX**xt||IN@B#2kAgace_f$ z>+Q8OSotz?ow?F@M)iRe-g7_F%e0IOn$uOM?iPFH$x4k){cI`*3z~wgM#5x<^1X6w4H^2CQ^69BqwC~+MxzNgnCYy7 z529o%7UIq;+C<<&KuPcK?-~lN9?}}8awN`lHht;Ea3Pyh~9MR=h3X1PzVG7#6 zbAGNyFilID(B?=dp$!hB&Xk3n^H!~#b89nl;Yw4EEC4e3JRceNmHC@GTL2_(v~Xv! zwC=p9Qnq74ZFL=d^IgmW_xMQu#sEW!Tnom`NoOp2ab&%_oq2}xG?`0h4?g`EtyR#p zaAQLBQ7YAQe9UEO_H`&BW`)g$Wn|E(<~7nWTnGLz&ysxj;ZFoZX9n#4vz#8KrGh9{ zaWu*Kx#)9@L}718+4$_loupXKC>=_y`01GB;lRxCK8vl6FpS3F@Up2_e09fu`#mcb zYJQz)tHypp`_3@Qicye4(qh1PS@883gG9h$)cbD+(h%yfr);2~GM>jcdxoduomH$C zmF1RA#J9ew6k-NV9X5xA076ij5?3-l~14c{+>c!G+||RGiZt#p>5kvL^6_49b@eco59MJO%9H)2_F-0;^;-@&(Om4=5e$5(F*OzRUen&r~o>)>qTocI!V_Kn`#^(uyqd%h|={SFLloL(~nkYicBJ zoB~MqggZUbc6=cEtB^LCUGOU+Tt8>EjAxA}YsYNbhQ~UIA6SsoeL~TzS;cQ)@HJkWuM!gcr*UtSXf&SDGOyB9;}H6x;^z1R7)S{_E{)JdxsqcOB4^z zH#Ib#rA}`$f4yeP^**VCfg7)b-xB+uHbpQ(o1>Yf?1NqxT&a%df?-FnAQ6>B_<5hfS zB2Mx~0~VN0x$9T7W{#Bztc3~O)Dk`(q4k)A^TXLazf;0tILiBc^C#-)uF8^2S;}lG3FqF)bL8R3v)t7MRbo$EpKF8OmEQ7|ckMcwqJaq=ry8wIh&i`m`zOrM zbjt3QCUPKjSmW}7E6!}TY6BzSgo&|~Cve1hc1^1Z_v|U}K_Biwqn;E0#gHr9HoYO) zV2v>*X7#r4p#K{xDtp^P#CKGRSK`n*=oHT5NUQRPWFAQ8;iCV}$p!osBX5&2VJB$v z+t$9}8Xk~X3*qHc|WrU0=XTJHOZ{@4HvUyLKEZon2b&CM0bPB7b=UxJ3T>2Z1Z z=C{rtysrTCZJ!C!K2FRc0eMH+`|5QKOkDQklW{(P5tP zEL)@p&!7^VekWP85@-#)9mx-G!qq=8SVwm?{BZzz)lM>Zg^lVGH>iJ1ShdB$2K7fE zGHjn5Y)!w|{oX_^?F;4Yav|yQCrGg3e~Kqbq#g*DWAwc|l9gqPhWRRi95??@LUP&M z>)pKIj@V`Pj#+@*?8e>CHbt#%NSpTX>5MEEXAfV3Y<^2&z5fpQ$~8ziRLz?!PvYP^ z-nhTeLKYyV>s~I45IslbF1z_h?PMR(9KIV(DodU?@Z_s~0>NpI#rrmImp#FG4$^PP z8LTGk4rrEbO#;TAHXnH@>hexN)qOLt2bGX|k`ws~Gta)<^tGG?`Qy8~S&o+e;)A}k zQ~BvSk?VV~o~(_m)hquNwHbdkw`n+S-<0yLiWNKt2@L|2cs+D)d3&0d@uA z^mqUnYJ3@z8qs@>7wj?cHMV_;M6NJ3dX4?)u7cOn--|mzXygt_LQHJJYR_Y6FjJQB z<~@5{@Gz1AAW>5|#2*Il8nLvQAi}a@8S5V(nd4V~PcigI7JVit3yg#ZgpTh7YT(m5F(d#8vhdb;ach8NB=H7`o`LCBz$?bx-`Hg=r#Jz+fTiNF}Z9U54Y*$tDR^ z^rro2*05mFu<#B428x&=d-KBhx`;Z6vakUIhXf>|Eo(7GjCt$t3iDqjMF&5GVN9<` z&_KPS_<{!VL;qfV_O~}^g5XNd@g~c*nrGE`i@42*2S^iDxN@31F7YhSDCc)J?HO^t zoRHe6}I^f!7Cm}&8_EBVV+oy}>#6%xhEL^Ee#@_YS4CB?9N!J>6nS$p6gE(8(E zB|2WM8RV_nTsJWFX!tx{(kB)nlM=w3d$gf{J81yDiwd=3 zWepj~lbjpVze&lyQ=@}KkP;3xT@jJdNh6UEOFfQL5e(Ea6GvD?;lZ-LHafEBv$irV0Y8ewgjjxFotmGeGU?hyY(XgGyyD~|Q>mT1 zk~W4zw1&U3KJ*}XIGgyGFakK|fTsu!z*j=nN>NnS`}*S|NGJzt9eLbppReJhH8=WQ z@Z+@yEJyY4Z0VJ@?t>^iUZW|n1fUe{PS_t_p)eoOmiM$ z`Fcz#Jm@x_-hnh(_v}5*>0pOh%%EO+EE_!$$%_3esHBm7;vCn0<1t|-W9szLu2|~_ zba2hmbRl8w6|;2v-dD;_wXYbTt1?<=Aft-<@*2)hJg8l^WWU){@f7ch@z3)!_UaNk zh*tV{uJ$ue2s7mrsrVi~k9l_0PiL{8lsv#kBDfVe+42@6if#jxxG-e5zcY6MpzjOD zj($){+`M^N$_z{sX9SO|BHa|8bDzk{P4{#`9GVzWDam?WV||q%}~FO@><9 zUXgh|@R4kwTH9HBgv|_Ndp*C1+f%zUT&r z44kmo3S!*SV`wYD;wyu)T-V-OztUh9PZN>SQ?JltWr@C`WN0gXgdm^_RSr!faX(Bg>@XD(QmwEpiZ>q!gaNlF~l+gmp z?VK{PZp$0Vm6?2=L~)I}?7OVPM~%TvK$0Z6eY+IVLri0B>Oh1wl4cK1M{ipt>7wZ_ zQgfZUt1LtP*iQ-wR3(E>oTO(?>XEToWr9maSH-?k^UtqCR<%nGAZ+u7MwLx^_JR>| z?dK!#(!%n;k@$a@8HN@F${y>Bv=xE6L#ac-16c-{&d9%7`u_(`L9xD(Vw!VnjjpFr zXxeMUAJgZSKZmS8tnT0S%JF$iMR@!c4Q%C*8vr&wKTdHYH(dhP772j7}&&Tl; z*AJ3JY$5*8{`wj1`dOU&w5OYYW-=C51XHn%K$pH=A!WoH0w>evy8R{`NxhE~?)<~@ zh`wb8VJjx1c(VMngEGOm_SnfDrq#D zLB+YDv;KC~ks#Ai82u|j^}^IKmv%uNZvL{Z_!VO)y3nCY6GmT$i4<_Bk>qV zrJZPX#8=(O5Y_X8su1ikq~iMXx7>SApkIxuTSZJLym*2gyI)`3k2Lg;`1XgoavIc0 znb8z(8`rP?-+_z~&4VpNu#9yjjlE0!ByVG2!(Ui)<1qsGom+sNdA0x6jW_`*L*f_! z8gWxj`;T$2F@Z?#8K-8%l!@wNKxUANxzlUO-nsHSESNFlH>Aw~B9}rEe8z1^<0Af1 z_5P@FZr|=7R0$;bsH^_IYRWJ~24Q(gFp&KDP)uM1t{dB5615;9E(Tx(;|^}R*B~yr zNNnPfr12C7)=zN!;ezEj7DJHtw>3dE11@2Hb-&UGd* ziV?4$T9B}h-oB#x)W8ub3cQ5tdoM%W96VKA-)Hl;PPpqs%=+7gH8}M1LDu)Eo9$** zJva@E-eC+wnuZ1$p+Zjppq~i32bsWjNCTmd2Vex$fk~bT5a2i9c%CgD|Lg!(TT9j&QP>k8M;}*H_m*j`{*-AjpHFjmBFA(#McOim({`t_ zHjoXj$U7cOwI&zVsw`ET;Y@5>ME?Z4e}~!?EW^MMi3}PfLd9}?)(pX;ohrN=OhZVS zeRktvo^RTjVK7OctAR*@8&M_b^bezF{oPbVw>&R3U9MJJ%w?_6YKyHv4JclRF2R?Z zffN&l!KSM`UuqEky$0O%H$93o#wn&D94KZvKAmSO6Wm4U_LYgQ8~h_c);2jZmE%A$ z%W|#Bvr8ows}1}($NNFXI+l>*irr=a5#SlHVi}@qAT2_If*1&C%M5F~zbXLRC<*j5 zFo;S3+r$WB+KXfe!6l~WOHE!pw>2QB02LRmwyr-0(C~xcpkW{aYfXo%)fS<|AG^qh z5cvUA$)_ytm@9HsZxfIW@R5`s^qT#qvHF|y>g7_2RV&j=@ z8A$XRZ~3Yv!xSk2p`S_w(pH44YCi)=;sJMl8PrnS(_W~^rG2Um@bw2y_L;Dl!l{)y z&4}|pk02%Y^>!S1B_c8~1kavZYxG23>e#0HA+6_8_#a)5gYPz%PxJD_GaQ}F#filO zgSJKOTV{*A?8q!r*?74=zs>bf{ydJ zrc+0^{Mx_G?(q-72*$+>1kRNjT&cD~^?U&?HUe9~^#Y#4scLYF5eBXgPcBy5*MPeS ze-K080Gr2wVwU?3PBWFYM?P$egSkS6`;N>pn@h)!Yh=AYs@Ud2X>%(B=o(eiGNfeq zN(HF*Y)b7yLe@5Vd+FQprkCSn0^21G48sT#jj6YjaHddhc|5&Ti>%%7;h8FZ(}eF8 zxYC3R&4}G!0_*?jmHKAX#Nw^zQTQJ{<$`zfg$(x`m?USLi9S^)Zba`-qOYtlk+He= z@GMi=^yW%CDEfC0owheZPo@lRok(%4U^AIAunocJ z47e=;w0vcy<#D#`aJJ-7iHSfW5Uc;zLWT&rcGTstCIl7? zk+P99{1$LEOgL3lvbKr6#YrVj71#lFpo8V4NQtx!XngfyRfFy^CbSv2p3kQ*l_EXk zI<|nv%0rK4+hL&v50|42mLl-fQZ)z!8p@Ql&!ax1+=b)A6Iu2bGO6b|MrUbQdF*Br@nbJ6bW4s&EG&%R=Y!PjvV@h1@d+hCgp z8OtIjBG8qUW~*h?eAD+;FGo!xW0~!s725w}^j(>8OdyJ2`yel=fiSYE9bg2#vqylz=*{no+8pE=v)_;eu_ zk10cV`Fxhw9LO@AG7^2=&L^ACntbKK9Cy#8V;!_?)8Ll5$&d}Sr@`onM?dc~*pz7o zCwS3X$K{)fS>JF~5#dsFLk3y9E3jK4CeV*l6GMz)e$ARrtY*ANbXTJebvhi z*QeuudP!BiO(BEY3)40Y3Mnh5-yxYm(szTX1S6{ju`fc3jFB0%;mlf{hc1>PTNI$| z!6#M&PrKcQrVpQ63v~Y|Fa$1@n|$=#a^NkOnDCQ}{2fS{28W8-Sn>Qt(FG%R1!#{62u}mw6XCpf-y1!Rrtuc6;3YKBE?G~aI*R6GMuaLo=u?U!6#SY z$qKOytM2;z<+F=itG2?ueg`6awut=Q&!;jr#dP?2zXEd^lY3{jn}#C%m@)!$`7~ApbsNBorqUE_V{A-Qf!ValZIjUn98#9aOg_`6 zl|i2O0TCuMDUi)qfj6{RT?Q^GV3>wr3M&LsKqyRR>~{3lkb#Xu5{R5@gk?A>g0_Vb z49E;N*$fia7kGB1&YzuLr0zr?HwjSl;nQpIiIw2$@ODGX4|e>&TMBgk*l9@M-|%;5 zmNt@q3`6*k=sTJsOl6|etWBg$juxzK5t(i-+T^To#v&q2XHya3xrU*ocrXovVkSj* zFwo7lMebeA{RQa53l22Jwn@*2&ixTc|qik6o>yJX-br*_^ zEYwpkNuQglw=0Z2$6wz9@m0DX_Eh&3K6-AMoNe$m$7fXlb8lz6AZ9U2X73PhMfM)_dbGDbq~Kv^O9XWBC>dM0Gzr57l*WEVjD)t5oiLG3_$#-l1tfSW75qVBB%FFWPCxee)7%~Kk`T&;90;& zSvMVacy!t{naQTOR<1{kF3kNlf(byM;2dox01?h2Jufyr3V9!bprFWrj380-LokD= z>TPg*<*%Pz!ts=^yJd!SNZ^p(9+wmo*1noKo4I7Nc{+R?Ovnf)h8TH!pmD3{dyD!fNK|tCR}w6sETz&k*MG8J_OB zGO&4+37Nq9<33CvH0n=GyIcB&zKlTnl-bl)SJ^~#-uvZ---cVB&)=R|qUv~j^KEk! zQ)cYNpiWN}FOUYFtp!M7U?NLZt)*LO-fWYN@os|ki;2Ht!<8~WEm2xsu@NPGfYhU18@NmW^)+~LpHJkbvLP$ zeAUyPCM{S`0D4Z5i-a%$0p{~*OvAwQeFlr!*N+Jh{(c=v(ns_jMJ?#6l*n&?SZ2^$ zFj0j~;HmQW&#$o3aQKI}&2w}j9eEno-Fl~orVrOzL2!|DJf%ZtZP%faSP6nr-WQ^!Ueg5?~s_Ogaw;6X#u27 z10Nhictn+N?XD#4$>mcPlNp=n_)`ho;@fzMwm zaca57R~(+=frAqqDx@}I-3{1eqHf!$FEu@$TCDK#^D8{NTnmV*uPJ4mgdhH*<@z*S zk8CRZC~%?f(egDmA?r|h0cxJla?^`gCPLluLZSzck@(v%o6lewQC^AK@|DL$;MeaD zVo+{!^7mAqF+~;?4sDr&2!|$eJYuYD=sJB9Y7EN+V0e`vqWvgFu>QQ06vDtVHmtd- z;P^hSubXwafo%rs6_XX~6pbRT_OrEy%ilh`$Y(CDa`$|Jmmi$u*kp!W$KpK#GwAH} z5S&IVRvjK+sPOsAYn(4NaJ@c90|TA64Nv%;!8*QAsp&AC+v*G?0?SR0YjuyK1#7%V zfdZGRE~QAPEoH6VY~O>3na7C4Zx{yqi@9~f?xuEwfh~eQqq;H5bq1h8p$ot>z@ZTP zA@hX{xs*wz*%SG6+;RMq_zOS++dxG7oi_rRl*M#5#bm~&n6k;Gg7eyL2GH_+YK}*_ z>9E>pvD$EGINnAh1Hk~sK#i%=1FH>(PdvZE=P#EyR7`W%T!GtXa_rBiD5lI!o}62Z z%OPkCbY`FBhQqnF22U)MIkQq{rQrmOFe>rfz?3y2;rC2vxz^yOseEX$mZvBM27Bl(urYJx??LXry=es*wX8?ti!IipS znY8(g!W*}YPoa>qn9ZfBG@Pw7O&Z5vBHo1|BZ)Ks1F8jzA;M%f#gT~|^Mwp~+hRTc zNyvxD*=CR!LNj=(R9h}rD@`tzYLuGp20;uBgM9rMGoC=h^*FO!8GX)M5v+L=&!Wd@MSPT&+(m@)jqmx-Kl^dL0u2AWY z{WhlEC3YZj(C)tJdR!@cTq!sC0>L?S$`sOOkTAkBg5k1jQcN^qnKm)EMaTsORt zaS(rdH1mZlmT7Ea09~y%75$Wf@O!T)u0OOkHvu9%26_?Yc<(p`I5?5RGL5!DBaCQA zF%AN;wt)mrJ7H>p2Ir$kr}Nx4I}t>P9RcN?dKt^)mgzk6`7|dN%Umke1N+F(2E=WE zuC9Gm8m`iCI>x9FoOga58^L$?6aT2si=Nlv(Pa_KG}vFv#hZ z!ebo*=_V9yCs>%wq~gasNcirg4iOqzA*7&g8hpw!xNH9uckiECC;o9jr;B3B;w1;B zxP7kJb8E5-A6vqK?a-NB7_58x$c8T8ZOj`WmSKPFXzZh{FZ(ptubjY2t;NN)YOK%0 zO3UM;muoz=+M*p5D-jq1O<#Frxyj#Osl^8e1FK53kdvDjfFXimNK@gHQ4`9iEDlX(duI_+xE~YfSr}X1$5l(t2r`zzJqM;aK2ul^ zkT7mg0tw@`xgvM%o5W6X%+%34c7-`;bl)S3_aWntozj>Hf4y+9QJq~XQ*Ju3OrYZU zeDZ3Ik6o^FvFfhhx-?`Xv+nxu1P*g&%MKs9SmSdGbsApGElq*tYJ>C3<-X$`9hCjW z9N8YbN6**zWZpKLt)8REO)9{zy)?(ad$@`!e{UMH3QYBOWr%RoRDmb1tZpPjjMU-d zjtP6}4XP4AU$wwC4emNH#gWNeB2;(V0=iEDI)5kehrf9`kFUy8*VXdE?hrfsZ@B^2e{4 z+EjN-%E@;9C)z)++4^4DU(7I(wpp!ri#9RDIEY;UNMs;fe-+Ls3=wXho8ZV~zW=_V zU>m|j%3v}bB!=iXi!?l+lH;@1^eH(Wp5I@kKR%tO;d-22>Zki%Z!iea4pm?X0@29g zmr6BqDT_PyO~qx&MF{*HE>&GF)m#kP(s28P#Mkyc0`0#M2!S#6smn_&R_c9@sK)>b zg^bNyA=9=$ZEaoQaaDaHY$#h8K+ZB)bNn?EK1vt^0P-oDgA+McYfUy=-bY~qFuF9* zE~CnU$sEUKio}5u5dKXCo7*Q-%wkQjdsCz!w>K-T8T3oC;k!i0XSUe|Y zi>%ch7OLH$a(B@fI24RZ@QDkJmGIX~sB&hpglQO@n45@Y0?_r%Y1_lP+LrbI#nh)w z{8yGazg!w|en~;uSIm)3MF#*;)sLnvvs4esT)ag#ddIz4QU-Xwenf-3fm>8WQ-qsm z3YZ8q=_zQ1iOSlT`qk6&8kxy988k{JJYHVlI!(}g(ZR#Es!%k}ufS50oMA6BE~ zDlJbR(I7^^R@AC;Xd=f{F2yEJyk((Bz*v%bM8nbPJd>GJG)DxE4~3icEogaIZc z{LNdp=PS=HuJNS{i9Tg%l3Uwr;5=a)-6M*L_H{9|7+8%I-58}LXJe+Xp& zP0#0RZpkmI>W3rxC7-gmX{r#aa76skQ!bfBcm3Cd(m>X>I5L@!J(@X`vv}Qs9MfrI zD5JL$#Fu;hfh=>Gn9xfG9G&Vpj~+<~&;*SPC{>I!mQjg*kR4wKBm5qesVbM&s(kL; zHO?%RXnB4j!)_FGjDU5=<9Z{Km-hv+AEF`v<;Mnn7HthDT7z-&oYr3 zj`+KWKASQ5ihbG0K$EILMCI9hIswBYIbDzQC;5yAuehc7fV=%LJAUZnjrWzN!xNX6 z`0R66cy6ggM=utU{--<=lv54bIlZvTXU<;c=_|`LT4C1y?N#k;D-i#~q7ApFUqbk+ zOQk07dG$nS8&*`~zA6e|M0ptD^2n{z8T;r8=7vHST4D;3%AN)OK`$ zs+!=@)-t{ZBKu0MNj|m4Y$3~RKEq@tMcOj4Ok;DX?dY{oo2Er(w0HP z^)|nVZFM_g%!_kmqVKA=jfB4mB#4<;X*yJz4$m)_NSP)X+a%Rah---8`AW<4Xt^E@ z$HQ^G_T{fmJ7HaJGx4{_e{`luswWDvZZ7hdh*2_Qnu*Lb|2r=$(DZzK%4QXA zLv%krQ)D7-$BTJ`ERdlSc_#|mBvQVmVaizlfa1lL0Rz~f#W7ac!av5rP?3*g-it^QB6SCheZ6xyGF@nA{|N_Q^`UCZd#2^Zk13V`%;zmqrqKl6uh0rBBO;ub zp9nGs^%KKFxpn+RlQRM#Yg?fzgaSF+pkN!@@{LqsGG#_fA3~6^rhB+!+>pmJ3ATyY zdm2|w*i;|m-k@s7AO`2qt~I*a4u8ayZE^F=M31jr*Zfpl0)J9WTdk%S9}78_0sO*U z+0OU+Fz`s|{M*LBgOm9PMD!Ee5{hhnK!h#nkh}ZKn8G&EKS2AQy~MoljyTfuvt5#V zVyz(JMOVM3VUV#c@@boiY?{ezknuKaTY=H6^PM(bXc}av?;UiK$HR&H;f-(owQnE0 zK>Wc;SB}|2WF8B&rWOv{tS=K^e4HLd2OxX_HC~(1Nfbn7x@0qR{;LhGUVR~+_VKB zsvMmvu&(v0B%uIJO=-(eR(+x`<1 z1#UYq&5`Mez+lmTi zL7_fP_hU-CXS5N+;KckC8QTm``xX4}uUf8uGEyDxfkdU=^+1uFW%`bLl9%pphsiOf6 zA|v0rZ;ChFb&MAuo+D#NXOkJRD-02i&Q9>)i*Dxr;|Iv3tccTq0p~x=aj_;S)B9=< z9M_1zt@Bes)}IJWsq!ID`OF~Mj}kKg*Y`+$)AlHgq3?&62&ye7hyxc9pZ!wJ8*&R0_Njm^X zW{SMw&SM;!E+(ke-9Qq;@9v2{gul0U{SE}X|0FrvM!RXKOBF}?s40BZ6nOV*CKI*YY@7z1< z%LEE0OxrMJLEZvOKu|mt>OPblSaG4`g440h#ogiL=KzTD5<@|7=bERN;Phto!Al~wNvF<-R1KO4PL!JOUfAjL_i4AL4WpI zJ^T_-yG*(1aII4B8EaU{?Sw$=f90)*I67PGOH&Hu47e!^$Fp!C4aGL$Z-|Utr!NXM zAC_A1TpiBVVa16#1R|j8VkX6_PaNUn?m4cmRYu4~Fe2fvzurTH-v-kVZr?xMw$X%- zPvP$cJ}9F6!B-^T{b2wVpaZTdX~QV_s=u$Qjox@+0hdy4o+)y8ve4&4_OlIimFfF9 z*n-QAR^W>eLsO^LT6}JyLDScvNKXW6uJWmCb;&ZHx>Ii6tD&EF2J|!hp(H17Z+g0X8oRn-}9pccOi;qz%1S{1OI40ymlHU z``mw0g~?o+uQ+jpiEMh)I!Xp3>iL`M>^}tOtL+= zQ|4OT<0F@AoL*~1mlY^ATAW|0Y;-y{2x9^f1~r8f`=+_$z)as3=WO`eIe617983p< z?UQWi9XkO8g#W-KeDgjyk?XGx%unQa>CFeR5}jT5%rVx6*hkObW4eAX*p|s1`={4w zDC{c>e9#pBMo|7}0NuX>12X^!f`ST^ebxV}uohNERXIGFA}q^zQF>%M7D?4L<4 z2@WGQZhEzM3BNPWqtiw9Pvjy4egLZq|J6~-lc^*d?N%6C`TSR(DWi~1oASHBzX)wy zV4>3BZ=X6(x#|J9AsyVfuK|S_ zSb|Tj21deAdx^kGwZY##_AI4Zqiql9cOo>v%e`pei-AJg<_)(UVY-vln5fzB?#za~8g4 zHZbySCu#T!pIw8`tP&}i3GnFiSNPnSi(AJsEa7i@eSHwW0QcRrj~5-7*?I!L0hfK% zZxP|M?-`Q#VMrCA1D+3SP49EQ>btwg*!y$G{;A-)@X&eWhf@uz%@&VcSY)-3E;l5gpq`VgUc` zfg%%W(+BxoA5Qi1ULTTU?c_rfw9jpc}7)#jkX_1?~ku=Xfnrb z`$ijVHwuz{+n_+H(d3KIU*ox@5}uzt6?7612^^44FRbyU3ky^`M!&IS+Z}dLaMRo* zX*+uNzdZ-HWOtT4l)#zj<&yz32pzh>O>Me)IFD~+>^Ve9e zHUjxz1V#{q9#k>hdtgoF| z@vi4{tz75f^9y|L+*PiYs-p$`=*J+!7)I2;z)T^_L@pgELf)K(`PA-_O4@+C3egP6 zHVqC=^|>doWw*ah3BL^~%jBNJbL&ZMy3>?&NdQ+B-eCx9{nMBvgRz9kea~yB_`c7T z@qPV-7`)36c}rNiPUN5$9hl~k3yWj^WZLyl%*lZS;h&#dT;t+ug{fScxk8rdT!wtw z#x@O1*>DX~6yH}I&!^ICv0MwnUai$zL6DDtGmLd6{ihnw1ZE34?1+m#w!kg9z{h`^ zq9gPlK5#e_r~^w*_-C3c=CDiyw>QYkb!O2|3b*Z_3L^cqnOcN&^e)HuAF>SLkB4yj z34^i4$^N}p6!}-5E&HncHo{i}FApmx0(T#p;aa)QrPazd5l@F8;u(SCc`TG_ER<{Q zpqD0T%L?m+5X$}eX0$gDj^O_5>Z*B z%0(53EI2l>O@n+yym%$>>(6b2!_S~#5-_4CLr|eW+BV6hY|6DpJMG*H6S66bdk)Q! zv8+(yZwmx#3co1G1%J$6ZY(x{4*br`3wWMS)A9cT^!M6%M7C&YzQTct94|RM*A}<8 z8@fgir!q>!&k)@tKdOxS8D^S_Z5ly-gb0}Gy4bU;Ai-Fo>;RTwkW1Uc z`l>&p$R*bw)~!4qwuJ$7j1&da_^_(}I;!5Lj4^F?r>f*r7O%KzAH~crTc)Dagn`F2 zCUG9ZnLsSV(Dv=cnB*xthR^;G%Zs)#k;|@vLLp=G(xdZ1fX}X@nx4<;3HrZvlTERnAvszL@3yo90Pbdu4g{e2S52c^mZo2=M$M zK}r;Twi}e;qgc*w;fIR-W@Xv4hr4OU{ndAPy3_%XyByeM_dq5pzgCP-pRcnK+_Kx zQq&iByzLQtcMA*=UUYCah{>qYOGn{RRDWERm8NblJ!~rj_|5xsR9Y?>%X|{$EeeZK zWkukwgEQQ|e|oQ|s4);DA?zk`2{^7t(+v+nrWyq9+ynF2RhG={|9 z1tEy*fBU}Z_1#`Ri>mq)BJz|E{GZot-|i1Ppc4M?*UwOHxh&S4zw%Xo88!~udyRvZ zA>4O#j$^aQvYPB2=w%4oB`NfWmgiAxhQ}!sfn_Jyd3PIB11F%Up|m_dxK;TAfg{sJ z?mjddIG7Qs2(|?MrMxMBCBXYfyy=$&c0}oZ_p2wF$yq+iZ>jR`E}PY+P1M&QdBn?a z+QPH1{3dM?PhT zF6H%4t@`)A>d&-%t&9@kXHeKtvwPiDJYS#leElJXrz7#EFq=>Fs^bTl%U|7UU8L8-uTKZLNa(dq7tiVUiQs6!#z7&vY)` z!=tt9R9pdl3iuC#oZIO+?nqxu#i!iv+z*TIP#uRMN`eEJ3x zND7|kaeiqn^0?1ac(@dt2<$Wy@PS9luo~631iOE!-nc&Xe}!Dy=DwTuv#*$IpY?Yu zx|=SUtSR#2ceJkkttrBLc1Z8nRCTFiL$KvYoN<1-+c}QGq!9%OTF=W&+A;~Tb- zFSBg}@KAnF;HM3!|H(Go@EZhn-TD51ylMj5G}dfG-lD4igD~Ha^~9rac&fmwZaws6 zdO9!w2&`2bJayq(q-(GcSmHmj3{O=zo{f1JEVkh9mf%7&x_uG2u(Zax#ntO;_g5%p zQ@s4x0S-*$*RA|pd_IDIeBciZ;U^GE+i|^jP}p?_0PwB{iqt(_aeVzTRejIKEr~Yf zI#D|baCvYd$E$BS*!B~+!382g0x96a@>(QK2~hFi!%IQtp4OO-2Rm&%TZ6w`fb)%b z;;%GXJbLa5&h>QmuR(U>mmfQ@VepH--KW624fH>HzOKA)7sdW=*i)?byFOJyRri~Q z{HDOSZdtWm$Q2P5D-Aw(?kd+xyL6^?J%B2+#XJw*bAri;NWl=SP7}B_7sTz~pB^r7 zXy-GOTzIq`{PgoV@O+<7pL(9BFD~phwg!T^LY9{v-N#fevo4{A;X{Le-!H-ssB-oF zdtm49z}||u@J(;#P%&HeeEo!o+$3-pF>5EEw%I?Cqvp7j>a77f`Z59$>P?4w%i+-U z1a`!g>&`-5a^Oroa2HM)kTrt99i6mkVG+|h_xe2zj`tp~z~8R~4o6YRKsP{n^1=d- zKYw-8-T1u>0vw(ya{o>Hn8>Eri9ZZC?VbPqpg-gzSEe({7k}@Uc6$n&=CY^Y@c(y? z2XC9kSKTMbuZZwn(xa+$FB3s%xGs-eSme~T)zRYfzkor2I}XkBz->nZpY#|sbzWtR zAb-H2bP&LE!VaEw@+A5is0Zga96aA3@6xcKYsJ zus4_Q36=1Yny6cO01Ewtdr>hS5)AaRq)~(YFXJzyEd9yFL6+*wbkL zu1~IE2$Lf6a|&+;>`euF@b3f9EtP^CeGO-i&u(`>fLjmD^1!V}$YoLk*-$m6&BXmn z5Vh#33)lGK*^9IsZ|{F64b;uM4$X3OW&(r02T%_1+otf7s;un=Ykw5%b=31OKT`rI zcs_3h{=Gn^-PNA^`j?hVeCfH%TwS|9F8Y`v+%!AE ziw@4V19)zJ--|AR72xL$;kSUsUODQEgX@9;0Q~b$t)XD5^6et>c7>yz2}B*gxW00F zVU;H?Em6Nt^voRv1;Y>yOcl86$bJsZOpvn7b<_2zAOgOxELCbeeQ|-a*OqBH?%sFw zQOKk$?l>^R@!84sC_a7QyugoTEqR~o*{1@P! zB=xS9Ydm^!k!z*e4d=lyP{lG$_DvQzabT81GZW;}DNNZ2Sdj!e8I~1jIvxw9D$ibB z;=*bHEW%9g#hp+UGI^XLGzUAXf%x5#y z9QR%VCh*42yU-7%!MdwEF3((D;ndaD;K=E^oB0laDu!WD%%(XoRph`_k(ok{LMDZ6 znd`@Yo2t+b;qCj>nhq<~I@d~7F0YhWsni0`zUzSSQAk+^$LFUwv2U7;Z6;xS3VcM+ zAM$-3Gfd@AUVHrzKU}9|@SP8>p#rKLF@&EI|?8xwKm4u}h0AR~x%MjlB!) zl`n=t+P0X;rkTj5$)!`+rh#D?Z9f3T^@8ZVl}3}bT9bOKwQ-gAdLaBZOyx4%b#Rvb zllj=Fy&DE!k;uA+*8$X5ez2VNTFS&yHbugcX@jVG=wv2epS@MQ-bUH|s| z)9fqekg$A;;fvy75&o0F2UXSoU$4J@iNAw~>mI)8qf5+WQ`8;r4#SY26ZjTE)}4kl z;!BDM4aeoo;ua2|TWgF-HCam)NP$7Ux;cS3dd-vJ!Z{|&s=S2<~k@+a3h z@xu#fxqs8gu8}j-C^}(?e6I*U0vt?Qf4SD=nJX(?TrFQehN~Np98|F_lY^55PVAdz zGM8>YkFC`C<&uc}iY2_Oqc=XdnMh=B^f+N#K zvbNRM^EZFM@I63%;L`#>pR{`Pc1Rg^6tg z0@NIr^DAYZTP(3!Zv;!^1&_Jw4BCW0nM-ri%p`}V3S?8(`a6xPv>7e|zhjF0Ue)oh z+*C06-3P7@m)>FF%VrsW`zKaW)Bt%vM1BnT1|S_*r#qDc1m1)fS1UZXxW-DYiSPS2 zjD@=mZSTd&T$*Du6C9i>kg=^0qSv@4nj-wA0q;l|@;O)WUue316nt5&-mm-M0(1E^ zuJ0Fp<)0w(!vZe?B&i>O01e0Ga;e7oykdydu zM_q4K;7Q<}rtn@LDW519{PO)TJY7EuzATsRKl=D8|LfspzU^f*w+MVMBL4z799KuX z{wE>`;CMdEwFVcK%Umv1sWqJ-O3V$ZV0%CV+-FlZ2PX3zo+>bv%U~JCy76vv`Pd*P z7ex3U0{`t_-aB>n4<9e_zSq96#r+68eEGt+ePR^@4272f{|1q7Rn=(`>Do!6E*t}G z*J4$uG+SI+t8!_z!fLG%NCog^j^Qyt8}*iDFqKVnaH_z8i9ETq9awg{g6=1j;#Pn^ zF@@huo5rKAs((G)+uts@u{?qA`qUZ%sg|d&QTTVj*8;h%CHtex@qAWl4KA-$xmv1H zZnSRL3WkBUZePfx*jLPPU?R_CE{$z=LPP4H0ZgjE2Mm$lFocJI)^2Cz*$Z%ERkq>l zKeR|8ZQ(0?+t7yr94*>5n3_kArQnL-0_q}dzw`aYl;LCL7 z^@s2O^fK34f+b`Xc@4t%pnNT`=>|wtII^t|W&I9))Ad-ZH(9FGS*kWDHJUUXuPq($ z!et0;N;V9Gv}IDvrkKfPnaO9E$fikKW}E0qlq4hq_bb5Pg1*ZV9;&*2{iV}s-hN+k zw`ad6;RU+V#s}a1*(GW%3)3Lo^7Q4vw_k;_nU}|WwqX9xl(7P)}Y#QXt^GquN&TL*V~us7*RzqL`YjE`LxYsF2hVd!(=8+CS_q6 zM!R@_D_+cNU*LJ*&qerSTjcXiUz@iV)BMW)Ic`8STyM*2SK-?~v5Fy>j?b;Yw*cP; z+^dipwe8^KL2-SbhT~CbwpgpTSgSWFH=5KP7uWN8*74PU@0YPve^A)%<#S!aw?DLkX(+B| z%qzUfSH2B+H9*07luCMsM-{tcj z+Uk~HqX!q#Mq7iZ zX(5Qo=Xf4X*Q4op)E$?m>$ac!ZRQY24ywVA->$zY`#KT?{glAB!8D9@BDYNgyRElp zQWj~;B4wGR%;3E#27x3$c4F^_vcQ)V{?Ziw#t?bhQ<^Em<-M=HVb~iMZZLo`!9V%? z75@0OQ+(Se)+QBR2K)ns2Wz~|@m2FiLyDoMl7rdb&4I4Wd!s~cu9h~vx4EsNxkb|9 z`^i>CN4ODLFzKR2DgCdYD*;m*>%$2;#%=tz2W^!LNgyv_E97rgB;T=riolSy`vX}cjD z-y$h>OvYg=Ym8Wh%#S4;^2QH3frqwb|629Fj`9A{``iW1jBgbFEbiA$ID3Af_i9Og z%QdUMGp%Q;WcX5+(B~^TA->=UpX0MTu8orqq&N6WA6h)^#L@Ft)TP&-)BWgfUh+$} zS(+z7{Gohpj-;?F@8%2VN-Nss4O*)ktxb0`ey{qlZu#{GY|J;DKj_OxC0{%jT%uKW`tr zb~f?XO*W_R96V*-2UHt3AIM~4zNaa8E>$P;-XwD^AAdQg}ge<)vH z!+8Hha>nHkQ}-OwDd4bJb8tUg(5bKl=RFYLfsmOtMi zC12>pE8fP?1jZds0#2_taZKkZ`{~W56X~>dUFQCTu*&n<&&s7+%@x>yN#wPJ-Np+K zOp7!3y?yN6rk}>9p%o;uaK$RuP%b5(tb%$)_8Y5~s3dT0+xC`MNMPsU2OSrbe(Yvj zvtW&42ymzb?J=cUteiy9YV$8jD|g9hA&lFZ!H;*!MN0xWurA(kleWq$?gVeoYIb6Mw<&;$TV{;&H0 literal 0 HcmV?d00001 diff --git a/docs/docs/assets/images/logo_dark.svg b/docs/docs/assets/images/logo_dark.svg new file mode 100644 index 00000000..0be819d0 --- /dev/null +++ b/docs/docs/assets/images/logo_dark.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + Logo + + Main circle + + + Inner circles + + + + + + + + + + + Text + + easy + + + diffraction + + + + \ No newline at end of file diff --git a/docs/docs/assets/images/logo_light.svg b/docs/docs/assets/images/logo_light.svg new file mode 100644 index 00000000..84103655 --- /dev/null +++ b/docs/docs/assets/images/logo_light.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + Logo + + Main circle + + + Inner circles + + + + + + + + + + + Text + + easy + + + diffraction + + + + \ No newline at end of file diff --git a/docs/assets/images/user-guide/data-acquisition_2d-raw-data.jpg b/docs/docs/assets/images/user-guide/data-acquisition_2d-raw-data.jpg similarity index 100% rename from docs/assets/images/user-guide/data-acquisition_2d-raw-data.jpg rename to docs/docs/assets/images/user-guide/data-acquisition_2d-raw-data.jpg diff --git a/docs/assets/images/user-guide/data-acquisition_instrument.png b/docs/docs/assets/images/user-guide/data-acquisition_instrument.png similarity index 100% rename from docs/assets/images/user-guide/data-acquisition_instrument.png rename to docs/docs/assets/images/user-guide/data-acquisition_instrument.png diff --git a/docs/assets/images/user-guide/data-analysis_model.png b/docs/docs/assets/images/user-guide/data-analysis_model.png similarity index 100% rename from docs/assets/images/user-guide/data-analysis_model.png rename to docs/docs/assets/images/user-guide/data-analysis_model.png diff --git a/docs/assets/images/user-guide/data-analysis_refinement.png b/docs/docs/assets/images/user-guide/data-analysis_refinement.png similarity index 100% rename from docs/assets/images/user-guide/data-analysis_refinement.png rename to docs/docs/assets/images/user-guide/data-analysis_refinement.png diff --git a/docs/assets/images/user-guide/data-reduction_1d-pattern.png b/docs/docs/assets/images/user-guide/data-reduction_1d-pattern.png similarity index 100% rename from docs/assets/images/user-guide/data-reduction_1d-pattern.png rename to docs/docs/assets/images/user-guide/data-reduction_1d-pattern.png diff --git a/docs/docs/assets/javascripts/extra.js b/docs/docs/assets/javascripts/extra.js new file mode 100644 index 00000000..9596ee07 --- /dev/null +++ b/docs/docs/assets/javascripts/extra.js @@ -0,0 +1,27 @@ +;(function () { + 'use strict' + + // Variables + const header = document.getElementsByTagName('header')[0] + + console.log(window.pageYOffset) + + // Hide-show header shadow + function toggleHeaderShadow() { + if (window.pageYOffset <= 0) { + header.classList.remove('md-header--shadow') + } else { + header.classList.add('md-header--shadow') + } + } + + // Onload + window.onload = function () { + toggleHeaderShadow() + } + + // Onscroll + window.onscroll = function () { + toggleHeaderShadow() + } +})() diff --git a/docs/docs/assets/javascripts/mathjax.js b/docs/docs/assets/javascripts/mathjax.js new file mode 100644 index 00000000..333524ec --- /dev/null +++ b/docs/docs/assets/javascripts/mathjax.js @@ -0,0 +1,33 @@ +window.MathJax = { + tex: { + //inlineMath: [['\\(', '\\)']], + //displayMath: [['\\[', '\\]']], + // Add support for $...$ and \(...\) delimiters + inlineMath: [ + ['$', '$'], + ['\\(', '\\)'], + ], + // Add support for $$...$$ and \[...]\ delimiters + displayMath: [ + ['$$', '$$'], + ['\\[', '\\]'], + ], + processEscapes: true, + processEnvironments: true, + }, + options: { + //ignoreHtmlClass: ".*|", + //processHtmlClass: "arithmatex" + // Skip code blocks only + skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'], + // Only ignore explicit opt-out + ignoreHtmlClass: 'no-mathjax|tex2jax_ignore', + }, +} + +document$.subscribe(() => { + MathJax.startup.output.clearCache() + MathJax.typesetClear() + MathJax.texReset() + MathJax.typesetPromise() +}) diff --git a/docs/docs/assets/stylesheets/extra.css b/docs/docs/assets/stylesheets/extra.css new file mode 100644 index 00000000..a625be80 --- /dev/null +++ b/docs/docs/assets/stylesheets/extra.css @@ -0,0 +1,359 @@ +/*************/ +/* Variables */ +/*************/ + +:root { + --sz-link-color--lightmode: #0184c7; + --sz-hovered-link-color--lightmode: #37bdf9; + --sz-body-background-color--lightmode: #fafafa; + --sz-body-text-color--lightmode: #525252; + --sz-body-heading-color--lightmode: #434343; + --sz-code-background-color--lightmode: #ececec; + + --sz-link-color--darkmode: #37bdf9; + --sz-hovered-link-color--darkmode: #2890c0; + --sz-body-background-color--darkmode: #262626; + --sz-body-text-color--darkmode: #a3a3a3; + --sz-body-heading-color--darkmode: #e5e5e5; + --sz-code-background-color--darkmode: #212121; +} + +/****************/ +/* Color styles */ +/****************/ + +/* Default styles https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/stylesheets/main/_typeset.scss */ + +/* Light mode */ + +/* Default light mode https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/stylesheets/main/_colors.scss */ + +[data-md-color-scheme="default"] { + + /* Primary color shades */ + --md-primary-fg-color: var(--sz-body-background-color--lightmode); /* Navigation background */ + --md-primary-bg-color: var(--sz-body-text-color--lightmode); /* E.g., Header title and icons */ + --md-primary-bg-color--light: var(--sz-body-text-color--lightmode); /* E.g., Header search */ + + /* Accent color shades */ + --md-accent-fg-color: var(--sz-hovered-link-color--lightmode); /* E.g., Hovered `a` elements & copy icon in code */ + + /* Default color shades */ + --md-default-fg-color: var(--sz-body-text-color--lightmode); + --md-default-fg-color--light: var(--sz-body-heading-color--lightmode); /* E.g., `h1` color & TOC viewed items & `$` in code */ + --md-default-fg-color--lighter: var(--sz-body-text-color--lightmode); /* E.g., `¶` sign near `h1-h8` */ + --md-default-fg-color--lightest: var(--sz-body-text-color--lightmode); /* E.g., Copy icon in code */ + --md-default-bg-color: var(--sz-body-background-color--lightmode); + + /* Code color shades */ + --md-code-bg-color: var(--sz-code-background-color--lightmode); + + /* Typeset color shades */ + --md-typeset-color: var(--sz-body-text-color--lightmode); + + /* Typeset `a` color shades */ + --md-typeset-a-color: var(--sz-link-color--lightmode); + + /* Footer color shades */ + --md-footer-fg-color: var(--sz-body-text-color--lightmode); /* E.g., Next -> */ + --md-footer-fg-color--light: var(--sz-body-text-color--lightmode); /* E.g., © 2022 EasyDiffraction, Material for MkDocs */ + --md-footer-fg-color--lighter: var(--sz-body-text-color--lightmode); /* E.g. Made with */ + --md-footer-bg-color: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., Next -> */ + --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., © 2022 EasyDiffraction */ + + /* Custom colors */ + --sz-red-color: #F44336; + --sz-blue-color: #03A9F4; + --sz-green-color: #4CAF50; + --sz-orange-color: #FF9800; + + /* Logo display */ + --md-footer-logo-dark-mode: none; + --md-footer-logo-light-mode: block; +} + +/* Dark mode */ + +/* Default dark mode: https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/stylesheets/palette/_scheme.scss */ + +[data-md-color-scheme="slate"] { + + /* Primary color shades */ + --md-primary-fg-color: var(--sz-body-background-color--darkmode); /* Navigation background */ + --md-primary-bg-color: var(--sz-body-text-color--darkmode); /* E.g., Header title and icons */ + --md-primary-bg-color--light: var(--sz-body-text-color--darkmode); /* E.g., Header search */ + + /* Accent color shades */ + --md-accent-fg-color: var(--sz-hovered-link-color--darkmode); /* E.g., Hovered `a` elements & copy icon in code */ + + /* Default color shades */ + --md-default-fg-color: var(--sz-body-text-color--darkmode); + --md-default-fg-color--light: var(--sz-body-heading-color--darkmode); /* E.g., `h1` color & TOC viewed items & `$` in code */ + --md-default-fg-color--lighter: var(--sz-body-text-color--darkmode); /* E.g., `¶` sign near `h1-h8` */ + --md-default-fg-color--lightest: var(--sz-body-text-color--darkmode); /* E.g., Copy icon in code */ + --md-default-bg-color: var(--sz-body-background-color--darkmode); + + /* Code color shades */ + --md-code-bg-color: var(--sz-code-background-color--darkmode); + + /* Typeset color shades */ + --md-typeset-color: var(--sz-body-text-color--darkmode); + + /* Typeset `a` color shades */ + --md-typeset-a-color: var(--sz-link-color--darkmode); + + /* Footer color shades */ + --md-footer-fg-color: var(--sz-body-text-color--darkmode); /* E.g., Next -> */ + --md-footer-fg-color--light: var(--sz-body-text-color--darkmode); /* E.g., © 2022 EasyDiffraction, Material for MkDocs */ + --md-footer-fg-color--lighter: var(--sz-body-text-color--darkmode); /* E.g. Made with */ + --md-footer-bg-color: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., Next -> */ + --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., © 2022 EasyDiffraction */ + + /* Custom colors */ + --sz-red-color: #EF9A9A; + --sz-blue-color: #81D4FA; + --sz-green-color: #A5D6A7; + --sz-orange-color: #FFCC80; + + /* Logo display */ + --md-footer-logo-dark-mode: block; + --md-footer-logo-light-mode: none; +} + +/*****************/ +/* Custom styles */ +/*****************/ + +/* Logo */ + +#logo_light_mode { + display: var(--md-footer-logo-light-mode); +} + +#logo_dark_mode { + display: var(--md-footer-logo-dark-mode); +} + +/* Customize default styles of MkDocs Material */ + +/* Hide navigation title */ +label.md-nav__title[for="__drawer"] { + height: 0; +} + +/* Hide site title (first topic) while keeping page title and version selector */ +.md-header__topic:first-child .md-ellipsis { + display: none; +} + +/* Increase logo size */ +.md-logo :is(img, svg) { + height: 1.8rem !important; +} + +/* Hide GH repo with counts (top right page corner) */ +.md-header__source { + display: none; +} + +/* Hide GH repo with counts (navigation bar in mobile view) */ +.md-nav__source { + display: none; +} + +/* Ensure all horizontal lines in the navigation list are removed or hidden */ +.md-nav__item { + /* Removes any border starting from the second level */ + border: none !important; + /* Modifies the background color to hide the first horizontal line */ + background-color: var(--md-default-bg-color); +} + +/* Increase TOC (on the right) width */ +.md-nav--secondary { + margin-left: -10px; + margin-right: -4px; +} + +/* */ +.md-nav__item > .md-nav__link { + padding-left: 0.5em; /* Default */ +} + +/* Change line height of the tabel cells */ +.md-typeset td, +.md-typeset th { + line-height: 1.25 !important; +} + +/* Change vertical alignment of the icon inside the tabel cells */ +.md-typeset td .twemoji { + vertical-align: sub !important; +} + +/* Change the width of the primary sidebar */ +/* +.md-sidebar--primary { + width: 240px; +} +*/ + +/* Change the overall width of the page */ +.md-grid { + max-width: 1280px; +} + +/* Needed for mkdocs-jupyter to show download and other buttons on top of the notebook */ +.md-content__button { + position: relative !important; +} + +/* Background color of the search input field */ +.md-search__input { + background-color: var(--md-code-bg-color) !important; +} + +/* Customize default style of mkdocs-jupyter plugin */ + +/* Set the width of the notebook to fill 100% and not reduce by the width of .md-content__button's +Adjust the margins and paddings to fit the defaults in MkDocs Material and do not crop the label in the header +*/ +.jupyter-wrapper { + width: 100% !important; + display: flex !important; +} + +.jp-Notebook { + padding: 0 !important; + margin-top: -3em !important; + + /* Ensure notebook content stretches across the page */ + width: 100% !important; + max-width: 100% !important; + + /* mkdocs-material + some notebook HTML end up as flex */ + align-items: stretch !important; +} + +.jp-Notebook .jp-Cell { + /* Key: flex children often need min-width: 0 to prevent weird shrink */ + width: 100% !important; + max-width: 100% !important; + min-width: 0 !important; + + /* Removes jupyter cell paddings */ + padding-left: 0 !important; +} + +/* Removes jupyter cell prefixes, like In[123]: */ +.prompt, +.jp-InputPrompt, +.jp-OutputPrompt { + display: none !important; +} + +/* Removes jupyter output cell padding to align with input cell text */ +.jp-RenderedText { + padding-left: 0.85em !important; +} + +/* Extra styling the panda dataframes, on top of the style included in the code */ +table.dataframe { + float: left; + margin-left: 0.75em !important; + margin-bottom: 0.5em !important; + font-size: var(--jp-code-font-size) !important; + color: var(--md-primary-bg-color) !important; + /* Allow table cell wrapping in MkDocs-Jupyter outputs */ + /* + table-layout: auto !important; + width: auto !important; + */ +} + +/* Allow wrap for the last column */ +/* +table.dataframe td:last-child, +table.dataframe th:last-child { + white-space: normal !important; + word-break: break-word !important; +} +*/ + +/* Custom styles for the CIF files */ + +.cif { + padding-left: 1em; + padding-right: 1em; + padding-top: 1px; + padding-bottom: 1px; + background-color: var(--md-code-bg-color); + font-size: small; +} +.red { + color: var(--sz-red-color); +} +.green { + color: var(--sz-green-color); +} +.blue { + color: var(--sz-blue-color); +} +.orange { + color: var(--sz-orange-color); +} +.grey { + color: grey; +} + +/**********/ +/* Labels */ +/**********/ + +.label-cif { + padding-top: 0.5ex; + padding-bottom: 0.5ex; + padding-left: 0.9ex; + padding-right: 0.9ex; + border-radius: 1ex; + color: var(--md-default-fg-color) !important; + background-color: var(--md-code-bg-color); +} + +p .label-cif, li .label-cif { + vertical-align: 5%; + font-size: 12px; +} + +.label-cif:hover { + color: white !important; +} + +.label-experiment { + padding-top: 0.25ex; + padding-bottom: 0.6ex; + padding-left: 0.9ex; + padding-right: 0.9ex; + border-radius: 1ex; + color: var(--md-default-fg-color) !important; + background-color: rgba(55, 189, 249, 0.1); +} + +p .label-experiment, li .label-experiment { + vertical-align: 5%; + font-size: 12px; +} + +h1 .label-experiment { + padding-top: 0.05ex; + padding-bottom: 0.4ex; + padding-left: 0.9ex; + padding-right: 0.9ex; + border-radius: 0.75ex; + color: var(--md-default-fg-color) !important; + background-color: rgba(55, 189, 249, 0.1); +} + +.label-experiment:hover { + color: white !important; +} diff --git a/docs/index.md b/docs/docs/index.md similarity index 100% rename from docs/index.md rename to docs/docs/index.md diff --git a/docs/installation-and-setup/index.md b/docs/docs/installation-and-setup/index.md similarity index 100% rename from docs/installation-and-setup/index.md rename to docs/docs/installation-and-setup/index.md diff --git a/docs/introduction/index.md b/docs/docs/introduction/index.md similarity index 100% rename from docs/introduction/index.md rename to docs/docs/introduction/index.md diff --git a/tutorials/ed-1.py b/docs/docs/tutorials/ed-1.py similarity index 100% rename from tutorials/ed-1.py rename to docs/docs/tutorials/ed-1.py diff --git a/tutorials/ed-10.py b/docs/docs/tutorials/ed-10.py similarity index 100% rename from tutorials/ed-10.py rename to docs/docs/tutorials/ed-10.py diff --git a/tutorials/ed-11.py b/docs/docs/tutorials/ed-11.py similarity index 100% rename from tutorials/ed-11.py rename to docs/docs/tutorials/ed-11.py diff --git a/tutorials/ed-12.py b/docs/docs/tutorials/ed-12.py similarity index 100% rename from tutorials/ed-12.py rename to docs/docs/tutorials/ed-12.py diff --git a/tutorials/ed-13.py b/docs/docs/tutorials/ed-13.py similarity index 100% rename from tutorials/ed-13.py rename to docs/docs/tutorials/ed-13.py diff --git a/tutorials/ed-14.py b/docs/docs/tutorials/ed-14.py similarity index 100% rename from tutorials/ed-14.py rename to docs/docs/tutorials/ed-14.py diff --git a/tutorials/ed-15.py b/docs/docs/tutorials/ed-15.py similarity index 100% rename from tutorials/ed-15.py rename to docs/docs/tutorials/ed-15.py diff --git a/tutorials/ed-16.py b/docs/docs/tutorials/ed-16.py similarity index 100% rename from tutorials/ed-16.py rename to docs/docs/tutorials/ed-16.py diff --git a/tutorials/ed-2.py b/docs/docs/tutorials/ed-2.py similarity index 100% rename from tutorials/ed-2.py rename to docs/docs/tutorials/ed-2.py diff --git a/tutorials/ed-3.py b/docs/docs/tutorials/ed-3.py similarity index 100% rename from tutorials/ed-3.py rename to docs/docs/tutorials/ed-3.py diff --git a/tutorials/ed-4.py b/docs/docs/tutorials/ed-4.py similarity index 100% rename from tutorials/ed-4.py rename to docs/docs/tutorials/ed-4.py diff --git a/tutorials/ed-5.py b/docs/docs/tutorials/ed-5.py similarity index 100% rename from tutorials/ed-5.py rename to docs/docs/tutorials/ed-5.py diff --git a/tutorials/ed-6.py b/docs/docs/tutorials/ed-6.py similarity index 100% rename from tutorials/ed-6.py rename to docs/docs/tutorials/ed-6.py diff --git a/tutorials/ed-7.py b/docs/docs/tutorials/ed-7.py similarity index 100% rename from tutorials/ed-7.py rename to docs/docs/tutorials/ed-7.py diff --git a/tutorials/ed-8.py b/docs/docs/tutorials/ed-8.py similarity index 100% rename from tutorials/ed-8.py rename to docs/docs/tutorials/ed-8.py diff --git a/tutorials/ed-9.py b/docs/docs/tutorials/ed-9.py similarity index 100% rename from tutorials/ed-9.py rename to docs/docs/tutorials/ed-9.py diff --git a/tutorials/index.json b/docs/docs/tutorials/index.json similarity index 100% rename from tutorials/index.json rename to docs/docs/tutorials/index.json diff --git a/docs/tutorials/index.md b/docs/docs/tutorials/index.md similarity index 100% rename from docs/tutorials/index.md rename to docs/docs/tutorials/index.md diff --git a/docs/user-guide/analysis-workflow/analysis.md b/docs/docs/user-guide/analysis-workflow/analysis.md similarity index 100% rename from docs/user-guide/analysis-workflow/analysis.md rename to docs/docs/user-guide/analysis-workflow/analysis.md diff --git a/docs/user-guide/analysis-workflow/experiment.md b/docs/docs/user-guide/analysis-workflow/experiment.md similarity index 100% rename from docs/user-guide/analysis-workflow/experiment.md rename to docs/docs/user-guide/analysis-workflow/experiment.md diff --git a/docs/user-guide/analysis-workflow/index.md b/docs/docs/user-guide/analysis-workflow/index.md similarity index 100% rename from docs/user-guide/analysis-workflow/index.md rename to docs/docs/user-guide/analysis-workflow/index.md diff --git a/docs/user-guide/analysis-workflow/model.md b/docs/docs/user-guide/analysis-workflow/model.md similarity index 100% rename from docs/user-guide/analysis-workflow/model.md rename to docs/docs/user-guide/analysis-workflow/model.md diff --git a/docs/user-guide/analysis-workflow/project.md b/docs/docs/user-guide/analysis-workflow/project.md similarity index 100% rename from docs/user-guide/analysis-workflow/project.md rename to docs/docs/user-guide/analysis-workflow/project.md diff --git a/docs/user-guide/analysis-workflow/summary.md b/docs/docs/user-guide/analysis-workflow/summary.md similarity index 100% rename from docs/user-guide/analysis-workflow/summary.md rename to docs/docs/user-guide/analysis-workflow/summary.md diff --git a/docs/user-guide/concept.md b/docs/docs/user-guide/concept.md similarity index 100% rename from docs/user-guide/concept.md rename to docs/docs/user-guide/concept.md diff --git a/docs/user-guide/data-format.md b/docs/docs/user-guide/data-format.md similarity index 100% rename from docs/user-guide/data-format.md rename to docs/docs/user-guide/data-format.md diff --git a/docs/user-guide/first-steps.md b/docs/docs/user-guide/first-steps.md similarity index 100% rename from docs/user-guide/first-steps.md rename to docs/docs/user-guide/first-steps.md diff --git a/docs/user-guide/glossary.md b/docs/docs/user-guide/glossary.md similarity index 100% rename from docs/user-guide/glossary.md rename to docs/docs/user-guide/glossary.md diff --git a/docs/user-guide/index.md b/docs/docs/user-guide/index.md similarity index 100% rename from docs/user-guide/index.md rename to docs/docs/user-guide/index.md diff --git a/docs/user-guide/parameters.md b/docs/docs/user-guide/parameters.md similarity index 100% rename from docs/user-guide/parameters.md rename to docs/docs/user-guide/parameters.md diff --git a/docs/user-guide/parameters/_diffrn_radiation.md b/docs/docs/user-guide/parameters/_diffrn_radiation.md similarity index 100% rename from docs/user-guide/parameters/_diffrn_radiation.md rename to docs/docs/user-guide/parameters/_diffrn_radiation.md diff --git a/docs/user-guide/parameters/_diffrn_radiation_wavelength.md b/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md similarity index 100% rename from docs/user-guide/parameters/_diffrn_radiation_wavelength.md rename to docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md diff --git a/docs/user-guide/parameters/_exptl_crystal.md b/docs/docs/user-guide/parameters/_exptl_crystal.md similarity index 100% rename from docs/user-guide/parameters/_exptl_crystal.md rename to docs/docs/user-guide/parameters/_exptl_crystal.md diff --git a/docs/user-guide/parameters/_extinction.md b/docs/docs/user-guide/parameters/_extinction.md similarity index 100% rename from docs/user-guide/parameters/_extinction.md rename to docs/docs/user-guide/parameters/_extinction.md diff --git a/docs/user-guide/parameters/_pd_calib.md b/docs/docs/user-guide/parameters/_pd_calib.md similarity index 100% rename from docs/user-guide/parameters/_pd_calib.md rename to docs/docs/user-guide/parameters/_pd_calib.md diff --git a/docs/user-guide/parameters/atom_site.md b/docs/docs/user-guide/parameters/atom_site.md similarity index 100% rename from docs/user-guide/parameters/atom_site.md rename to docs/docs/user-guide/parameters/atom_site.md diff --git a/docs/user-guide/parameters/background.md b/docs/docs/user-guide/parameters/background.md similarity index 100% rename from docs/user-guide/parameters/background.md rename to docs/docs/user-guide/parameters/background.md diff --git a/docs/user-guide/parameters/cell.md b/docs/docs/user-guide/parameters/cell.md similarity index 100% rename from docs/user-guide/parameters/cell.md rename to docs/docs/user-guide/parameters/cell.md diff --git a/docs/user-guide/parameters/expt_type.md b/docs/docs/user-guide/parameters/expt_type.md similarity index 100% rename from docs/user-guide/parameters/expt_type.md rename to docs/docs/user-guide/parameters/expt_type.md diff --git a/docs/user-guide/parameters/instrument.md b/docs/docs/user-guide/parameters/instrument.md similarity index 100% rename from docs/user-guide/parameters/instrument.md rename to docs/docs/user-guide/parameters/instrument.md diff --git a/docs/user-guide/parameters/linked_phases.md b/docs/docs/user-guide/parameters/linked_phases.md similarity index 100% rename from docs/user-guide/parameters/linked_phases.md rename to docs/docs/user-guide/parameters/linked_phases.md diff --git a/docs/user-guide/parameters/pd_meas.md b/docs/docs/user-guide/parameters/pd_meas.md similarity index 100% rename from docs/user-guide/parameters/pd_meas.md rename to docs/docs/user-guide/parameters/pd_meas.md diff --git a/docs/user-guide/parameters/peak.md b/docs/docs/user-guide/parameters/peak.md similarity index 100% rename from docs/user-guide/parameters/peak.md rename to docs/docs/user-guide/parameters/peak.md diff --git a/docs/user-guide/parameters/space_group.md b/docs/docs/user-guide/parameters/space_group.md similarity index 100% rename from docs/user-guide/parameters/space_group.md rename to docs/docs/user-guide/parameters/space_group.md diff --git a/docs/includes/abbreviations.md b/docs/includes/abbreviations.md new file mode 100644 index 00000000..682f16ff --- /dev/null +++ b/docs/includes/abbreviations.md @@ -0,0 +1,15 @@ + + +*[CIF]: Crystallographic Information File. +*[curl]: Command-line tool for transferring data with URLs. +*[GitHub]: A web-based platform for version control and collaboration. +*[Google Colab]: Cloud service that allows you to run Jupyter Notebooks in the cloud. +*[IUCr]: International Union of Crystallography. +*[Jupyter Notebook]: An open-source web application that allows you to create and share documents that contain live code, equations, visualizations, and narrative text. +*[JupyterLab]: Web-based interactive development environment for notebooks, code, and data. +*[pip]: Package installer for Python. +*[PyPI]: The Python Package Index is a repository of software for the Python programming language. +*[Conda]: Conda is a cross-platform, language-agnostic binary package manager. +*[Pixi]: A modern package manager for Windows, macOS, and Linux. + + diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 467bfec8..aef26c01 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -1,45 +1,167 @@ -########################### -# Override default settings -########################### - # Project information -site_name: '' #EasyDiffraction Library -site_url: https://easyscience.github.io/diffraction-lib/ #https://docs.easydiffraction.org/lib/ +site_name: EasyDiffraction Library +site_url: https://easyscience.github.io/diffraction-lib # Repository -repo_url: https://github.com/easyscience/diffraction-lib/ +repo_url: https://github.com/easyscience/diffraction-lib edit_uri: edit/develop/docs/ # Copyright -copyright: © 2026 EasyDiffraction +copyright: © 2021-2026 EasyDiffraction + +# Sets the theme and theme-specific configuration +theme: + name: material + custom_dir: overrides + features: + #- content.action.edit # Temporary disable edit button (until decided on which branch to use and where to host the notebooks) + #- content.action.view + - content.code.annotate + - content.code.copy # Auto generated button to copy a code block's content + - content.tooltips + - navigation.footer + - navigation.indexes + #- navigation.instant # Instant loading, but it causes issues with rendering equations + #- navigation.sections + - navigation.top # Back-to-top button + - navigation.tracking # Anchor tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + palette: + # Palette toggle for light mode + - media: '(prefers-color-scheme: light)' + scheme: default + primary: custom + toggle: + icon: fontawesome/solid/sun + name: Switch to dark mode + # Palette toggle for dark mode + - media: '(prefers-color-scheme: dark)' + scheme: slate + primary: custom + toggle: + icon: fontawesome/solid/moon + name: Switch to light mode + font: + text: Mulish + code: Roboto Mono + icon: + edit: material/file-edit-outline + favicon: assets/images/favicon.png + logo_dark_mode: assets/images/logo_dark.svg + logo_light_mode: assets/images/logo_light.svg -# Extra icons in the bottom right corner +# A set of key-value pairs, where the values can be any valid YAML +# construct, that will be passed to the template extra: - social: + generator: false # Disable `Made with Material for MkDocs` (bottom left) + social: # Extra icons in the bottom right corner + - icon: easyscience # File: overrides/.icons/easyscience.svg + link: https://easyscience.org + name: EasyScience Framework Webpage - icon: easydiffraction # File: overrides/.icons/easydiffraction.svg - link: https://easydiffraction.org + link: https://easyscience.github.io/diffraction name: EasyDiffraction Main Webpage - icon: app # File: overrides/.icons/app.svg - link: https://docs.easydiffraction.org/app/ + link: https://easyscience.github.io/diffraction-app name: EasyDiffraction Application Docs - icon: fontawesome/brands/github # Name as in Font Awesome link: https://github.com/easyscience/diffraction-lib name: EasyDiffraction Library Source Code on GitHub + # Set custom variables to be used in Markdown and HTML files + vars: + ci_branch: !ENV CI_BRANCH + github_repository: !ENV GITHUB_REPOSITORY + release_version: !ENV RELEASE_VERSION + docs_version: !ENV DOCS_VERSION + notebooks_dir: !ENV NOTEBOOKS_DIR + # Renders a version selector in the header + version: + provider: mike + +# Customization to be included by the theme +extra_css: + - assets/stylesheets/extra.css + +extra_javascript: + - assets/javascripts/extra.js + # MathJax for rendering mathematical expressions + - assets/javascripts/mathjax.js # Custom MathJax config to ensure compatibility with mkdocs-jupyter + - https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js # Official MathJax CDN + +# A list of extensions beyond the ones that MkDocs uses by default (meta, toc, tables, and fenced_code) +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - pymdownx.arithmatex: # rendering of equations and integrates with MathJax or KaTeX + generic: true + - pymdownx.blocks.caption + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + options: + custom_icons: + - docs/overrides/.icons + - pymdownx.highlight: # whether highlighting should be carried out during build time by Pygments + use_pygments: true + pygments_lang_class: true + - pymdownx.snippets: + auto_append: + - docs/includes/abbreviations.md + - pymdownx.superfences: # whether highlighting should be carried out during build time by Pygments + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: # enables content tabs + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - toc: + toc_depth: 3 -# Jupyter notebooks +# A list of plugins (with optional configuration settings) to use when building the site plugins: + - autorefs + - inline-svg + - markdownextradata # Plugin that injects the mkdocs.yml extra variables into the Markdown template + - mike # Plugin that makes it easy to deploy multiple versions of the docs - mkdocs-jupyter: - execute_ignore: - - '*.ipynb' # Do not execute any notebooks -# - 'quick*.ipynb' -# - 'basic*.ipynb' -# - 'advanced*.ipynb' -# - 'cryst*.ipynb' -# - 'pdf*.ipynb' + include: ['*.ipynb'] # Default: ['*.py', '*.ipynb'] + execute: false # Do not execute notebooks during build. They are expected to be pre-executed. + allow_errors: false + include_source: true + include_requirejs: true # Required for Plotly + #custom_mathjax_url: 'https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js' # See 'extra_javascript' above + ignore_h1_titles: true # Use titles defined in the nav section below + remove_tag_config: + remove_input_tags: + - hide-in-docs + - mkdocstrings: + handlers: + python: + paths: ['src'] # Change 'src' to your actual sources directory + options: + docstring_style: google + group_by_category: false + heading_level: 1 + show_root_heading: true + show_root_full_path: false + show_submodules: true + show_source: true + - search -################## -# Add new settings -################## +# Determines additional directories to watch when running mkdocs serve +watch: + - includes + - overrides + - ../src # Exclude files and folders from the global navigation not_in_nav: | diff --git a/docs/overrides/.icons/app.svg b/docs/overrides/.icons/app.svg new file mode 100644 index 00000000..b4fdd4f3 --- /dev/null +++ b/docs/overrides/.icons/app.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/docs/overrides/.icons/easydiffraction.svg b/docs/overrides/.icons/easydiffraction.svg new file mode 100644 index 00000000..5813e570 --- /dev/null +++ b/docs/overrides/.icons/easydiffraction.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/overrides/.icons/easyscience.svg b/docs/overrides/.icons/easyscience.svg new file mode 100644 index 00000000..fb514912 --- /dev/null +++ b/docs/overrides/.icons/easyscience.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/overrides/.icons/google-colab.svg b/docs/overrides/.icons/google-colab.svg new file mode 100644 index 00000000..9cd9d1b0 --- /dev/null +++ b/docs/overrides/.icons/google-colab.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 00000000..2e146827 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} + +{% block content %} + +{% if page.nb_url %} + {# Parse notebook path/URL #} + {% set parts = page.nb_url.split('/') %} + {% set tutorial_name = parts[-2] %} + {% set filename = parts[-1] %} + + {# Colab url #} + {% set base_colab_url = "https://colab.research.google.com/github/" %} + {% set colab_url = + base_colab_url ~ config.extra.vars.github_repository ~ + "/blob/gh-pages/" ~ config.extra.vars.docs_version ~ + "/tutorials/" ~ tutorial_name ~ "/" ~ filename + %} + + {# Download link: relative to the current page #} + {% set file_url = filename %} + + {# Open in Colab (absolute GitHub URL; works anywhere) #} + + {% include ".icons/google-colab.svg" %} + + + {# Download: use a RELATIVE link to the file next to this page #} + + {% include ".icons/material/download.svg" %} + +{% endif %} + +{{ super() }} +{% endblock content %} diff --git a/docs/overrides/partials/logo.html b/docs/overrides/partials/logo.html new file mode 100644 index 00000000..78fa69ca --- /dev/null +++ b/docs/overrides/partials/logo.html @@ -0,0 +1,15 @@ +{% if ( config.theme.logo_light_mode and config.theme.logo_dark_mode ) %} +logo +logo +{% elif config.theme.logo %} +logo +{% else %} {% set icon = config.theme.icon.logo or "material/library" %} {% +include ".icons/" ~ icon ~ ".svg" %} {% endif %} diff --git a/pixi.lock b/pixi.lock index 1685316c..4cd98911 100644 --- a/pixi.lock +++ b/pixi.lock @@ -14,8 +14,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda @@ -32,13 +32,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda @@ -53,16 +53,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -70,68 +70,76 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/94/0a/af49691938dfe175d71b8a929bd7e4ace2809c0c5134e28bc535660d5262/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d0/c1/8f69e69303554fb3a5ada668683690d68623dcfb1eb52067cce85c91012a/chardet-7.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/af/3b86dbd18d8dab5646f5b7c7db5bd9c43108e093864032aabd35d41b127d/diffpy_pdffit2-1.5.2.tar.gz - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fb/bc/60d93477b653eeb1ddf5f9ec34be689b79234d82dbdded269ac0252715b8/fonttools-4.62.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/8c/db8e79c4c744ebae1dcf25f7dbcc5d7df912cdbcdf7221e761479e8bd04b/gemmi-0.7.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -140,7 +148,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -164,10 +172,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -178,7 +186,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/28/83/36557b04cfdc317ed8a525c4993b23e43a8fbcddaddd78619112ca07138c/msgspec-0.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -202,12 +210,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -226,15 +234,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -242,18 +251,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/94/98/aa2d4b9d28969cc7f62409f9f9fc5b5a853af651255eba03e9bee8546dd9/scipp-25.12.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2e/75/5604f4d17ab607510d4702f156329194d8edfff7e29644ca9200b085e9a2/scipp-26.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -262,7 +272,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/53/01/1c0485ae02e645bc517bf5d5a6ca674f62c97e247890b954cbfe85c64dae/spglib-2.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -270,11 +282,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -287,7 +299,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/79/34/b104c413079874493eed7bf11838b47b697cf1f0ed7e9de374ea37b4e4e0/uv-0.10.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -309,14 +320,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.2-h14c5de8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.0-h19cb2f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.4-h991f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda @@ -325,12 +336,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.2-h11316ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.52.0-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libuv-1.51.0-h58003a5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.0-h0d3cbff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.1-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.7.0-hf6efa0e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.1-hb6871ef_1.conda @@ -345,17 +356,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -363,68 +374,76 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/54/2f/ca2f6d868e450eb37c8b91903f6741024caa2c884418e6a26c96df34751d/chardet-7.3.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/19/eb/bb3ff420acdaf9bcaf94c510f42df11974bc3fc475ef50d619366f33fda3/diffpy_pdffit2-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c1/dc/c409c8ceec0d3119e9ab0b7b1a2e3c76d1f4d66e4a9db5c59e6b7652e7df/fonttools-4.62.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/80/fd758344a72ca7b5e1c5bbdc1d263f3b215d3897941b5f450380445ca0a9/gemmi-0.7.5-cp313-cp313-macosx_10_14_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -433,7 +452,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -457,10 +476,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -471,7 +490,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/8a/d1/b902d38b6e5ba3bdddbec469bba388d647f960aeed7b5b3623a8debe8a76/msgspec-0.20.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -495,12 +514,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -519,15 +538,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -535,18 +555,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/22/75e119e0a200914f88f121cd956e1eb7f72c8ace4b63171f52ba0334d142/scipp-25.12.0-cp313-cp313-macosx_11_0_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e2/69/1dcb8e967f62759578938db5b29792b82ea8939a2d712e79491fa3e1cf0a/scipp-26.3.1-cp313-cp313-macosx_14_0_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl @@ -555,7 +576,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/a5/174d33068d4383df4be9ee1ea9251f17820a622f3be744ca2ab7334818ee/spglib-2.6.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -563,11 +586,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -580,7 +603,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/6f/34/2e5cd576d312eb1131b615f49ee95ff6efb740965324843617adae729cf2/uv-0.10.9-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -602,14 +624,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.8-h8d0574d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-hef89b57_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.0-h55c6f16_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.2-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda @@ -618,12 +640,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.52.0-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.0-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.1-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda @@ -638,17 +660,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -656,67 +678,75 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/dc/ca/df314f70a6ed83394ac31eb835d024c04c9b46f6c2fe5d260fc7287058c3/chardet-7.3.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/28/d050c2716c74c6fce9ace360e727e6f86b68212fb6b0ea57c005ebe574ea/diffpy_pdffit2-1.5.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/82/c7/985c1670aa6d82ef270f04cde11394c168f2002700353bd2bde405e59b8f/fonttools-4.62.0-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/9c/1236dd7d22ed48527286b613c84e3376ea731b65e6734b6e6a0b4d03744c/gemmi-0.7.5-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -725,7 +755,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -749,10 +779,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -763,7 +793,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/57/b6/eff0305961a1d9447ec2b02f8c73c8946f22564d302a504185b730c9a761/msgspec-0.20.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -787,12 +817,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -811,15 +841,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -827,18 +858,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/18/5c/bc0ca94bff65fe0d206a369b54625f8ec7852dfd1d835174692026f34df9/scipp-25.12.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/79/fe/b14d806894cf05178f1e77d0d619f071db50cf698bc654c54f9241223bcf/scipp-26.3.1-cp313-cp313-macosx_14_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl @@ -847,7 +879,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/47/634fe8323c6c2bfa86e10eb41ebfe410db5e6231aa1727a31ce4f002480f/spglib-2.6.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -855,11 +889,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -872,7 +906,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/89/35/684f641de4de2b20db7d2163c735b2bb211e3b3c84c241706d6448e5e868/uv-0.10.9-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -904,9 +937,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.0-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_455.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.7.0-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda @@ -923,16 +956,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -940,68 +973,76 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/0f/57072b253af40c8aa6636e6de7d75985624c1eb392815b2f934199340a89/charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/a2/6b/2ac79f482dc59ea6ca2205830396ee23efd767db9a757394b7cf486ff00a/chardet-7.3.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1f/0c/6826cb2151628c59cca66ca6089ff910ab3ccd62b0524c2b398dc145ee52/diffpy_pdffit2-1.5.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f5/7a/e25245a30457595740041dba9d0ea8ec1b2517f2f1a6a741f15eba1a4edc/fonttools-4.62.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/ab/7d7463cda94f8b68b969ea97aaad679655a0e436efd6a643e528a8de114e/gemmi-0.7.5-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -1010,7 +1051,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -1034,10 +1075,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -1048,7 +1089,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f1/25/5e8080fe0117f799b1b68008dc29a65862077296b92550632de015128579/msgspec-0.20.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -1071,12 +1112,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -1094,35 +1135,38 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d8/8b/e2bbeb42068f0c48899e8eddd34902afc0f7429d4d2a152d2dc2670dc661/pythreejs-2.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/eb/d1/b3cd2733a96a36c54c36889b2cfdd0331c1e5b57fa1757485a22d0ec3142/scipp-25.12.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/37/fd/22621d3ee9e3ee87ef4c89b63bba55b265ab85039b3c1ba88ed2380a24c1/scipp-26.3.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl @@ -1131,7 +1175,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/16/56/a31e8d3c9e8d21100b83bbe1c1f3f7c94db317393a229e193461e5e6d2a4/spglib-2.6.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -1139,11 +1185,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -1156,7 +1202,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c9/e9/adf7a12136573937d12ac189569e2e90e7fad18b458192083df6986f3013/uv-0.10.9-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -1172,7 +1217,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl - pypi: ./ - py311-dev: + py-311-env: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: @@ -1186,8 +1231,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda @@ -1203,7 +1248,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda @@ -1211,7 +1256,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda @@ -1225,16 +1270,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl @@ -1242,69 +1287,77 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/f4/44d3b830a20e89ff82a3134912d9a1cf6084d64f3b95dcad40f74449a654/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/96/91/9cb4259a08689fd0d7f0552e8f78bead89a1778429a478e9857cc0ccfb43/chardet-7.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/76/53/c16972708cbb79f2942922571a687c52bd109a7bd51175aeb7558dff2236/coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/af/3b86dbd18d8dab5646f5b7c7db5bd9c43108e093864032aabd35d41b127d/diffpy_pdffit2-1.5.2.tar.gz - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/8a/d7/8e4845993ee233c2023d11babe9b3dae7d30333da1d792eeccebcb77baab/fonttools-4.62.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/48/eb/46e443fc70b4aabe6e775521ff476aefb051db9acabb16a5cb51f04e3e2b/gemmi-0.7.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -1313,7 +1366,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -1337,10 +1390,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -1351,7 +1404,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/da/e0/6cc2e852837cd6086fe7d8406af4294e66827a60a4cf60b86575a4a65ca8/msgpack-1.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6b/96/5c095b940de3aa6b43a71ec76275ac3537b21bd45c7499b5a17a429110fa/msgspec-0.20.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -1376,12 +1429,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/c8/46dfeac5825e600579157eea177be43e2f7ff4a99da9d0d0a49533509ac5/pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -1400,15 +1453,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -1416,18 +1470,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/4e/b6/ffe0bb67cec66cd450acff599bb07507bbf5ffda1a3a15dd2d8dbe7a6da7/scipp-25.12.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d4/06/19ff1efd58b85906149ce83dfddce23252cea5bec7e0fa5f834336cfe836/scipp-26.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -1436,7 +1491,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/55/41/591cd1e94254c20f00bb1f32c0b1a6de68c03d54e6daf78dd7b146d0b3fc/spglib-2.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/21/dd/3b7c53f1dbbf736fd27041aee68f8ac52226b610f914085b1652c2323442/sqlalchemy-2.0.48-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -1444,11 +1501,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -1461,7 +1518,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/79/34/b104c413079874493eed7bf11838b47b697cf1f0ed7e9de374ea37b4e4e0/uv-0.10.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -1484,14 +1540,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.2-h14c5de8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.0-h19cb2f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.4-h991f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda @@ -1499,12 +1555,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.2-h11316ed_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.52.0-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libuv-1.51.0-h58003a5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.0-h0d3cbff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.1-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.7.0-hf6efa0e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.1-hb6871ef_1.conda @@ -1518,17 +1574,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl @@ -1536,69 +1592,77 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/8f/9e/bcec3b22c64ecec47d39bf5167c2613efd41898c019dccd4183f6aa5d6a7/charset_normalizer-3.4.5-cp311-cp311-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/ef/97/eaf0d2b7c62d4de86e0c95adb30d9d56febe1bf78e57d62f198ece85b236/chardet-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/b4/ad/b59e5b451cf7172b8d1043dc0fa718f23aab379bc1521ee13d4bd9bfa960/coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/37/d24c8f8220ff07b839b2c043ea4903a33b0f455abe673ae3c03bbdb7f212/coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/9e/0e27056c6165ab3e2536d5efbc358cf495e6cd57fbaf51e68b1113fa7771/diffpy_pdffit2-1.5.2-cp311-cp311-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c0/7a/9aeec114bc9fc00d757a41f092f7107863d372e684a5b5724c043654477c/fonttools-4.62.0-cp311-cp311-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7f/79/b13830a65bf9fc85474a984604f094cc18817dc93a784f4c567a2dc05169/gemmi-0.7.5-cp311-cp311-macosx_10_14_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -1607,7 +1671,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -1631,10 +1695,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -1645,7 +1709,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/2c/97/560d11202bcd537abca693fd85d81cebe2107ba17301de42b01ac1677b69/msgpack-1.1.2-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/03/59/fdcb3af72f750a8de2bcf39d62ada70b5eb17b06d7f63860e0a679cb656b/msgspec-0.20.0-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -1670,12 +1734,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2b/46/5da1ec4a5171ee7bf1a0efa064aba70ba3d6e0788ce3f5acd1375d23c8c0/pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -1694,15 +1758,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -1710,18 +1775,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/0d/3f98a936a30bff4a460b51b9f85c4d994f94249930b2d8bedeb8111a359e/scipp-25.12.0-cp311-cp311-macosx_11_0_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/81/21/4962b1daddf0422e56c5ed4c41bea1ccb6d2a9ab72b795196835a20969c7/scipp-26.3.1-cp311-cp311-macosx_14_0_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl @@ -1730,7 +1796,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6c/44/30888e2a5b2fa2e6df18606b442cb8b126b0bea5a2f1ec4a2a82538ffecf/spglib-2.6.0-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -1738,11 +1806,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -1755,7 +1823,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/6f/34/2e5cd576d312eb1131b615f49ee95ff6efb740965324843617adae729cf2/uv-0.10.9-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -1778,14 +1845,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.8-h8d0574d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-hef89b57_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.0-h55c6f16_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.2-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda @@ -1793,12 +1860,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.52.0-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.0-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.1-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda @@ -1812,17 +1879,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl @@ -1830,68 +1897,76 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/8f/9e/bcec3b22c64ecec47d39bf5167c2613efd41898c019dccd4183f6aa5d6a7/charset_normalizer-3.4.5-cp311-cp311-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/6b/02/4b814ab51e5b3981ea1ded8bd97dee878e64458f7e954597692dc37c00ca/chardet-7.3.0-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/f1/17/0cb7ca3de72e5f4ef2ec2fa0089beafbcaaaead1844e8b8a63d35173d77d/coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ce/c9/7b61255980383781774d9857aa9e97fe7e9b8b08f97c0974afeef3083dd9/diffpy_pdffit2-1.5.2-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e4/33/63d79ca41020dd460b51f1e0f58ad1ff0a36b7bcbdf8f3971d52836581e9/fonttools-4.62.0-cp311-cp311-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/15/26cac702cdf6281ddeb185d5912ce14e555e277c6e4caeb1d36966e43822/gemmi-0.7.5-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -1900,7 +1975,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -1924,10 +1999,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -1938,7 +2013,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/83/04/28a41024ccbd67467380b6fb440ae916c1e4f25e2cd4c63abe6835ac566e/msgpack-1.1.2-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/5a/15/3c225610da9f02505d37d69a77f4a2e7daae2a125f99d638df211ba84e59/msgspec-0.20.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -1963,12 +2038,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/93/a29e9bc02d1cf557a834da780ceccd54e02421627200696fcf805ebdc3fb/pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -1987,15 +2062,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -2003,18 +2079,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/6a/3a/ab0eb61593569d5a0d080002e4b8c0998cb1116d8710781b7225c304b880/scipp-25.12.0-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/60/54/5011adb56853caabfd90686c2e543d1e3c76a8ef2755809b7e12e3f3583b/scipp-26.3.1-cp311-cp311-macosx_14_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl @@ -2023,7 +2100,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f6/ca/270d463f6c34f539bb55acdab14099c092d3be28c8af64d61399aa07610c/spglib-2.6.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/d7/6d/b8b78b5b80f3c3ab3f7fa90faa195ec3401f6d884b60221260fd4d51864c/sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -2031,11 +2110,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -2048,7 +2127,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/89/35/684f641de4de2b20db7d2163c735b2bb211e3b3c84c241706d6448e5e868/uv-0.10.9-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -2080,9 +2158,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.0-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_455.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.7.0-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda @@ -2098,16 +2176,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl @@ -2115,69 +2193,77 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fe/1f/a853b73d386521fd44b7f67ded6b17b7b2367067d9106a5c4b44f9a34274/charset_normalizer-3.4.5-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/c8/a4/067808ec52282aa4c4a5e2a87b5afb3ed5270b7ebb81c969c75169bc8947/chardet-7.3.0-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/61/08/3d9c8613079d2b11c185b865de9a4c1a68850cfda2b357fae365cf609f29/coverage-7.13.4-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/92/1cb532e88560cbee973396254b21bece8c5d7c2ece958a67afa08c9f10dc/debugpy-1.8.20-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/33/fae9a52a6cb97efd21176303dfef44e487b56e3473c1329e019d5682d158/diffpy_pdffit2-1.5.2-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/77/ce/f5a4c42c117f8113ce04048053c128d17426751a508f26398110c993a074/fonttools-4.62.0-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b9/5e/62402bf021183bc6122cb01b8f1be17cac67545713fb30f888f59357a782/gemmi-0.7.5-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/3a/efb2cf697fbccdf75b24e2c18025e7dfa54c4f31fab75c51d0fe79942cef/greenlet-3.3.2-cp311-cp311-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -2186,7 +2272,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -2210,10 +2296,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -2224,7 +2310,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/89/5e/406b7d578926b68790e390d83a1165a9bfc2d95612a1a9c1c4d5c72ea815/msgspec-0.20.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -2248,12 +2334,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/31/03/bef822e4f2d8f9d7448c133d0a18185d3cce3e70472774fffefe8b0ed562/pillow-12.1.1-cp311-cp311-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -2271,35 +2357,38 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d8/8b/e2bbeb42068f0c48899e8eddd34902afc0f7429d4d2a152d2dc2670dc661/pythreejs-2.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/79/c3/3e75075c7f71735f22b66fab0481f2c98e3a4d58cba55cb50ba29114bcf6/pywinpty-3.0.3-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/bd/75/6a3786de6645ac2ccd94fbf83c59cc6b929bfa3a89cb62c8cb3be4de0606/scipp-25.12.0-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/e6/0d/8882a4c7a5ebe59a46b709e82411d9c730d67250d41a2e11bc4bcd4d431d/scipp-26.3.1-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl @@ -2308,7 +2397,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/25/a8/d89e1bde525baba10eb8d0be79a5bbaf56c59a47b32bb954866d96a228e3/spglib-2.6.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/58/d5/dd767277f6feef12d05651538f280277e661698f617fa4d086cce6055416/sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -2316,11 +2407,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -2333,7 +2424,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c9/e9/adf7a12136573937d12ac189569e2e90e7fad18b458192083df6986f3013/uv-0.10.9-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -2350,7 +2440,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl - pypi: ./ - py313-dev: + py-313-env: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: @@ -2364,8 +2454,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda @@ -2382,13 +2472,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda @@ -2403,16 +2493,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -2420,68 +2510,76 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/94/0a/af49691938dfe175d71b8a929bd7e4ace2809c0c5134e28bc535660d5262/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d0/c1/8f69e69303554fb3a5ada668683690d68623dcfb1eb52067cce85c91012a/chardet-7.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/af/3b86dbd18d8dab5646f5b7c7db5bd9c43108e093864032aabd35d41b127d/diffpy_pdffit2-1.5.2.tar.gz - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/fb/bc/60d93477b653eeb1ddf5f9ec34be689b79234d82dbdded269ac0252715b8/fonttools-4.62.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/8c/db8e79c4c744ebae1dcf25f7dbcc5d7df912cdbcdf7221e761479e8bd04b/gemmi-0.7.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -2490,7 +2588,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -2514,10 +2612,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -2528,7 +2626,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/5d/ba/459f18c16f2b3fc1a1ca871f72f07d70c07bf768ad0a507a698b8052ac58/msgpack-1.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/28/83/36557b04cfdc317ed8a525c4993b23e43a8fbcddaddd78619112ca07138c/msgspec-0.20.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -2552,12 +2650,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -2576,15 +2674,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -2592,18 +2691,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/94/98/aa2d4b9d28969cc7f62409f9f9fc5b5a853af651255eba03e9bee8546dd9/scipp-25.12.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/2e/75/5604f4d17ab607510d4702f156329194d8edfff7e29644ca9200b085e9a2/scipp-26.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -2612,7 +2712,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/53/01/1c0485ae02e645bc517bf5d5a6ca674f62c97e247890b954cbfe85c64dae/spglib-2.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -2620,11 +2722,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -2637,7 +2739,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/79/34/b104c413079874493eed7bf11838b47b697cf1f0ed7e9de374ea37b4e4e0/uv-0.10.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -2659,14 +2760,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.2-h14c5de8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.0-h19cb2f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.4-h991f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda @@ -2675,12 +2776,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.2-h11316ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.52.0-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libuv-1.51.0-h58003a5_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.0-h0d3cbff_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.1-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.7.0-hf6efa0e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.1-hb6871ef_1.conda @@ -2695,17 +2796,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -2713,68 +2814,76 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/54/2f/ca2f6d868e450eb37c8b91903f6741024caa2c884418e6a26c96df34751d/chardet-7.3.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/19/eb/bb3ff420acdaf9bcaf94c510f42df11974bc3fc475ef50d619366f33fda3/diffpy_pdffit2-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c1/dc/c409c8ceec0d3119e9ab0b7b1a2e3c76d1f4d66e4a9db5c59e6b7652e7df/fonttools-4.62.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/80/fd758344a72ca7b5e1c5bbdc1d263f3b215d3897941b5f450380445ca0a9/gemmi-0.7.5-cp313-cp313-macosx_10_14_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -2783,7 +2892,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -2807,10 +2916,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -2821,7 +2930,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/6b/31/b46518ecc604d7edf3a4f94cb3bf021fc62aa301f0cb849936968164ef23/msgpack-1.1.2-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/8a/d1/b902d38b6e5ba3bdddbec469bba388d647f960aeed7b5b3623a8debe8a76/msgspec-0.20.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -2845,12 +2954,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -2869,15 +2978,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -2885,18 +2995,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/22/75e119e0a200914f88f121cd956e1eb7f72c8ace4b63171f52ba0334d142/scipp-25.12.0-cp313-cp313-macosx_11_0_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e2/69/1dcb8e967f62759578938db5b29792b82ea8939a2d712e79491fa3e1cf0a/scipp-26.3.1-cp313-cp313-macosx_14_0_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl @@ -2905,7 +3016,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/a5/174d33068d4383df4be9ee1ea9251f17820a622f3be744ca2ab7334818ee/spglib-2.6.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -2913,11 +3026,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -2930,7 +3043,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/6f/34/2e5cd576d312eb1131b615f49ee95ff6efb740965324843617adae729cf2/uv-0.10.9-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -2952,14 +3064,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.8-h8d0574d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-hef89b57_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.0-h55c6f16_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.2-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda @@ -2968,12 +3080,12 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.52.0-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.0-hc7d1edf_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.1-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda @@ -2988,17 +3100,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -3006,67 +3118,75 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/dc/ca/df314f70a6ed83394ac31eb835d024c04c9b46f6c2fe5d260fc7287058c3/chardet-7.3.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/28/d050c2716c74c6fce9ace360e727e6f86b68212fb6b0ea57c005ebe574ea/diffpy_pdffit2-1.5.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/82/c7/985c1670aa6d82ef270f04cde11394c168f2002700353bd2bde405e59b8f/fonttools-4.62.0-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/9c/1236dd7d22ed48527286b613c84e3376ea731b65e6734b6e6a0b4d03744c/gemmi-0.7.5-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -3075,7 +3195,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -3099,10 +3219,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -3113,7 +3233,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/92/dc/c385f38f2c2433333345a82926c6bfa5ecfff3ef787201614317b58dd8be/msgpack-1.1.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/57/b6/eff0305961a1d9447ec2b02f8c73c8946f22564d302a504185b730c9a761/msgspec-0.20.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -3137,12 +3257,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -3161,15 +3281,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -3177,18 +3298,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/18/5c/bc0ca94bff65fe0d206a369b54625f8ec7852dfd1d835174692026f34df9/scipp-25.12.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/79/fe/b14d806894cf05178f1e77d0d619f071db50cf698bc654c54f9241223bcf/scipp-26.3.1-cp313-cp313-macosx_14_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl @@ -3197,7 +3319,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/47/634fe8323c6c2bfa86e10eb41ebfe410db5e6231aa1727a31ce4f002480f/spglib-2.6.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -3205,11 +3329,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -3222,7 +3346,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/89/35/684f641de4de2b20db7d2163c735b2bb211e3b3c84c241706d6448e5e868/uv-0.10.9-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -3254,9 +3377,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.0-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_455.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.7.0-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda @@ -3273,16 +3396,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/d0/7b958df957e4827837b590944008f0b28078f552b451f7407b4b3d54f574/asciichartpy-1.5.25-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl @@ -3290,68 +3413,76 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/8e/278ea79cf8ee0c395a5ad74625b3465c2fc234bb277f171dd59dd203820d/bumps-1.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/0f/57072b253af40c8aa6636e6de7d75985624c1eb392815b2f934199340a89/charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/a2/6b/2ac79f482dc59ea6ca2205830396ee23efd767db9a757394b7cf486ff00a/chardet-7.3.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/52/e8/c14cc8af8cd38e86887053843382629bd8ebd117f83f15eb1194d65a2c9d/cryspy-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1f/0c/6826cb2151628c59cca66ca6089ff910ab3ccd62b0524c2b398dc145ee52/diffpy_pdffit2-1.5.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/8e/52/39914bf42bb01901c91781def35c1aeffa431a59299e9748c0cfae3e5493/diffpy_structure-3.3.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f5/7a/e25245a30457595740041dba9d0ea8ec1b2517f2f1a6a741f15eba1a4edc/fonttools-4.62.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ee/ab/7d7463cda94f8b68b969ea97aaad679655a0e436efd6a643e528a8de114e/gemmi-0.7.5-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/c9/6869a1dcf4aaf309b9543ec070be3ec3adebee7c9bec9af8c230494134b9/interrogate-1.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl @@ -3360,7 +3491,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl @@ -3384,10 +3515,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/4e/c09876f08fa9faaa5e1178f3d77b7af3f343258689bd6f3b72593b2f74e3/mkdocs_markdownextradata_plugin-0.2.6-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/3d/020a6b6248c3d4a37797db068256f0b3f15b01bc481327ba888c50309aa8/mkdocs_plugin_inline_svg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl @@ -3398,7 +3529,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/74/07/1ed8277f8653c40ebc65985180b007879f6a836c525b3885dcc6448ae6cb/msgpack-1.1.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f1/25/5e8080fe0117f799b1b68008dc29a65862077296b92550632de015128579/msgspec-0.20.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl @@ -3421,12 +3552,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl @@ -3444,35 +3575,38 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d8/8b/e2bbeb42068f0c48899e8eddd34902afc0f7429d4d2a152d2dc2670dc661/pythreejs-2.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/eb/d1/b3cd2733a96a36c54c36889b2cfdd0331c1e5b57fa1757485a22d0ec3142/scipp-25.12.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/37/fd/22621d3ee9e3ee87ef4c89b63bba55b265ab85039b3c1ba88ed2380a24c1/scipp-26.3.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl @@ -3481,7 +3615,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/59/0782e51887ac6b07ffd1570e0364cf901ebc36345fea669969d2084baebb/simple_websocket-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/16/56/a31e8d3c9e8d21100b83bbe1c1f3f7c94db317393a229e193461e5e6d2a4/spglib-2.6.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl @@ -3489,11 +3625,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl @@ -3506,7 +3642,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c9/e9/adf7a12136573937d12ac189569e2e90e7fad18b458192083df6986f3013/uv-0.10.9-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bc/4a/c6fd02a642bbe4e9f25cdd3714a328e3fc3eb6bd7b5e96f1a2285bd928b9/varname-0.15.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl @@ -3728,17 +3863,16 @@ packages: requires_dist: - typing-extensions>=4.0.0 ; python_full_version < '3.9' requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl name: anyio - version: 4.12.1 - sha256: d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c + version: 4.13.0 + sha256: 08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 requires_dist: - exceptiongroup>=1.0.2 ; python_full_version < '3.11' - idna>=2.8 - typing-extensions>=4.5 ; python_full_version < '3.13' - - trio>=0.32.0 ; python_full_version >= '3.10' and extra == 'trio' - - trio>=0.31.0 ; python_full_version < '3.10' and extra == 'trio' - requires_python: '>=3.9' + - trio>=0.32.0 ; extra == 'trio' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl name: appnope version: 0.1.4 @@ -3811,16 +3945,17 @@ packages: requires_dist: - setuptools - flake8 ; extra == 'qa' -- pypi: https://files.pythonhosted.org/packages/3e/9b/9b55b4d4855743de61ba91566d03b2560285ed8fc0387b9cf914795d4abf/ase-3.27.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl name: ase - version: 3.27.0 - sha256: 058c48ea504fe7fbbe7c932f778415243ef2df45b1ab869866f24efcc17f0538 + version: 3.28.0 + sha256: 0e24056302d7307b7247f90de281de15e3031c14cf400bedb1116c3b0d0e50b8 requires_dist: - numpy>=1.21.6 - scipy>=1.8.1 - matplotlib>=3.5.2 - sphinx ; extra == 'docs' - sphinx-book-theme ; extra == 'docs' + - sphinxcontrib-video ; extra == 'docs' - sphinx-gallery ; extra == 'docs' - pillow ; extra == 'docs' - pytest>=7.4.0 ; extra == 'test' @@ -3855,17 +3990,17 @@ packages: - pytest-cov ; extra == 'test' - pytest-xdist ; extra == 'test' requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/13/5c/af990f019b8dd11c5492a6371fe74a5b0276357370030b67254a87329944/async_lru-2.2.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl name: async-lru - version: 2.2.0 - sha256: e2c1cf731eba202b59c5feedaef14ffd9d02ad0037fcda64938699f2c380eafe + version: 2.3.0 + sha256: eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315 requires_dist: - typing-extensions>=4.0.0 ; python_full_version < '3.11' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl name: attrs - version: 25.4.0 - sha256: adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373 + version: 26.1.0 + sha256: c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl name: autopep8 @@ -3935,10 +4070,10 @@ packages: version: 1.9.0 sha256: ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/4a/57/3b7d4dd193ade4641c865bc2b93aeeb71162e81fc348b8dad020215601ed/build-1.4.2-py3-none-any.whl name: build - version: 1.4.0 - sha256: 6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596 + version: 1.4.2 + sha256: 7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88 requires_dist: - packaging>=24.0 - pyproject-hooks @@ -4136,35 +4271,75 @@ packages: version: 3.5.0 sha256: a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/03/f4/44d3b830a20e89ff82a3134912d9a1cf6084d64f3b95dcad40f74449a654/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/54/2f/ca2f6d868e450eb37c8b91903f6741024caa2c884418e6a26c96df34751d/chardet-7.3.0-cp313-cp313-macosx_10_13_x86_64.whl + name: chardet + version: 7.3.0 + sha256: 51a0ad2031bd10d24357d7e52de68abe29efb62b7aedf1bd9d1c593fd438cec5 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/6b/02/4b814ab51e5b3981ea1ded8bd97dee878e64458f7e954597692dc37c00ca/chardet-7.3.0-cp311-cp311-macosx_11_0_arm64.whl + name: chardet + version: 7.3.0 + sha256: 811029a7cf397ed32441aa134475f7047826fba496047a6afd7bba1da7fc4ab7 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/96/91/9cb4259a08689fd0d7f0552e8f78bead89a1778429a478e9857cc0ccfb43/chardet-7.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: chardet + version: 7.3.0 + sha256: 90a6249a455b8ecdb207a9f5a5eb46e1c21f2173eacb5e1603def61ed4784303 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a2/6b/2ac79f482dc59ea6ca2205830396ee23efd767db9a757394b7cf486ff00a/chardet-7.3.0-cp313-cp313-win_amd64.whl + name: chardet + version: 7.3.0 + sha256: 340f4eccafa17cfc5a90af1fb8558dc791156f815f8d800d8ac2b1b9fb2b8103 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/c8/a4/067808ec52282aa4c4a5e2a87b5afb3ed5270b7ebb81c969c75169bc8947/chardet-7.3.0-cp311-cp311-win_amd64.whl + name: chardet + version: 7.3.0 + sha256: ff4d81f5561f2382343dc922e533c017d52bc55125e50551c18034fffcdbd0cb + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/d0/c1/8f69e69303554fb3a5ada668683690d68623dcfb1eb52067cce85c91012a/chardet-7.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: chardet + version: 7.3.0 + sha256: 622206fdb0d274766559fa172e2d680d58cc5fc6a67015c21c36fec500e7bd50 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/dc/ca/df314f70a6ed83394ac31eb835d024c04c9b46f6c2fe5d260fc7287058c3/chardet-7.3.0-cp313-cp313-macosx_11_0_arm64.whl + name: chardet + version: 7.3.0 + sha256: 23d264962f680deec691308cc753685b4c5a69798a26e00ce4f4cf96d2e43fcb + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ef/97/eaf0d2b7c62d4de86e0c95adb30d9d56febe1bf78e57d62f198ece85b236/chardet-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl + name: chardet + version: 7.3.0 + sha256: 5b58eb6d6dea67fc3b578dd956dd6e92aafea383aa559ccea55265554ff062a3 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl name: charset-normalizer - version: 3.4.5 - sha256: 5bcb3227c3d9aaf73eaaab1db7ccd80a8995c509ee9941e2aae060ca6e4e5d81 + version: 3.4.6 + sha256: 11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/8f/9e/bcec3b22c64ecec47d39bf5167c2613efd41898c019dccd4183f6aa5d6a7/charset_normalizer-3.4.5-cp311-cp311-macosx_10_9_universal2.whl +- pypi: https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: charset-normalizer - version: 3.4.5 - sha256: 610f72c0ee565dfb8ae1241b666119582fdbfe7c0975c175be719f940e110694 + version: 3.4.6 + sha256: 530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/94/0a/af49691938dfe175d71b8a929bd7e4ace2809c0c5134e28bc535660d5262/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: charset-normalizer - version: 3.4.5 - sha256: 0625665e4ebdddb553ab185de5db7054393af8879fb0c87bd5690d14379d6819 + version: 3.4.6 + sha256: 9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/b9/0f/57072b253af40c8aa6636e6de7d75985624c1eb392815b2f934199340a89/charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl name: charset-normalizer - version: 3.4.5 - sha256: e37bd100d2c5d3ba35db9c7c5ba5a9228cbcffe5c4778dc824b164e5257813d7 + version: 3.4.6 + sha256: 82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl +- pypi: https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl name: charset-normalizer - version: 3.4.5 - sha256: ac59c15e3f1465f722607800c68713f9fbc2f672b9eb649fe831da4019ae9b23 + version: 3.4.6 + sha256: 572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/fe/1f/a853b73d386521fd44b7f67ded6b17b7b2367067d9106a5c4b44f9a34274/charset_normalizer-3.4.5-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl name: charset-normalizer - version: 3.4.5 - sha256: f8102ae93c0bc863b1d41ea0f4499c20a83229f52ed870850892df555187154a + version: 3.4.6 + sha256: a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl name: click @@ -4390,59 +4565,79 @@ packages: - pytest-xdist ; extra == 'test-no-images' - wurlitzer ; extra == 'test-no-images' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/61/08/3d9c8613079d2b11c185b865de9a4c1a68850cfda2b357fae365cf609f29/coverage-7.13.4-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl + name: copier + version: 9.14.0 + sha256: e12a18cfef22e67254e5229f0b4bdab85e1e3e82926e448226be0b70d0f4de53 + requires_dist: + - colorama>=0.4.6 + - dunamai>=1.7.0 + - funcy>=1.17 + - jinja2-ansible-filters>=1.3.1 + - jinja2>=3.1.5 + - packaging>=23.0 + - pathspec>=0.9.0 + - platformdirs>=4.3.6 + - plumbum>=1.6.9 + - pydantic>=2.4.2 + - pygments>=2.7.1 + - pyyaml>=5.3.1 + - questionary>=1.8.1 + - typing-extensions>=4.0.0,<5.0.0 ; python_full_version < '3.11' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl name: coverage - version: 7.13.4 - sha256: 2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f + version: 7.13.5 + sha256: 941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/76/53/c16972708cbb79f2942922571a687c52bd109a7bd51175aeb7558dff2236/coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl name: coverage - version: 7.13.4 - sha256: 8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7 + version: 7.13.5 + sha256: 145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/4b/37/d24c8f8220ff07b839b2c043ea4903a33b0f455abe673ae3c03bbdb7f212/coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl name: coverage - version: 7.13.4 - sha256: 3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac + version: 7.13.5 + sha256: 66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/b4/ad/b59e5b451cf7172b8d1043dc0fa718f23aab379bc1521ee13d4bd9bfa960/coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl name: coverage - version: 7.13.4 - sha256: d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053 + version: 7.13.5 + sha256: 631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl name: coverage - version: 7.13.4 - sha256: b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9 + version: 7.13.5 + sha256: 5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl name: coverage - version: 7.13.4 - sha256: e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd + version: 7.13.5 + sha256: ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl name: coverage - version: 7.13.4 - sha256: b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b + version: 7.13.5 + sha256: 78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/f1/17/0cb7ca3de72e5f4ef2ec2fa0089beafbcaaaead1844e8b8a63d35173d77d/coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl name: coverage - version: 7.13.4 - sha256: 19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11 + version: 7.13.5 + sha256: 258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' @@ -4482,10 +4677,10 @@ packages: requires_dist: - pyobjc-framework-cocoa ; sys_platform == 'darwin' and extra == 'macos-listener' requires_python: '>=3.6' -- pypi: https://files.pythonhosted.org/packages/e5/23/d39ccc4ed76222db31530b0a7d38876fdb7673e23f838e8d8f0ed4651a4f/dask-2026.1.2-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/4a/f3/00bb1e867fba351e2d784170955713bee200c43ea306c59f30bd7e748192/dask-2026.3.0-py3-none-any.whl name: dask - version: 2026.1.2 - sha256: 46a0cf3b8d87f78a3d2e6b145aea4418a6d6d606fe6a16c79bd8ca2bb862bc91 + version: 2026.3.0 + sha256: be614b9242b0b38288060fb2d7696125946469c98a1c30e174883fd199e0428d requires_dist: - click>=8.1 - cloudpickle>=3.0.0 @@ -4499,7 +4694,7 @@ packages: - dask[array] ; extra == 'dataframe' - pandas>=2.0 ; extra == 'dataframe' - pyarrow>=16.0 ; extra == 'dataframe' - - distributed>=2026.1.2,<2026.1.3 ; extra == 'distributed' + - distributed>=2026.3.0,<2026.3.1 ; extra == 'distributed' - bokeh>=3.1.0 ; extra == 'diagnostics' - jinja2>=2.10.3 ; extra == 'diagnostics' - dask[array,dataframe,diagnostics,distributed] ; extra == 'complete' @@ -4539,10 +4734,10 @@ packages: version: 0.7.1 sha256: a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*' -- pypi: https://files.pythonhosted.org/packages/13/40/e412e277c43983693456d7f11f2bc72ca9209aa1342255bb446496d2fb48/dfo_ls-1.6.2-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/c7/a0/5ff05d1919ca249508012cad89f08fdc6cfbdaa15b41651c5fe6dffaf1d3/dfo_ls-1.6.5-py3-none-any.whl name: dfo-ls - version: 1.6.2 - sha256: 961d15e7194f3868e9e48da45010cb6d24d85491007fbee4e38a9f3ab8050cef + version: 1.6.5 + sha256: d147d42e471e240f9abf8bc38351a88f555ea6a8fcfd83119bbbf93c36f75ab2 requires_dist: - setuptools - numpy @@ -4610,15 +4805,15 @@ packages: - numpy - pycifrw requires_python: '>=3.11,<3.14' -- pypi: https://files.pythonhosted.org/packages/3f/5e/fcb9a14641d65d9fc79efd7c97eef38aa8f1d99c7cef657b6aedd742efef/diffpy_utils-3.7.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/61/2b/e260d50e64690d2a9e405d52ccd18a63c286c5088937dd0107cb23eb3195/diffpy_utils-3.7.2-py3-none-any.whl name: diffpy-utils - version: 3.7.1 - sha256: 8bf33eb3e228bf6a18242e4cc01cf4b7bff307fbef90f745bf7d680c7ed4d3b7 + version: 3.7.2 + sha256: 6100600736791a8e4638e3dd476704f4dabe3cab75bcb5c60c83c16a2032519a requires_dist: - numpy - xraydb - scipy - requires_python: '>=3.11,<3.15' + requires_python: '>=3.10,<3.15' - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl name: dill version: 0.4.1 @@ -4667,24 +4862,49 @@ packages: - tomli>=2.0.0,<3.0.0 ; python_full_version < '3.11' and extra == 'tomli' - untokenize>=0.1.1,<0.2.0 requires_python: '>=3.9,<4.0' +- pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl + name: docstring-parser-fork + version: 0.0.14 + sha256: 4c544f234ef2cc2749a3df32b70c437d77888b1099143a1ad5454452c574b9af + requires_dist: + - docstring-parser[docs] ; extra == 'dev' + - docstring-parser[test] ; extra == 'dev' + - pre-commit>=2.16.0 ; python_full_version >= '3.9' and extra == 'dev' + - pydoctor>=25.4.0 ; extra == 'docs' + - pytest ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl + name: dunamai + version: 1.26.0 + sha256: f584edf0fda0d308cce0961f807bc90a8fe3d9ff4d62f94e72eca7b43f0ed5f6 + requires_dist: + - importlib-metadata>=1.6.0 ; python_full_version < '3.8' + - packaging>=20.9 + requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty36 - sha256: c26412f987f3f60607ea00f77d4f12aadc3b38ea31833f634b218e09965dbdbc + version: 0.10.2+dev4 + sha256: c8c56839d471863bde26398ee4ae4a9316dae45a3e250437f8bfc947c30d2cb9 requires_dist: - asciichartpy - asteval - bumps - colorama - cryspy + - darkdetect - dfo-ls - diffpy-pdffit2 - diffpy-utils - essdiffraction - gemmi + - jupyterlab - lmfit - numpy + - pandas + - pixi-kernel + - plotly - pooch + - py3dmol - rich - scipy - sympy @@ -4693,67 +4913,37 @@ packages: - typer - uncertainties - varname - - build ; extra == 'all' - - darkdetect ; extra == 'all' - - docformatter ; extra == 'all' - - interrogate ; extra == 'all' - - jinja2 ; extra == 'all' - - jupyterquiz ; extra == 'all' - - jupytext ; extra == 'all' - - mike ; extra == 'all' - - mkdocs ; extra == 'all' - - mkdocs-autorefs ; extra == 'all' - - mkdocs-jupyter ; extra == 'all' - - mkdocs-markdownextradata-plugin ; extra == 'all' - - mkdocs-material ; extra == 'all' - - mkdocs-plugin-inline-svg ; extra == 'all' - - mkdocstrings-python ; extra == 'all' - - nbmake ; extra == 'all' - - nbqa ; extra == 'all' - - nbstripout ; extra == 'all' - - pandas ; extra == 'all' - - plotly ; extra == 'all' - - pre-commit ; extra == 'all' - - py3dmol ; extra == 'all' - - pytest ; extra == 'all' - - pytest-cov ; extra == 'all' - - pytest-xdist ; extra == 'all' - - pyyaml ; extra == 'all' - - radon ; extra == 'all' - - ruff ; extra == 'all' - - validate-pyproject[all] ; extra == 'all' - - versioningit ; extra == 'all' - build ; extra == 'dev' + - copier ; extra == 'dev' - docformatter ; extra == 'dev' + - gitpython ; extra == 'dev' - interrogate ; extra == 'dev' - jinja2 ; extra == 'dev' - jupyterquiz ; extra == 'dev' - jupytext ; extra == 'dev' + - mike ; extra == 'dev' + - mkdocs ; extra == 'dev' + - mkdocs-autorefs ; extra == 'dev' + - mkdocs-jupyter ; extra == 'dev' + - mkdocs-markdownextradata-plugin ; extra == 'dev' + - mkdocs-material ; extra == 'dev' + - mkdocs-plugin-inline-svg ; extra == 'dev' + - mkdocstrings-python ; extra == 'dev' - nbmake ; extra == 'dev' - nbqa ; extra == 'dev' - nbstripout ; extra == 'dev' - pre-commit ; extra == 'dev' + - pydoclint ; extra == 'dev' - pytest ; extra == 'dev' - pytest-cov ; extra == 'dev' - pytest-xdist ; extra == 'dev' + - pyyaml ; extra == 'dev' - radon ; extra == 'dev' - ruff ; extra == 'dev' + - spdx-headers ; extra == 'dev' - validate-pyproject[all] ; extra == 'dev' - versioningit ; extra == 'dev' - - mike ; extra == 'docs' - - mkdocs ; extra == 'docs' - - mkdocs-autorefs ; extra == 'docs' - - mkdocs-jupyter ; extra == 'docs' - - mkdocs-markdownextradata-plugin ; extra == 'docs' - - mkdocs-material ; extra == 'docs' - - mkdocs-plugin-inline-svg ; extra == 'docs' - - mkdocstrings-python ; extra == 'docs' - - pyyaml ; extra == 'docs' - - darkdetect ; extra == 'visualization' - - pandas ; extra == 'visualization' - - plotly ; extra == 'visualization' - - py3dmol ; extra == 'visualization' - requires_python: '>=3.11,<3.14' + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl name: email-validator version: 2.3.0 @@ -4785,13 +4975,13 @@ packages: - pytest>=7.0 ; extra == 'test' - ipywidgets>=8.1.7 ; extra == 'test' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/a8/bf/530a2b63de72bdb8e518049a1e6639d1e6d79e591bfcdaaae08a9a3a7b84/essreduce-26.3.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl name: essreduce - version: 26.3.1 - sha256: 0f629a52ad1793904cc41f6298229321f249f87e972b35f0b0fcfad8f280a78b + version: 26.4.0 + sha256: 06a9ebf58cba3cc29ac70f7b89a3e596be92d6a61130361b8c19fa8afec2b1b5 requires_dist: - sciline>=25.11.0 - - scipp>=25.4.0 + - scipp>=26.3.0 - scippneutron>=25.11.1 - scippnexus>=25.6.0 - graphviz>=0.20 ; extra == 'test' @@ -4854,15 +5044,15 @@ packages: - pytest-benchmark ; extra == 'devel' - pytest-cache ; extra == 'devel' - validictory ; extra == 'devel' -- pypi: https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl name: filelock - version: 3.25.1 - sha256: 18972df45473c4aa2c7921b609ee9ca4925910cc3a0fb226c96b92fc224ef7bf + version: 3.25.2 + sha256: ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/77/ce/f5a4c42c117f8113ce04048053c128d17426751a508f26398110c993a074/fonttools-4.62.0-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl name: fonttools - version: 4.62.0 - sha256: 4da779e8f342a32856075ddb193b2a024ad900bc04ecb744014c32409ae871ed + version: 4.62.1 + sha256: 68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -4893,10 +5083,10 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/82/c7/985c1670aa6d82ef270f04cde11394c168f2002700353bd2bde405e59b8f/fonttools-4.62.0-cp313-cp313-macosx_10_13_universal2.whl +- pypi: https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl name: fonttools - version: 4.62.0 - sha256: 274c8b8a87e439faf565d3bcd3f9f9e31bca7740755776a4a90a4bfeaa722efa + version: 4.62.1 + sha256: 9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14 requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -4927,10 +5117,10 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/8a/d7/8e4845993ee233c2023d11babe9b3dae7d30333da1d792eeccebcb77baab/fonttools-4.62.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl name: fonttools - version: 4.62.0 - sha256: 591220d5333264b1df0d3285adbdfe2af4f6a45bbf9ca2b485f97c9f577c49ff + version: 4.62.1 + sha256: 7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056 requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -4961,10 +5151,10 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/c0/7a/9aeec114bc9fc00d757a41f092f7107863d372e684a5b5724c043654477c/fonttools-4.62.0-cp311-cp311-macosx_10_9_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl name: fonttools - version: 4.62.0 - sha256: 153afc3012ff8761b1733e8fbe5d98623409774c44ffd88fbcb780e240c11d13 + version: 4.62.1 + sha256: c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79 requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -4995,10 +5185,10 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/c1/dc/c409c8ceec0d3119e9ab0b7b1a2e3c76d1f4d66e4a9db5c59e6b7652e7df/fonttools-4.62.0-cp313-cp313-macosx_10_13_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl name: fonttools - version: 4.62.0 - sha256: 93e27131a5a0ae82aaadcffe309b1bae195f6711689722af026862bede05c07c + version: 4.62.1 + sha256: 40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7 requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -5029,10 +5219,10 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/e4/33/63d79ca41020dd460b51f1e0f58ad1ff0a36b7bcbdf8f3971d52836581e9/fonttools-4.62.0-cp311-cp311-macosx_10_9_universal2.whl +- pypi: https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl name: fonttools - version: 4.62.0 - sha256: 196cafef9aeec5258425bd31a4e9a414b2ee0d1557bca184d7923d3d3bcd90f9 + version: 4.62.1 + sha256: 1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -5063,10 +5253,10 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/f5/7a/e25245a30457595740041dba9d0ea8ec1b2517f2f1a6a741f15eba1a4edc/fonttools-4.62.0-cp313-cp313-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl name: fonttools - version: 4.62.0 - sha256: 6826a5aa53fb6def8a66bf423939745f415546c4e92478a7c531b8b6282b6c3b + version: 4.62.1 + sha256: e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2 requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -5097,10 +5287,10 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/fb/bc/60d93477b653eeb1ddf5f9ec34be689b79234d82dbdded269ac0252715b8/fonttools-4.62.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl name: fonttools - version: 4.62.0 - sha256: 106aec9226f9498fc5345125ff7200842c01eda273ae038f5049b0916907acee + version: 4.62.1 + sha256: 6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1 requires_dist: - lxml>=4.0 ; extra == 'lxml' - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' @@ -5286,6 +5476,10 @@ packages: - zstandard ; python_full_version < '3.14' and extra == 'test-full' - tqdm ; extra == 'tqdm' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl + name: funcy + version: '2.0' + sha256: 53df23c8bb1651b12f095df764bfb057935d49537a56de211b098f4c79614bb0 - pypi: https://files.pythonhosted.org/packages/42/15/26cac702cdf6281ddeb185d5912ce14e555e277c6e4caeb1d36966e43822/gemmi-0.7.5-cp311-cp311-macosx_11_0_arm64.whl name: gemmi version: 0.7.5 @@ -5336,6 +5530,35 @@ packages: - markdown ; extra == 'dev' - flake8 ; extra == 'dev' - wheel ; extra == 'dev' +- pypi: https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl + name: gitdb + version: 4.0.12 + sha256: 67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf + requires_dist: + - smmap>=3.0.1,<6 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl + name: gitpython + version: 3.1.46 + sha256: 79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058 + requires_dist: + - gitdb>=4.0.1,<5 + - typing-extensions>=3.10.0.2 ; python_full_version < '3.10' + - coverage[toml] ; extra == 'test' + - ddt>=1.1.1,!=1.4.3 ; extra == 'test' + - mock ; python_full_version < '3.8' and extra == 'test' + - mypy==1.18.2 ; python_full_version >= '3.9' and extra == 'test' + - pre-commit ; extra == 'test' + - pytest>=7.3.1 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-instafail ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-sugar ; extra == 'test' + - typing-extensions ; python_full_version < '3.11' and extra == 'test' + - sphinx>=7.1.2,<7.2 ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - sphinx-autodoc-typehints ; extra == 'doc' + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl name: graphviz version: '0.21' @@ -5422,10 +5645,10 @@ packages: - psutil ; extra == 'test' - setuptools ; extra == 'test' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl name: griffelib - version: 2.0.0 - sha256: 01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f + version: 2.0.1 + sha256: b769eed581c0e857d362fc8fcd8e57ecd2330c124b6104ac8b4c1c86d76970aa requires_dist: - pip>=24.0 ; extra == 'pypi' - platformdirs>=4.2 ; extra == 'pypi' @@ -5573,9 +5796,9 @@ packages: - socksio==1.* ; extra == 'socks' - zstandard>=0.18.0 ; extra == 'zstd' requires_python: '>=3.8' -- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda - sha256: 142a722072fa96cf16ff98eaaf641f54ab84744af81754c292cb81e0881c0329 - md5: 186a18e3ba246eccfc7cff00cd19a870 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda + sha256: fbf86c4a59c2ed05bbffb2ba25c7ed94f6185ec30ecb691615d42342baa1a16a + md5: c80d8a3b84358cb967fa81e7075fbc8a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 @@ -5583,32 +5806,32 @@ packages: license: MIT license_family: MIT purls: [] - size: 12728445 - timestamp: 1767969922681 -- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.2-h14c5de8_0.conda - sha256: f3066beae7fe3002f09c8a412cdf1819f49a2c9a485f720ec11664330cf9f1fe - md5: 30334add4de016489b731c6662511684 + size: 12723451 + timestamp: 1773822285671 +- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda + sha256: 1294117122d55246bb83ad5b589e2a031aacdf2d0b1f99fd338aa4394f881735 + md5: 627eca44e62e2b665eeec57a984a7f00 depends: - - __osx >=10.13 + - __osx >=11.0 license: MIT license_family: MIT purls: [] - size: 12263724 - timestamp: 1767970604977 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-hef89b57_0.conda - sha256: 24bc62335106c30fecbcc1dba62c5eba06d18b90ea1061abd111af7b9c89c2d7 - md5: 114e6bfe7c5ad2525eb3597acdbf2300 + size: 12273764 + timestamp: 1773822733780 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda + sha256: 3a7907a17e9937d3a46dfd41cffaf815abad59a569440d1e25177c15fd0684e5 + md5: f1182c91c0de31a7abd40cedf6a5ebef depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] - size: 12389400 - timestamp: 1772209104304 -- pypi: https://files.pythonhosted.org/packages/40/66/71c1227dff78aaeb942fed29dd5651f2aec166cc7c9aeea3e8b26a539b7d/identify-2.6.17-py2.py3-none-any.whl + size: 12361647 + timestamp: 1773822915649 +- pypi: https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl name: identify - version: 2.6.17 - sha256: be5f8412d5ed4b20f2bd41a65f920990bdccaa6a4a18a08f1eefdcd0bdd885f0 + version: 2.6.18 + sha256: 8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737 requires_dist: - ukkonen ; extra == 'license' requires_python: '>=3.10' @@ -5622,18 +5845,16 @@ packages: - pytest>=8.3.2 ; extra == 'all' - flake8>=7.1.1 ; extra == 'all' requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl name: importlib-metadata - version: 8.7.1 - sha256: 5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151 + version: 9.0.0 + sha256: 2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7 requires_dist: - zipp>=3.20 - pytest>=6,!=8.1.* ; extra == 'test' - packaging ; extra == 'test' - pyfakefs ; extra == 'test' - - flufl-flake8 ; extra == 'test' - pytest-perf>=0.9.2 ; extra == 'test' - - jaraco-test>=5.4 ; extra == 'test' - sphinx>=3.5 ; extra == 'doc' - jaraco-packaging>=9.3 ; extra == 'doc' - rst-linker>=1.9 ; extra == 'doc' @@ -5641,13 +5862,12 @@ packages: - sphinx-lint ; extra == 'doc' - jaraco-tidelift>=1.4 ; extra == 'doc' - ipython ; extra == 'perf' - - pytest-checkdocs>=2.4 ; extra == 'check' + - pytest-checkdocs>=2.14 ; extra == 'check' - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check' - pytest-cov ; extra == 'cover' - pytest-enabler>=3.4 ; extra == 'enabler' - - pytest-mypy>=1.0.1 ; extra == 'type' - - mypy<1.19 ; platform_python_implementation == 'PyPy' and extra == 'type' - requires_python: '>=3.9' + - pytest-mypy>=1.0.1 ; platform_python_implementation != 'PyPy' and extra == 'type' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl name: iniconfig version: 2.3.0 @@ -5696,23 +5916,23 @@ packages: - pytest-cov ; extra == 'test' - nbval>=0.9.2 ; extra == 'test' requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl name: ipykernel - version: 6.31.0 - sha256: abe5386f6ced727a70e0eb0cf1da801fa7c5fa6ff82147747d5a0406cd8c94af + version: 7.2.0 + sha256: 3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661 requires_dist: - appnope>=0.1.2 ; sys_platform == 'darwin' - comm>=0.1.1 - debugpy>=1.6.5 - ipython>=7.23.1 - - jupyter-client>=8.0.0 - - jupyter-core>=4.12,!=5.0.* + - jupyter-client>=8.8.0 + - jupyter-core>=5.1,!=6.0.* - matplotlib-inline>=0.1 - nest-asyncio>=1.4 - packaging>=22 - psutil>=5.7 - pyzmq>=25 - - tornado>=6.2 + - tornado>=6.4.1 - traitlets>=5.4.0 - coverage[toml] ; extra == 'cov' - matplotlib ; extra == 'cov' @@ -5721,8 +5941,8 @@ packages: - intersphinx-registry ; extra == 'docs' - myst-parser ; extra == 'docs' - pydata-sphinx-theme ; extra == 'docs' - - sphinx ; extra == 'docs' - sphinx-autodoc-typehints ; extra == 'docs' + - sphinx<8.2.0 ; extra == 'docs' - sphinxcontrib-github-alt ; extra == 'docs' - sphinxcontrib-spelling ; extra == 'docs' - trio ; extra == 'docs' @@ -5734,8 +5954,8 @@ packages: - pytest-asyncio>=0.23.5 ; extra == 'test' - pytest-cov ; extra == 'test' - pytest-timeout ; extra == 'test' - - pytest>=7.0,<9 ; extra == 'test' - requires_python: '>=3.9' + - pytest>=7.0,<10 ; extra == 'test' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl name: ipython version: 9.10.0 @@ -5906,16 +6126,25 @@ packages: - markupsafe>=2.0 - babel>=2.7 ; extra == 'i18n' requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl + name: jinja2-ansible-filters + version: 1.3.2 + sha256: e1082f5564917649c76fed239117820610516ec10f87735d0338688800a55b34 + requires_dist: + - jinja2 + - pyyaml + - pytest ; extra == 'test' + - pytest-cov ; extra == 'test' - pypi: https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl name: json5 version: 0.13.0 sha256: 9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc requires_python: '>=3.8.0' -- pypi: https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl name: jsonpointer - version: 3.0.0 - sha256: 13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942 - requires_python: '>=3.7' + version: 3.1.1 + sha256: 8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl name: jsonschema version: 4.26.0 @@ -6106,10 +6335,10 @@ packages: - pytest-timeout ; extra == 'test' - pytest>=7.0 ; extra == 'test' requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/b9/52/372d3494766d690dfdd286871bf5f7fb9a6c61f7566ccaa7153a163dd1df/jupyterlab-4.5.5-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl name: jupyterlab - version: 4.5.5 - sha256: a35694a40a8e7f2e82f387472af24e61b22adcce87b5a8ab97a5d9c486202a6d + version: 4.5.6 + sha256: d6b3dac883aa4d9993348e0f8e95b24624f75099aed64eab6a4351a9cdd1e580 requires_dist: - async-lru>=1.0.0 - httpx>=0.25.0,<1 @@ -6360,9 +6589,9 @@ packages: - changelist==0.5 ; extra == 'dev' - spin==0.15 ; extra == 'dev' requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda - sha256: 565941ac1f8b0d2f2e8f02827cbca648f4d18cd461afc31f15604cd291b5c5f3 - md5: 12bd9a3f089ee6c9266a37dab82afabd +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda + sha256: 3d584956604909ff5df353767f3a2a2f60e07d070b328d109f30ac40cd62df6c + md5: 18335a698559cdbcd86150a48bf54ba6 depends: - __glibc >=2.17,<3.0.a0 - zstd >=1.5.7,<1.6.0a0 @@ -6371,8 +6600,8 @@ packages: license: GPL-3.0-only license_family: GPL purls: [] - size: 725507 - timestamp: 1770267139900 + size: 728002 + timestamp: 1774197446916 - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda sha256: a7a4481a4d217a3eadea0ec489826a69070fcc3153f00443aa491ed21527d239 md5: 6f7b4302263347698fd24565fbf11310 @@ -6645,26 +6874,26 @@ packages: purls: [] size: 68079 timestamp: 1765819124349 -- conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.0-h19cb2f5_1.conda - sha256: fa002b43752fe5860e588435525195324fe250287105ebd472ac138e97de45e6 - md5: 836389b6b9ae58f3fbcf7cafebd5c7f2 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda + sha256: 46561199545890e050a8a90edcfce984e5f881da86b09388926e3a6c6b759dec + md5: ed6f7b7a35f942a0301e581d72616f7d depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] - size: 570141 - timestamp: 1772001147762 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.0-h55c6f16_1.conda - sha256: ce1049fa6fda9cf08ff1c50fb39573b5b0ea6958375d8ea7ccd8456ab81a0bcb - md5: e9c56daea841013e7774b5cd46f41564 + size: 564908 + timestamp: 1774439353713 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.2-h55c6f16_0.conda + sha256: d1402087c8792461bfc081629e8aa97e6e577a31ae0b84e6b9cc144a18f48067 + md5: 4280e0a7fd613b271e022e60dea0138c depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] - size: 568910 - timestamp: 1772001095642 + size: 568094 + timestamp: 1774439202359 - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 md5: 172bf1cd1ff8629f2b1179945ed45055 @@ -7034,55 +7263,55 @@ packages: purls: [] size: 89411 timestamp: 1769482314283 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda - sha256: a4a7dab8db4dc81c736e9a9b42bdfd97b087816e029e221380511960ac46c690 - md5: b499ce4b026493a13774bcf0f4c33849 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda + sha256: 663444d77a42f2265f54fb8b48c5450bfff4388d9c0f8253dd7855f0d993153f + md5: 2a45e7f8af083626f009645a6481f12d depends: - __glibc >=2.17,<3.0.a0 - - c-ares >=1.34.5,<2.0a0 + - c-ares >=1.34.6,<2.0a0 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.2,<4.0a0 + - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] - size: 666600 - timestamp: 1756834976695 -- conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda - sha256: c48d7e1cc927aef83ff9c48ae34dd1d7495c6ccc1edc4a3a6ba6aff1624be9ac - md5: e7630cef881b1174d40f3e69a883e55f + size: 663344 + timestamp: 1773854035739 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda + sha256: 899551e16aac9dfb85bfc2fd98b655f4d1b7fea45720ec04ccb93d95b4d24798 + md5: dba4c95e2fe24adcae4b77ebf33559ae depends: - - __osx >=10.13 - - c-ares >=1.34.5,<2.0a0 + - __osx >=11.0 + - c-ares >=1.34.6,<2.0a0 - libcxx >=19 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.2,<4.0a0 + - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] - size: 605680 - timestamp: 1756835898134 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - sha256: a07cb53b5ffa2d5a18afc6fd5a526a5a53dd9523fbc022148bd2f9395697c46d - md5: a4b4dd73c67df470d091312ab87bf6ae + size: 606749 + timestamp: 1773854765508 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda + sha256: 2bc7bc3978066f2c274ebcbf711850cc9ab92e023e433b9631958a098d11e10a + md5: 6ea18834adbc3b33df9bd9fb45eaf95b depends: - __osx >=11.0 - - c-ares >=1.34.5,<2.0a0 + - c-ares >=1.34.6,<2.0a0 - libcxx >=19 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libzlib >=1.3.1,<2.0a0 - - openssl >=3.5.2,<4.0a0 + - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] - size: 575454 - timestamp: 1756835746393 + size: 576526 + timestamp: 1773854624224 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 md5: d864d34357c3b65a4b731f78c0801dc4 @@ -7295,98 +7524,97 @@ packages: purls: [] size: 520078 timestamp: 1772704728534 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 - md5: edb0dca6bc32e4f4789199455a1dbeb8 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda + sha256: 55044c403570f0dc26e6364de4dc5368e5f3fc7ff103e867c487e2b5ab2bcda9 + md5: d87ff7921124eccd67248aa483c23fec depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=13 constrains: - - zlib 1.3.1 *_2 + - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] - size: 60963 - timestamp: 1727963148474 -- conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - sha256: 8412f96504fc5993a63edf1e211d042a1fd5b1d51dedec755d2058948fcced09 - md5: 003a54a4e32b02f7355b50a837e699da + size: 63629 + timestamp: 1774072609062 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda + sha256: 4c6da089952b2d70150c74234679d6f7ac04f4a98f9432dec724968f912691e7 + md5: 30439ff30578e504ee5e0b390afc8c65 depends: - - __osx >=10.13 + - __osx >=11.0 constrains: - - zlib 1.3.1 *_2 + - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] - size: 57133 - timestamp: 1727963183990 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b - md5: 369964e85dc26bfe78f41399b366c435 + size: 59000 + timestamp: 1774073052242 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda + sha256: 361415a698514b19a852f5d1123c5da746d4642139904156ddfca7c922d23a05 + md5: bc5a5721b6439f2f62a84f2548136082 depends: - __osx >=11.0 constrains: - - zlib 1.3.1 *_2 + - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] - size: 46438 - timestamp: 1727963202283 -- conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - sha256: ba945c6493449bed0e6e29883c4943817f7c79cbff52b83360f7b341277c6402 - md5: 41fbfac52c601159df6c01f875de31b9 + size: 47759 + timestamp: 1774072956767 +- conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda + sha256: 88609816e0cc7452bac637aaf65783e5edf4fee8a9f8e22bdc3a75882c536061 + md5: dbabbd6234dea34040e631f87676292f depends: - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 constrains: - - zlib 1.3.1 *_2 + - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] - size: 55476 - timestamp: 1727963768015 -- conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.0-h0d3cbff_0.conda - sha256: b63df4e592b3362e7d13e3d1cf8e55ce932ff4f17611c8514b5d36368ec2094c - md5: 3921780bab286f2439ba483c22b90345 + size: 58347 + timestamp: 1774072851498 +- conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.1-h0d3cbff_0.conda + sha256: 65c30298a921b3f6d49e2f4ec220bc2d2237cbdabd1c19031f4fafbfdad8aa5e + md5: d5e67fb9aeb3f32fc474ca7859a5583b depends: - __osx >=11.0 constrains: - - openmp 22.1.0|22.1.0.* - intel-openmp <0.0a0 + - openmp 22.1.1|22.1.1.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] - size: 311938 - timestamp: 1772024731611 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.0-hc7d1edf_0.conda - sha256: 0daeedb3872ad0fdd6f0d7e7165c63488e8a315d7057907434145fba0c1e7b3d - md5: ff0820b5588b20be3b858552ecf8ffae + size: 311088 + timestamp: 1774349643537 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.1-hc7d1edf_0.conda + sha256: c6f67e928f47603aca7e4b83632d8f3e82bd698051c7c0b34fcce3796eb9b63c + md5: 5a44f53783d87427790fc8692542f1bb depends: - __osx >=11.0 constrains: - - openmp 22.1.0|22.1.0.* - intel-openmp <0.0a0 + - openmp 22.1.1|22.1.1.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] - size: 285558 - timestamp: 1772028716784 -- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.0-h4fa8253_0.conda - sha256: bb55a3736380759d338f87aac68df4fd7d845ae090b94400525f5d21a55eea31 - md5: e5505e0b7d6ef5c19d5c0c1884a2f494 + size: 285912 + timestamp: 1774349644882 +- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + sha256: 64c7fe6490583f3c49c36c2f413e681072102db8abea13a3e1832f44eaf55518 + md5: d9f479404fe316e575f4a4575f3df406 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - openmp 22.1.0|22.1.0.* + - openmp 22.1.1|22.1.1.* - intel-openmp <0.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] - size: 347404 - timestamp: 1772025050288 + size: 347138 + timestamp: 1774349485844 - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl name: lmfit version: 1.3.4 @@ -7785,22 +8013,22 @@ packages: - markupsafe>=2.0.1 - mkdocs>=1.1 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/ca/fa/ab291bbb8f7453f8449a982843b044b37050c85f5895ef97f599683a1249/mkdocs_get_deps-0.2.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl name: mkdocs-get-deps - version: 0.2.1 - sha256: 07d6076298715dfcb8232e7dec083d09015b4e65482ce7f6743cb07cd1da847e + version: 0.2.2 + sha256: e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650 requires_dist: - importlib-metadata>=4.3 ; python_full_version < '3.10' - mergedeep>=1.3.4 - platformdirs>=2.2.0 - pyyaml>=5.1 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/93/89/eb601278b12c471235860992f5973cf3c55ca3f77d1d6127389eb045a021/mkdocs_jupyter-0.26.1-py3-none-any.whl name: mkdocs-jupyter - version: 0.25.1 - sha256: 3f679a857609885d322880e72533ef5255561bbfdb13cfee2a1e92ef4d4ad8d8 + version: 0.26.1 + sha256: 527242c2c8f1d30970764bbab752de41243e5703f458d8bc05336ec53828192e requires_dist: - - ipykernel>6.0.0,<7.0.0 + - ipykernel>6.0.0,<8 - jupytext>1.13.8,<2 - mkdocs-material>9.0.0 - mkdocs>=1.4.0,<2 @@ -7815,10 +8043,10 @@ packages: - mkdocs - pyyaml requires_python: '>=3.6' -- pypi: https://files.pythonhosted.org/packages/e7/94/e3535a9ed078b238df3df75a44694ca0ff5772fd538df4939c658a58c59d/mkdocs_material-9.7.4-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl name: mkdocs-material - version: 9.7.4 - sha256: 6549ad95e4d130ed5099759dfa76ea34c593eefdb9c18c97273605518e99cfbf + version: 9.7.6 + sha256: 71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba requires_dist: - babel>=2.10 - backrefs>=5.7.post1 @@ -7826,7 +8054,7 @@ packages: - jinja2>=3.1 - markdown>=3.2 - mkdocs-material-extensions>=1.3 - - mkdocs>=1.6 + - mkdocs>=1.6,<2 - paginate>=0.5 - pygments>=2.16 - pymdown-extensions>=10.2 @@ -7876,11 +8104,11 @@ packages: - griffelib>=2.0 - typing-extensions>=4.0 ; python_full_version < '3.11' requires_python: '>=3.10' -- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_455.conda - sha256: b2b4c84b95210760e4d12319416c60ab66e03674ccdcbd14aeb59f82ebb1318d - md5: fd05d1e894497b012d05a804232254ed +- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda + sha256: f2c2b2a3c2e7d08d78c10bef7c135a4262c80d1d48c85fb5902ca30d61d645f4 + md5: 3fd3009cef89c36e9898a6feeb0f5530 depends: - - llvm-openmp >=21.1.8 + - llvm-openmp >=22.1.1 - tbb >=2022.3.0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 @@ -7888,8 +8116,8 @@ packages: license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary purls: [] - size: 100224829 - timestamp: 1767634557029 + size: 99997309 + timestamp: 1774449747739 - pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl name: mpld3 version: 0.5.12 @@ -8087,10 +8315,10 @@ packages: requires_dist: - typing-extensions>=4.1.0 ; python_full_version < '3.11' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/4b/27/20770bd6bf8fbe1e16f848ba21da9df061f38d2e6483952c29d2bb5d1d8b/narwhals-2.17.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/3f/c3/06490e98393dcb4d6ce2bf331a39335375c300afaef526897881fbeae6ab/narwhals-2.18.1-py3-none-any.whl name: narwhals - version: 2.17.0 - sha256: 2ac5307b7c2b275a7d66eeda906b8605e3d7a760951e188dcfff86e8ebe083dd + version: 2.18.1 + sha256: a0a8bb80205323851338888ba3a12b4f65d352362c8a94be591244faf36504ad requires_dist: - cudf-cu12>=24.10.0 ; extra == 'cudf' - dask[dataframe]>=2024.8 ; extra == 'dask' @@ -9614,11 +9842,6 @@ packages: - trove-classifiers>=2024.10.12 ; extra == 'tests' - defusedxml ; extra == 'xmp' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/de/f0/c81e05b613866b76d2d1066490adf1a3dbc4ee9d9c839961c3fc8a6997af/pip-26.0.1-py3-none-any.whl - name: pip - version: 26.0.1 - sha256: bdb1b08f4274833d62c1aa29e20907365a2ceb950410df15fc9521bad440122b - requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl name: pixi-kernel version: 0.7.1 @@ -9636,10 +9859,10 @@ packages: version: 4.9.4 sha256: 68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/ab/f2/11ebc9b4cc8aaa23423a082c738b27792b88ebe0a8a6590ca6993c09a258/plopp-26.3.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/31/8b/9e8baf7dacac8d0c174925c38ff43c6d94bc9abb35503f67762caccb6869/plopp-26.3.1-py3-none-any.whl name: plopp - version: 26.3.0 - sha256: 44e231f202e8e9bfba4556762e7a49ec4832c5c1df0c96a744f87e7d50fddc11 + version: 26.3.1 + sha256: 56531f2f71fa4f7f33c172312d2423d969deb9b9dd29b2524ad3ed7e33d220eb requires_dist: - lazy-loader>=0.4 - matplotlib>=3.8 @@ -9718,6 +9941,14 @@ packages: - pytest-benchmark ; extra == 'testing' - coverage ; extra == 'testing' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl + name: plumbum + version: 1.10.0 + sha256: 9583d737ac901c474d99d030e4d5eec4c4e6d2d7417b1cf49728cf3be34f6dc8 + requires_dist: + - pywin32 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32' + - paramiko ; extra == 'ssh' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl name: ply version: '3.11' @@ -10156,6 +10387,16 @@ packages: requires_dist: - typing-extensions>=4.14.1 requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl + name: pydoclint + version: 0.8.3 + sha256: 5fc9b82d0d515afce0908cb70e8ff695a68b19042785c248c4f227ad66b4a164 + requires_dist: + - click>=8.1.0 + - docstring-parser-fork>=0.0.12 + - tomli>=2.0.1 ; python_full_version < '3.11' + - flake8>=4 ; extra == 'flake8' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl name: pygments version: 2.19.2 @@ -10205,10 +10446,10 @@ packages: - setuptools ; extra == 'dev' - xmlschema ; extra == 'dev' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl name: pytest-cov - version: 7.0.0 - sha256: 3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 + version: 7.1.0 + sha256: a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 requires_dist: - coverage[toml]>=7.10.6 - pluggy>=1.2 @@ -10427,10 +10668,10 @@ packages: requires_dist: - six>=1.5 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' -- pypi: https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl name: python-discovery - version: 1.1.2 - sha256: d18edd61b382d62f8bcd004a71ebaabc87df31dbefb30aeed59f4fc6afa005be + version: 1.2.0 + sha256: 1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a requires_dist: - filelock>=3.15.4 - platformdirs>=4.3.6,<5 @@ -10529,6 +10770,14 @@ packages: - pytest-check-links ; extra == 'test' - numpy>=1.14 ; extra == 'test' requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl + name: pywin32 + version: '311' + sha256: 3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503 +- pypi: https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl + name: pywin32 + version: '311' + sha256: 718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d - pypi: https://files.pythonhosted.org/packages/79/c3/3e75075c7f71735f22b66fab0481f2c98e3a4d58cba55cb50ba29114bcf6/pywinpty-3.0.3-cp311-cp311-win_amd64.whl name: pywinpty version: 3.0.3 @@ -10628,6 +10877,13 @@ packages: requires_dist: - cffi ; implementation_name == 'pypy' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl + name: questionary + version: 2.1.1 + sha256: a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59 + requires_dist: + - prompt-toolkit>=2.0,<4.0 + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/93/f7/d00d9b4a0313a6be3a3e0818e6375e15da6d7076f4ae47d1324e7ca986a1/radon-6.0.1-py2.py3-none-any.whl name: radon version: 6.0.1 @@ -10680,18 +10936,24 @@ packages: - rpds-py>=0.7.0 - typing-extensions>=4.4.0 ; python_full_version < '3.13' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl name: requests - version: 2.32.5 - sha256: 2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 + version: 2.33.0 + sha256: 3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b requires_dist: - charset-normalizer>=2,<4 - idna>=2.5,<4 - - urllib3>=1.21.1,<3 - - certifi>=2017.4.17 + - urllib3>=1.26,<3 + - certifi>=2023.5.7 - pysocks>=1.5.6,!=1.5.7 ; extra == 'socks' - - chardet>=3.0.2,<6 ; extra == 'use-chardet-on-py3' - requires_python: '>=3.9' + - chardet>=3.0.2,<8 ; extra == 'use-chardet-on-py3' + - pytest-httpbin==2.1.0 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-xdist ; extra == 'test' + - pysocks>=1.5.6,!=1.5.7 ; extra == 'test' + - pytest>=3 ; extra == 'test' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/57/4d/a7545bf6c62b0dbe5795f22ea9e88cc070fdced5c34663ebc5bed2f610c0/returns-0.26.0-py3-none-any.whl name: returns version: 0.26.0 @@ -10771,25 +11033,25 @@ packages: version: 0.30.0 sha256: e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl name: ruff - version: 0.15.5 - sha256: 6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080 + version: 0.15.7 + sha256: 4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl name: ruff - version: 0.15.5 - sha256: 821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe + version: 0.15.7 + sha256: 722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl name: ruff - version: 0.15.5 - sha256: 89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010 + version: 0.15.7 + sha256: 7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: ruff - version: 0.15.5 - sha256: c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a + version: 0.15.7 + sha256: dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5 requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl name: sciline @@ -10808,10 +11070,10 @@ packages: - rich ; extra == 'test' - rich ; extra == 'progress' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/12/0d/3f98a936a30bff4a460b51b9f85c4d994f94249930b2d8bedeb8111a359e/scipp-25.12.0-cp311-cp311-macosx_11_0_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/2e/75/5604f4d17ab607510d4702f156329194d8edfff7e29644ca9200b085e9a2/scipp-26.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl name: scipp - version: 25.12.0 - sha256: 6391dc46739006e1e4eb7f2fcbcdbdd40f11a3cbae53e93b63b5ba32909bd792 + version: 26.3.1 + sha256: 993706e7c31f0317be2db5f528f9142ba67b2e52d7af174fcad195f702e1d6c7 requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -10834,10 +11096,10 @@ packages: - nodejs ; extra == 'all' - pythreejs ; extra == 'all' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/18/5c/bc0ca94bff65fe0d206a369b54625f8ec7852dfd1d835174692026f34df9/scipp-25.12.0-cp313-cp313-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/37/fd/22621d3ee9e3ee87ef4c89b63bba55b265ab85039b3c1ba88ed2380a24c1/scipp-26.3.1-cp313-cp313-win_amd64.whl name: scipp - version: 25.12.0 - sha256: 0285c91b202dea9aeb18f29d73ff135208a19b2068cd30e17ee81fc435b1943c + version: 26.3.1 + sha256: 03c6dbf8936a2ed62587c5abe8ab5266a5098834a0709321ce799bd1328eb3e6 requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -10860,10 +11122,10 @@ packages: - nodejs ; extra == 'all' - pythreejs ; extra == 'all' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/4e/b6/ffe0bb67cec66cd450acff599bb07507bbf5ffda1a3a15dd2d8dbe7a6da7/scipp-25.12.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/60/54/5011adb56853caabfd90686c2e543d1e3c76a8ef2755809b7e12e3f3583b/scipp-26.3.1-cp311-cp311-macosx_14_0_arm64.whl name: scipp - version: 25.12.0 - sha256: 9ec0200eeb660965056b27f5f05505a961d8921b78d0a92c04743d1c22ce7203 + version: 26.3.1 + sha256: 67d275fc83b062053df9aa7ce3af4d2205109c2bc3ab22467bcd73ceb0a83df2 requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -10886,10 +11148,10 @@ packages: - nodejs ; extra == 'all' - pythreejs ; extra == 'all' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/6a/3a/ab0eb61593569d5a0d080002e4b8c0998cb1116d8710781b7225c304b880/scipp-25.12.0-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/79/fe/b14d806894cf05178f1e77d0d619f071db50cf698bc654c54f9241223bcf/scipp-26.3.1-cp313-cp313-macosx_14_0_arm64.whl name: scipp - version: 25.12.0 - sha256: e27082f5bd3655f15479ce87be495235b9bcd9b5db654a7219261be0c6117b31 + version: 26.3.1 + sha256: 8dfe8adedb5cba05acaaea15e3b6fe1820ac2f497e87c1e581ba4be9d82c53bb requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -10912,10 +11174,10 @@ packages: - nodejs ; extra == 'all' - pythreejs ; extra == 'all' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/94/98/aa2d4b9d28969cc7f62409f9f9fc5b5a853af651255eba03e9bee8546dd9/scipp-25.12.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/81/21/4962b1daddf0422e56c5ed4c41bea1ccb6d2a9ab72b795196835a20969c7/scipp-26.3.1-cp311-cp311-macosx_14_0_x86_64.whl name: scipp - version: 25.12.0 - sha256: e5694bef45299c4813ee2fe863fab600c3b0f5e13e2735072dbbb5cf1804d0e0 + version: 26.3.1 + sha256: 7c90e78fcba1d272df059fc01350c9e18f017aec26369b03def723a3702d763d requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -10938,10 +11200,10 @@ packages: - nodejs ; extra == 'all' - pythreejs ; extra == 'all' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/bd/75/6a3786de6645ac2ccd94fbf83c59cc6b929bfa3a89cb62c8cb3be4de0606/scipp-25.12.0-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/d4/06/19ff1efd58b85906149ce83dfddce23252cea5bec7e0fa5f834336cfe836/scipp-26.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl name: scipp - version: 25.12.0 - sha256: aee5f585232e2a7a664f57bb9695164715271b74896704e7ee8a669dd7b06008 + version: 26.3.1 + sha256: ab5859a24b3150b588dd2c67e68b0c7f07c9444eae501f3b6326d6b4a34cbf10 requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -10964,10 +11226,10 @@ packages: - nodejs ; extra == 'all' - pythreejs ; extra == 'all' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/e5/22/75e119e0a200914f88f121cd956e1eb7f72c8ace4b63171f52ba0334d142/scipp-25.12.0-cp313-cp313-macosx_11_0_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/e2/69/1dcb8e967f62759578938db5b29792b82ea8939a2d712e79491fa3e1cf0a/scipp-26.3.1-cp313-cp313-macosx_14_0_x86_64.whl name: scipp - version: 25.12.0 - sha256: 27ebc37f3b7e20c9dca9cf1a0ac53c4ffaea31c02dfc8ba2b5aa008a252cbcba + version: 26.3.1 + sha256: 4c9c8632ba24ce74bd98430a1376310fa5b3fcd2c3467a7e6a484ebb091e915f requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -10990,10 +11252,10 @@ packages: - nodejs ; extra == 'all' - pythreejs ; extra == 'all' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/eb/d1/b3cd2733a96a36c54c36889b2cfdd0331c1e5b57fa1757485a22d0ec3142/scipp-25.12.0-cp313-cp313-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/e6/0d/8882a4c7a5ebe59a46b709e82411d9c730d67250d41a2e11bc4bcd4d431d/scipp-26.3.1-cp311-cp311-win_amd64.whl name: scipp - version: 25.12.0 - sha256: 015db5035749750cf026db56fe537af10f159dc3cd0f51f0ea9f4ecc3a7a5da8 + version: 26.3.1 + sha256: 37877cf07b4f54f224d5465c265d6a1e591d605d0c23dd350a4b48d95c26ab7b requires_dist: - numpy>=2 - pytest ; extra == 'test' @@ -11494,11 +11756,38 @@ packages: version: 1.17.0 sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl + name: smmap + version: 5.0.3 + sha256: c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl name: soupsieve version: 2.8.3 sha256: ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95 requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/3e/17/1f31d8562e6f970d64911f1abc330d233bc0c0601411cf7e19c1292be6da/spdx_headers-1.5.1-py3-none-any.whl + name: spdx-headers + version: 1.5.1 + sha256: 73bcb1ed087824b55ccaa497d03d8f0f0b0eaf30e5f0f7d5bbd29d2c4fe78fcf + requires_dist: + - chardet>=5.2.0 + - requests>=2.32.3 + - black>=23.0.0 ; extra == 'dev' + - build>=0.10.0 ; extra == 'dev' + - hatch>=1.9.0 ; extra == 'dev' + - isort>=5.12.0 ; extra == 'dev' + - mypy>=1.0.0 ; extra == 'dev' + - pre-commit>=4.3.0 ; extra == 'dev' + - pytest-cov>=4.0.0 ; extra == 'dev' + - pytest>=7.0.0 ; extra == 'dev' + - ruff>=0.5.0 ; extra == 'dev' + - twine>=4.0.0 ; extra == 'dev' + - types-requests>=2.31.0.6 ; extra == 'dev' + - pytest-cov>=4.0.0 ; extra == 'test' + - pytest-mock>=3.10.0 ; extra == 'test' + - pytest>=7.0.0 ; extra == 'test' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/16/56/a31e8d3c9e8d21100b83bbe1c1f3f7c94db317393a229e193461e5e6d2a4/spglib-2.6.0-cp313-cp313-win_amd64.whl name: spglib version: 2.6.0 @@ -12091,10 +12380,10 @@ packages: purls: [] size: 3526350 timestamp: 1769460339384 -- pypi: https://files.pythonhosted.org/packages/7c/b9/ac773f87400bce0637b5f87bb272d0b347c91d7063bff09fb7055b65dd2f/tof-26.1.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl name: tof - version: 26.1.0 - sha256: 2603a7b94a7296ee503a0edadb314701431808c8ededb5c442334f89633962a5 + version: 26.3.0 + sha256: e89783a072b05fdb53d9e76fbf919dc8935e75e118fdaf17ca5cc33727ef002b requires_dist: - plopp>=23.10.0 - pooch>=1.5.0 @@ -12153,25 +12442,25 @@ packages: version: 1.1.0 sha256: 15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl name: tornado - version: 6.5.4 - sha256: e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f + version: 6.5.5 + sha256: 6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl +- pypi: https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl name: tornado - version: 6.5.4 - sha256: d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9 + version: 6.5.5 + sha256: 487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl name: tornado - version: 6.5.4 - sha256: fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc + version: 6.5.5 + sha256: 65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl name: tornado - version: 6.5.4 - sha256: 2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843 + version: 6.5.5 + sha256: e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5 requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl name: traitlets @@ -12310,26 +12599,6 @@ packages: - pysocks>=1.5.6,!=1.5.7,<2.0 ; extra == 'socks' - backports-zstd>=1.0.0 ; python_full_version < '3.14' and extra == 'zstd' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/6f/34/2e5cd576d312eb1131b615f49ee95ff6efb740965324843617adae729cf2/uv-0.10.9-py3-none-macosx_10_12_x86_64.whl - name: uv - version: 0.10.9 - sha256: 880dd4cffe4bd184e8871ddf4c7d3c3b042e1f16d2682310644aa8d61eaea3e6 - requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/79/34/b104c413079874493eed7bf11838b47b697cf1f0ed7e9de374ea37b4e4e0/uv-0.10.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - name: uv - version: 0.10.9 - sha256: 7c9d6deb30edbc22123be75479f99fb476613eaf38a8034c0e98bba24a344179 - requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/89/35/684f641de4de2b20db7d2163c735b2bb211e3b3c84c241706d6448e5e868/uv-0.10.9-py3-none-macosx_11_0_arm64.whl - name: uv - version: 0.10.9 - sha256: a7a784254380552398a6baf4149faf5b31a4003275f685c28421cf8197178a08 - requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/c9/e9/adf7a12136573937d12ac189569e2e90e7fad18b458192083df6986f3013/uv-0.10.9-py3-none-win_amd64.whl - name: uv - version: 0.10.9 - sha256: af79552276d8bd622048ab2d67ec22120a6af64d83963c46b1482218c27b571f - requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl name: validate-pyproject version: '0.25' diff --git a/pixi.toml b/pixi.toml index c0a8608e..f94cc0ce 100644 --- a/pixi.toml +++ b/pixi.toml @@ -27,6 +27,7 @@ PYTHONPATH = "${PIXI_PROJECT_ROOT}/src;%PYTHONPATH%" ########### [workspace] + # Supported platforms for the lock file (pixi.lock) platforms = ['win-64', 'linux-64', 'osx-64', 'osx-arm64'] @@ -34,7 +35,7 @@ platforms = ['win-64', 'linux-64', 'osx-64', 'osx-arm64'] channels = ['conda-forge'] ##################### -# System requirements +# SYSTEM REQUIREMENTS ##################### [system-requirements] @@ -52,33 +53,22 @@ macos = '14.0' # Default feature configuration [dependencies] -gsl = '*' # GNU Scientific Library; required for pdffit2. - -#[target.win-64.dependencies] -#libcblas = '*' # CBLAS library for linear algebra; required for pdffit2. +nodejs = '*' # Required for Prettier (non-Python formatting) +gsl = '*' # GNU Scientific Library; required for pdffit2 [pypi-dependencies] # == [feature.default.pypi-dependencies] -pip = '*' # Native package installer -uv = '*' # Package manager -jupyterlab = '*' # Jupyter notebooks -pixi-kernel = '*' # Pixi Jupyter kernel -#easydiffraction = { version = '*', extras = ['all'] } # Main package -easydiffraction = { path = ".", editable = true, extras = ['all'] } +#pip = '*' # Native package installer +easydiffraction = { path = ".", editable = true, extras = ['dev'] } -# Specific features +# Specific features: Set specific Python versions -# Each feature sets a specific Python version for the environment. -[feature.py311.dependencies] +[feature.py-min.dependencies] python = '3.11.*' - -[feature.py313.dependencies] +[feature.py-max.dependencies] python = '3.13.*' -# This feature installs Node.js for formatting non-Python files with Prettier. -[feature.nodejs.dependencies] -nodejs = '*' ############## # ENVIRONMENTS @@ -88,13 +78,12 @@ nodejs = '*' # The `default` feature is always included in all environments. # Additional features can be specified per environment. +py-311-env = { features = ['default', 'py-min'] } +py-313-env = { features = ['default', 'py-max'] } # The `default` environment is always created and includes the `default` feature. # It does not need to be specified explicitly unless non-default features are included. - -default = { features = ['py313', 'nodejs'] } -py311-dev = { features = ['py311', 'nodejs'] } -py313-dev = { features = ['py313', 'nodejs'] } +default = { features = ['default', 'py-max'] } ####### # TASKS @@ -102,59 +91,57 @@ py313-dev = { features = ['py313', 'nodejs'] } [tasks] -## 🧪 Testing Tasks +################## +# 🧪 Testing Tasks +################## + unit-tests = 'python -m pytest tests/unit/ --color=yes -v' integration-tests = 'python -m pytest tests/integration/ --color=yes -n auto -v' -notebook-tests = 'python -m pytest --nbmake tutorials/ --nbmake-timeout=600 --color=yes -n auto -v' +notebook-tests = 'python -m pytest --nbmake docs/docs/tutorials/ --nbmake-timeout=600 --color=yes -n auto -v' script-tests = 'python -m pytest tools/test_scripts.py --color=yes -n auto -v' -extra = 'python -m pytest tests/unit/extra.py -q --tb=no --disable-warnings --color=yes' test = { depends-on = ['unit-tests'] } -# 🧹 Code Quality +########### +# ✔️ Checks +########### -### ✔️ Checks pyproject-check = 'python -m validate_pyproject pyproject.toml' -py-lint-check-pre = "python -m ruff check" -py-lint-check = 'pixi run py-lint-check-pre .' -py-format-check-pre = "python -m ruff format --check" -py-format-check = "pixi run py-format-check-pre ." -nonpy-format-check-pre = "npx prettier --list-different --config=prettierrc.toml" -nonpy-format-check-modified = "pixi run nonpy-format-check-pre $(git diff --diff-filter=d --name-only HEAD | grep -E '\\.(json|ya?ml|toml|md|css|html)$' || echo .)" -nonpy-format-check = "pixi run nonpy-format-check-pre ." -notebook-format-check = 'nbqa ruff tutorials/' -docs-format-check = 'docformatter src/ tutorials/ --check' -# Run like a real commit: staged files only (almost) -pre-commit-check = 'pre-commit run --hook-stage pre-commit' -# CI check: lint/format everything -pre-commit-check-all = 'pre-commit run --all-files --hook-stage pre-commit' -# Pre-push check: lint/format everything -pre-push-check = 'pre-commit run --all-files --hook-stage pre-push' - -check = { depends-on = [ - 'docs-format-check', - 'py-format-check', - 'py-lint-check', - 'nonpy-format-check-modified', -] } +docstring-lint-check = 'pydoclint --quiet src/' +docstring-format-check = 'docformatter --check src/' +notebook-lint-check = 'nbqa ruff docs/docs/tutorials/' +py-lint-check = 'ruff check src/ tests/ docs/docs/tutorials/' +py-format-check = "ruff format --check src/ tests/ docs/docs/tutorials/" +nonpy-format-check = "npx prettier --list-different --config=prettierrc.toml --ignore-unknown ." +nonpy-format-check-modified = "python tools/nonpy_prettier_modified.py" -### 🛠️ Fixes -py-lint-fix = 'pixi run py-lint-check --fix' -#py-format-fix = "python -m ruff format $(git diff --cached --name-only -- '*.py')" -py-format-fix = "python -m ruff format" -nonpy-format-fix = 'pixi run nonpy-format-check --write' -nonpy-format-fix-modified = "pixi run nonpy-format-check-modified --write" -notebook-format-fix = 'pixi run notebook-format-check --fix' -docs-format-fix = 'docformatter src/ tutorials/ --in-place' +check = 'pre-commit run --hook-stage manual --all-files' + +########## +# 🛠️ Fixes +########## + +docs-format-fix = 'docformatter --in-place src/ docs/docs/tutorials/' +notebook-lint-fix = 'nbqa ruff --fix docs/docs/tutorials/' +py-lint-fix = 'ruff check --fix src/ tests/ docs/docs/tutorials/' +py-format-fix = "ruff format src/ tests/ docs/docs/tutorials/" +nonpy-format-fix = 'npx prettier --write --list-different --config=prettierrc.toml --ignore-unknown .' +nonpy-format-fix-modified = "python tools/nonpy_prettier_modified.py --write" +success-message-fix = 'echo "✅ All auto-formatting steps completed successfully!"' fix = { depends-on = [ 'py-format-fix', 'docs-format-fix', 'py-lint-fix', 'nonpy-format-fix', + 'notebook-lint-fix', + 'success-message-fix', ] } -## 🧮 Code Complexity +#################### +# 🧮 Code Complexity +#################### + complexity-check = 'radon cc -s src/' complexity-check-json = 'radon cc -s -j src/' maintainability-check = 'radon mi src/' @@ -162,62 +149,127 @@ maintainability-check-json = 'radon mi -j src/' raw-metrics = 'radon raw -s src/' raw-metrics-json = 'radon raw -s -j src/' -## 📊 Coverage +############# +# 📊 Coverage +############# + unit-tests-coverage = 'pixi run unit-tests --cov=src/easydiffraction --cov-report=term-missing' integration-tests-coverage = 'pixi run integration-tests --cov=src/easydiffraction --cov-report=term-missing' -docstring-coverage = 'interrogate -c pyproject.toml src/' +docstring-coverage = 'interrogate -c pyproject.toml src/easydiffraction' -cov = { depends-on = ['docstring-coverage', 'integration-tests-coverage'] } +cov = { depends-on = [ + 'docstring-coverage', + 'unit-tests-coverage', + 'integration-tests-coverage', +] } -## 📓 Notebook Management -notebook-convert = 'jupytext tutorials/*.py --from py:percent --to ipynb' -notebook-strip = 'nbstripout tutorials/*.ipynb' +######################## +# 📓 Notebook Management +######################## + +notebook-convert = 'jupytext docs/docs/tutorials/*.py --from py:percent --to ipynb' +notebook-strip = 'nbstripout docs/docs/tutorials/*.ipynb' notebook-tweak = 'python tools/tweak_notebooks.py tutorials/' -notebook-clean = 'rm -f tutorials/*.ipynb' -notebook-exec = 'python -m pytest --nbmake tutorials/ --nbmake-timeout=600 --overwrite --color=yes -n auto -v' +notebook-exec = 'python -m pytest --nbmake docs/docs/tutorials/ --nbmake-timeout=600 --overwrite --color=yes -n auto -v' notebook-prepare = { depends-on = [ - 'notebook-convert', + #'notebook-convert', 'notebook-strip', - 'notebook-tweak', + #'notebook-tweak', ] } -## 📚 Documentation Tasks -docs-assets = 'tools/add_assets_to_docs.sh' -docs-notebooks = 'mv tutorials/*.* docs/tutorials/' # *.py, *.ipynb, index.json -docs-config = 'python tools/create_mkdocs_yml.py' -docs-deploy = 'mike deploy --push --branch gh-pages --update-aliases --alias-type redirect' -docs-set-default = 'mike set-default --push --branch gh-pages' -docs-serve = "JUPYTER_PLATFORM_DIRS=1 PYTHONWARNINGS='ignore::RuntimeWarning' python -m mkdocs serve --dirty" -docs-build = "JUPYTER_PLATFORM_DIRS=1 PYTHONWARNINGS='ignore::RuntimeWarning' python -m mkdocs build" -docs-local = "pixi run docs-build --no-directory-urls" -docs-clean = 'tools/cleanup_docs.sh' -docs-setup = { depends-on = [ - 'docs-config', - 'docs-assets', - 'notebook-prepare', - 'docs-notebooks', +######################## +# 📚 Documentation Tasks +######################## + +docs-vars = "JUPYTER_PLATFORM_DIRS=1 PYTHONWARNINGS='ignore::RuntimeWarning'" +docs-pre = "pixi run docs-vars python -m mkdocs" +docs-serve = "pixi run docs-pre serve -f docs/mkdocs.yml" +docs-serve-dirty = "pixi run docs-serve --dirty" +docs-build = "pixi run docs-pre build -f docs/mkdocs.yml" +docs-build-local = "pixi run docs-build --no-directory-urls" + +docs-deploy-pre = 'mike deploy -F docs/mkdocs.yml --push --branch gh-pages --update-aliases --alias-type redirect' +docs-set-default-pre = 'mike set-default -F docs/mkdocs.yml --push --branch gh-pages' + +docs-update-assets = 'python tools/update_docs_assets.py' + +############################## +# 📦 Template Management Tasks +############################## + +copier-copy = "copier copy gh:easyscience/templates . --data-file ../diffraction/.copier-answers.yml --data template_type=lib" +copier-recopy = "copier recopy --data-file ../diffraction/.copier-answers.yml --data template_type=lib" +copier-update = "copier update --data-file ../diffraction/.copier-answers.yml --data template_type=lib" + +##################### +# 🪝 Pre-commit Hooks +##################### + +pre-commit-clean = 'pre-commit clean' +pre-commit-install = 'pre-commit install --hook-type pre-commit --hook-type pre-push --overwrite' +pre-commit-uninstall = 'pre-commit uninstall --hook-type pre-commit --hook-type pre-push' +pre-commit-setup = { depends-on = [ + 'pre-commit-clean', + 'pre-commit-uninstall', + 'pre-commit-install', +] } + +################# +# 🐙️ GitHub Tasks +################# + +repo-wiki = 'gh api -X PATCH repos/easyscience/diffraction-lib -f has_wiki=false' +repo-discussions = 'gh api -X PATCH repos/easyscience/diffraction-lib -f has_discussions=true' +repo-description = "gh api -X PATCH repos/easyscience/diffraction-lib -f description='Diffraction data analysis'" +repo-homepage = "gh api -X PATCH repos/easyscience/diffraction-lib -f homepage='https://easyscience.github.io/diffraction-lib'" +repo-config = { depends-on = [ + 'repo-wiki', + 'repo-discussions', + 'repo-description', + 'repo-homepage', +] } + +master-protection = 'gh api -X POST repos/easyscience/diffraction-lib/rulesets --input .github/configs/rulesets-master.json' +develop-protection = 'gh api -X POST repos/easyscience/diffraction-lib/rulesets --input .github/configs/rulesets-develop.json' +gh-pages-protection = 'gh api -X POST repos/easyscience/diffraction-lib/rulesets --input .github/configs/rulesets-gh-pages.json' +branch-protection = { depends-on = [ + 'master-protection', + 'develop-protection', + 'gh-pages-protection', ] } -## 🚀 Development & Build Tasks +pages-deployment = 'gh api -X POST repos/easyscience/diffraction-lib/pages --input .github/configs/pages-deployment.json' + +github-labels = 'python tools/update_github_labels.py' + +######################### +# ⚖️ SPDX License Headers +######################### + +license-remove = 'python tools/remove_license_headers.py src/ tests/' +license-add = 'python tools/add_license_headers.py src/ tests/' +license-check = 'python tools/check_license_headers.py src/ tests/' + +#################################### +# 🚀 Other Development & Build Tasks +#################################### + +default-build = 'python -m build' dist-build = 'python -m build --wheel --outdir dist' -spdx-update = 'python tools/update_spdx.py' -#dev-install = 'uv pip install --requirements pyproject.toml --extra all' -#dev-install = "python -m uv pip install --force-reinstall --editable '.[all]'" + npm-config = 'npm config set registry https://registry.npmjs.org/' prettier-install = 'npm install --no-save --no-audit --no-fund prettier prettier-plugin-toml' -pre-commit-setup = 'pre-commit clean && pre-commit uninstall && pre-commit install --hook-type pre-commit --hook-type pre-push --overwrite' -pre-commit-update = 'pre-commit autoupdate' + clean-pycache = "find . -type d -name '__pycache__' -prune -exec rm -rf '{}' +" -dev = { depends-on = [ - #'dev-install', +post-install = { depends-on = [ 'npm-config', 'prettier-install', #'pre-commit-setup', ] } -wheel = { depends-on = ['npm-config', 'prettier-install'] } - -## 🔗 Main Package Shortcut -easydiffraction = 'python -m easydiffraction' +########################## +# 🔗 Main Package Shortcut +########################## +easydiffraction = 'python -m easydiffraction' \ No newline at end of file diff --git a/prettierrc.toml b/prettierrc.toml index 6874d28d..b98c86eb 100644 --- a/prettierrc.toml +++ b/prettierrc.toml @@ -1,11 +1,22 @@ +plugins = [ + "prettier-plugin-toml", # use the TOML plugin +] + endOfLine = 'lf' # change line endings to LF -printWidth = 80 # wrap Markdown files at 80 characters proseWrap = 'always' # change wrapping in Markdown files semi = false # remove semicolons singleQuote = true # use single quotes instead of double quotes tabWidth = 2 # change tab width to 2 spaces useTabs = false # use spaces instead of tabs -plugins = [ - 'prettier-plugin-toml', # use the TOML plugin -] +printWidth = 79 # wrap lines at 79 characters + +[[overrides]] +files = ["*.md"] +[overrides.options] +printWidth = 72 # wrap Markdown files at 72 characters + +[[overrides]] +files = ["*.yml", "*.yaml"] +[overrides.options] +printWidth = 88 # wrap YAML files at 88 characters diff --git a/pyproject.toml b/pyproject.toml index dd645c71..9c4af972 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,9 +6,10 @@ name = 'easydiffraction' dynamic = ['version'] # Use versioningit to manage the version description = 'Diffraction data analysis' -authors = [{ name = 'EasyDiffraction contributors' }] +authors = [{ name = 'EasyScience contributors' }] readme = 'README.md' -license = { file = 'LICENSE' } +license = 'BSD-3-Clause' +license-files = ['LICENSE'] classifiers = [ 'Intended Audience :: Science/Research', 'Topic :: Scientific/Engineering', @@ -20,7 +21,7 @@ classifiers = [ 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', ] -requires-python = '>=3.11,<3.14' +requires-python = '>=3.11' dependencies = [ 'essdiffraction', # ESS-specific diffraction library 'numpy', # Numerical computing library @@ -43,29 +44,36 @@ dependencies = [ 'diffpy.utils', # Utilities for PDF calculations 'uncertainties', # Propagation of uncertainties 'typeguard', # Runtime type checking + 'darkdetect', # Detecting dark mode (system-level) + 'pandas', # Displaying tables in Jupyter notebooks + 'plotly', # Interactive plots + 'py3Dmol', # Visualisation of crystal structures + 'jupyterlab', # Jupyter notebooks + 'pixi-kernel', # Pixi Jupyter kernel ] [project.optional-dependencies] dev = [ - 'build', # Building the package - 'pre-commit', # Pre-commit hooks - 'jinja2', # Templating - 'nbmake', # Building notebooks - 'nbstripout', # Strip output from notebooks - 'nbqa', # Linting and formatting notebooks - 'pytest', # Testing - 'pytest-cov', # Test coverage - 'pytest-xdist', # Enable parallel testing - 'ruff', # Linting and formatting code - 'radon', # Code complexity and maintainability - 'validate-pyproject[all]', # Validate pyproject.toml - 'versioningit', # Automatic versioning from git tags - 'jupytext', # Jupyter notebook text format support - 'jupyterquiz', # Quizzes in Jupyter notebooks - 'docformatter', # Code formatter for docstrings - 'interrogate', # Check for missing docstrings -] -docs = [ + 'GitPython', # Interact with Git repositories + 'build', # Building the package + 'pre-commit', # Pre-commit hooks + 'jinja2', # Templating + 'nbmake', # Building notebooks + 'nbstripout', # Strip output from notebooks + 'nbqa', # Linting and formatting notebooks + 'pytest', # Testing + 'pytest-cov', # Test coverage + 'pytest-xdist', # Enable parallel testing + 'ruff', # Linting and formatting code + 'radon', # Code complexity and maintainability + 'validate-pyproject[all]', # Validate pyproject.toml + 'versioningit', # Automatic versioning from git tags + 'jupytext', # Jupyter notebook text format support + 'jupyterquiz', # Quizzes in Jupyter notebooks + 'pydoclint', # Docstring linter + 'docformatter', # Docstring formatter + 'interrogate', # Docstring coverage checker + 'copier', # Template management 'mike', # MkDocs: Versioned documentation support 'mkdocs', # Static site generator 'mkdocs-material', # Documentation framework on top of MkDocs @@ -75,35 +83,20 @@ docs = [ 'mkdocs-markdownextradata-plugin', # MkDocs: Markdown extra data support, such as global variables 'mkdocstrings-python', # MkDocs: Python docstring support 'pyyaml', # YAML parser -] -visualization = [ - 'darkdetect', # Detecting dark mode (system-level) - 'pandas', # Displaying tables in Jupyter notebooks - 'plotly', # Interactive plots - 'py3Dmol', # Visualisation of crystal structures -] -all = [ - 'easydiffraction[dev]', - 'easydiffraction[docs]', - 'easydiffraction[visualization]', + 'spdx-headers', # SPDX license header validation ] [project.urls] -homepage = 'https://easydiffraction.org' -documentation = 'https://docs.easydiffraction.org/lib' -source = 'https://github.com/easyscience/diffraction-lib' -tracker = 'https://github.com/easyscience/diffraction-lib/issues' +Homepage = 'https://easydiffraction.org' +Documentation = 'https://easyscience.github.io/diffraction-lib' +'Release Notes' = 'https://github.com/easyscience/diffraction-lib/releases' +'Source Code' = 'https://github.com/easyscience/diffraction-lib' +'Issue Tracker' = 'https://github.com/easyscience/diffraction-lib/issues' ############################ # Build system configuration ############################ -# Build system 'hatch' -- Python project manager -# https://hatch.pypa.io/ - -# Versioning system 'versioningit' -- Versioning from git tags -# https://versioningit.readthedocs.io/ - [build-system] build-backend = 'hatchling.build' requires = ['hatchling', 'versioningit'] @@ -112,6 +105,9 @@ requires = ['hatchling', 'versioningit'] # Configuration for hatchling ############################# +# 'hatch' -- Build system for Python +# https://hatch.pypa.io/ + [tool.hatch.build.targets.wheel] packages = ['src/easydiffraction'] @@ -125,6 +121,9 @@ source = 'versioningit' # Use versioningit to manage the version # Configuration for versioningit ################################ +# 'versioningit' -- Versioning from git tags +# https://versioningit.readthedocs.io/ + # Versioningit generates versions from git tags, so we don't need to # either specify them statically in pyproject.toml or save them in the # source code. Do not use {distance} in the version format, as it @@ -144,43 +143,45 @@ method = 'git' match = ['v*'] default-tag = 'v999.0.0' -################################ -# Configuration for docformatter -################################ - -# 'docformatter' -- Code formatter for docstrings -# https://docformatter.readthedocs.io/en/latest/ - -[tool.docformatter] -recursive = true -wrap-summaries = 72 -wrap-descriptions = 72 -close-quotes-on-newline = true - ################################ # Configuration for interrogate ################################ -# 'interrogate' -- Check for missing docstrings +# 'interrogate' -- Docstring coverage checker # https://interrogate.readthedocs.io/en/latest/ [tool.interrogate] -fail-under = 35 # Temporarily reduce to allow gradual improvement +fail-under = 35 # Minimum docstring coverage percentage to pass verbose = 1 -exclude = ["src/**/__init__.py"] +#exclude = ['src/**/__init__.py'] ####################################### # Configuration for coverage/pytest-cov ####################################### +# 'coverage' -- Code coverage measurement tool +# https://coverage.readthedocs.io/en/latest/ + [tool.coverage.run] -branch = true # Measure branch coverage as well -source = ['src/easydiffraction'] # Limit coverage to the source code directory +branch = true # Measure branch coverage as well +source = ['src'] # Limit coverage to the source code directory [tool.coverage.report] -show_missing = true # Show missing lines -skip_covered = true # Skip files with 100% coverage in the report -fail_under = 60 # Temporarily reduce to allow gradual improvement +show_missing = true # Show missing lines +skip_covered = false # Skip files with 100% coverage in the report +fail_under = 60 # Minimum coverage percentage to pass + +########################## +# Configuration for pytest +########################## + +# 'pytest' -- Testing framework +# https://docs.pytest.org/en/stable/ + +[tool.pytest.ini_options] +addopts = '--import-mode=importlib' +markers = ['fast: mark test as fast (should be run on every push)'] +testpaths = ['tests'] ######################## # Configuration for ruff @@ -190,107 +191,171 @@ fail_under = 60 # Temporarily reduce to allow gradual improvement # https://docs.astral.sh/ruff/rules/ [tool.ruff] -# Temporarily exclude some directories until we have improved the code quality there -exclude = ['tmp', 'tests/unit'] +exclude = ['tmp'] indent-width = 4 line-length = 99 -# Enable new rules that are not yet stable, like DOC -preview = true +preview = true # Enable new rules that are not yet stable, like DOC + +# Formatting options for Ruff + +[tool.ruff.format] +docstring-code-format = true # Whether to format code snippets in docstrings +docstring-code-line-length = 72 # Line length for code snippets in docstrings +indent-style = 'space' # PEP 8 recommends using spaces over tabs +line-ending = 'lf' # Line endings will be converted to \n +quote-style = 'single' # But double quotes in docstrings (PEP 8, PEP 257) + +# Linting rules to use with Ruff -# https://docs.astral.sh/ruff/rules/ [tool.ruff.lint] select = [ - 'ARG', # Argument-related issues (e.g., unused arguments) - 'B', # Bugbear-specific checks (e.g., likely bugs, bad patterns) - 'C', # Complexity-related issues (e.g., high McCabe complexity) - 'D', # Docstring formatting issues (old rules) - 'DOC', # Docstring formatting issues (new rules) - 'DTZ', # Datetime timezone issues (e.g., inconsistent timezone formats) - 'E', # General PEP 8 style errors - 'F', # Pyflakes-specific checks (e.g., unused variables, imports) - 'FLY', # Flynt-specific checks (e.g., enforcing f-strings) - 'G', # Type annotation issues (e.g., missing or incorrect type hints) - 'I', # Import sorting issues (e.g., unsorted imports) - 'ICN', # Import conventions (e.g., enforce aliasing like import numpy as np) - 'N', # Naming convention issues (e.g., variable names, function names) - 'NPY', # NumPy-specific checks (e.g., array operations, broadcasting) - 'PGH', # Misc text patterns checks - 'PTH', # Encourages using pathlib over os.path - 'S', # Security-related issues (e.g., use of insecure functions or libraries) - 'SIM', # Simplification issues (e.g., redundant code, unnecessary constructs) - 'TCH', # Type checking issues (e.g., incompatible types, missing type annotations) - 'TID252', # Enforces absolute imports over relative imports - 'W', # General PEP 8 warnings (e.g., lines too long, trailing whitespace) - #'ANN', # Missing or incorrect type annotations - #'COM', # Comment formatting issues - #'PERF', # Performance-related issues (e.g., inefficient code patterns) - #'PIE', # Potentially problematic idioms and errors - #'PL', # PyLint-specific checks (e.g., code smells, potential errors) - #'PT', # Pytest-related issues - #'RET', # Return statement issues (e.g., inconsistent returns) - #'RUF', # Ruff-specific checks (e.g., enforcing best practices) - #'SLF', # Self argument-related issues (e.g., missing or misused self) - #'T20', # Flake8-print-specific checks (e.g., print statements left in code) - #'TD', # Type definition issues (e.g., incorrect or missing type definitions) - #'TRY', # Tryceratops Try/Except-related issues (e.g., broad exceptions, empty except blocks) - #'UP', # Pyupgrade-specific checks + # Various rules + #'C90', # https://docs.astral.sh/ruff/rules/#mccabe-c90 + 'F', # https://docs.astral.sh/ruff/rules/#pyflakes-f + 'FLY', # https://docs.astral.sh/ruff/rules/#flynt-fly + #'FURB', # https://docs.astral.sh/ruff/rules/#refurb-furb + 'I', # https://docs.astral.sh/ruff/rules/#isort-i + 'N', # https://docs.astral.sh/ruff/rules/#pep8-naming-n + 'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy + 'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh + #'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf + #'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + #'TRY', # https://docs.astral.sh/ruff/rules/#tryceratops-try + #'UP', # https://docs.astral.sh/ruff/rules/#pyupgrade-up + # pycodestyle (E, W) rules + 'E', # https://docs.astral.sh/ruff/rules/#error-e + 'W', # https://docs.astral.sh/ruff/rules/#warning-w + # Pylint (PL) rules + #'PLC', # https://docs.astral.sh/ruff/rules/#convention-plc + #'PLE', # https://docs.astral.sh/ruff/rules/#error-ple + #'PLR', # https://docs.astral.sh/ruff/rules/#refactor-plr + #'PLW', # https://docs.astral.sh/ruff/rules/#warning-plw + # flake8 rules + #'A', # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + #'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + 'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg + #'ASYNC', # https://docs.astral.sh/ruff/rules/#flake8-async-async + 'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b + #'BLE', # https://docs.astral.sh/ruff/rules/#flake8-blind-except-ble + #'C4', # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 + #'COM', # https://docs.astral.sh/ruff/rules/#flake8-commas-com + 'DTZ', # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz + #'EM', # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + #'FA', # https://docs.astral.sh/ruff/rules/#flake8-future-annotations-fa + #'FBT', # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt + #'FIX', # https://docs.astral.sh/ruff/rules/#flake8-fixme-fix + 'G', # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g + 'ICN', # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn + #'INP', # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp + #'ISC', # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc + #'LOG', # https://docs.astral.sh/ruff/rules/#flake8-logging-log + #'PIE', # https://docs.astral.sh/ruff/rules/#flake8-pie-pie + #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt + 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + #'PYI', # https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi + #'RET', # https://docs.astral.sh/ruff/rules/#flake8-return-ret + #'RSE', # https://docs.astral.sh/ruff/rules/#flake8-raise-rse + 'S', # https://docs.astral.sh/ruff/rules/#flake8-bandit-s + 'SIM', # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + #'SLF', # https://docs.astral.sh/ruff/rules/#flake8-self-slf + #'SLOT', # https://docs.astral.sh/ruff/rules/#flake8-slots-slot + #'T20', # https://docs.astral.sh/ruff/rules/#flake8-print-t20 + #'TC', # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc + #'TD', # https://docs.astral.sh/ruff/rules/#flake8-todos-td + #'TID', # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid ] -# Temporarily disable some docstring checks until we have improved the docstring coverage + +# Exceptions to the linting rules + +# Ignore specific rules globally ignore = [ - 'C408', # Ignore: Unnecessary `dict()` call - 'C416', # Ignore: Unnecessary list comprehension - 'D100', # Ignore: Missing docstring in public module - 'D101', # Ignore: Missing docstring in class - 'D102', # Ignore: Missing docstring in public method - 'D103', # Ignore: Missing docstring in public function - 'D104', # Ignore: Missing docstring in public package - 'D105', # Ignore: Missing docstring in magic method - 'D107', # Ignore: Missing docstring in __init__ - 'D205', # Ignore: 1 blank line required between summary and description - 'DOC201', # Ignore: `return` is not documented in docstring - 'DOC501', # Ignore: Raised exception `ValueError` missing from docstring - 'DOC502', # Ignore: Raised exception is not explicitly raised: `TypeError` - 'DTZ005', # Ignore: `datetime.datetime.now()` called without a `tz` argument + 'COM812', # https://docs.astral.sh/ruff/rules/missing-trailing-comma/ + # The following covered by [tool.pydoclint] section below + 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d + 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc ] -# Temporarily increase McCabe complexity limit to 19 to allow -# refactoring in smaller steps. -[tool.ruff.lint.mccabe] -max-complexity = 37 # 19 # default is 10 +# Ignore specific rules in certain files or directories +[tool.ruff.lint.per-file-ignores] +'tests/**' = [ + 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc + 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ + 'S101', # https://docs.astral.sh/ruff/rules/assert/ +] +'docs/**' = [ + 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ + 'T201', # https://docs.astral.sh/ruff/rules/print/ +] +# Vendored jupyter_dark_detect: keep as-is from upstream for easy updates +# https://github.com/OpenMined/jupyter-dark-detect/tree/main/jupyter_dark_detect +'src/easydiffraction/utils/_vendored/jupyter_dark_detect/*' = [ + 'S', + 'TID', + 'W', + 'S', + 'E', +] + +# Specific options for certain rules [tool.ruff.lint.flake8-tidy-imports] +# Disallow all relative imports ban-relative-imports = 'all' [tool.ruff.lint.isort] +# Forces all from imports to appear on their own line force-single-line = true -[tool.ruff.lint.per-file-ignores] -'*test_*.py' = ['S101'] # allow asserts in test files -'conftest.py' = ['S101'] # allow asserts in test files -'*/__init__.py' = ['F401'] # re-exports are intentional in __init__.py -# Vendored jupyter_dark_detect: keep as-is from upstream for easy updates -# https://github.com/OpenMined/jupyter-dark-detect/tree/main/jupyter_dark_detect -'src/easydiffraction/utils/_vendored/jupyter_dark_detect/*' = [ - 'S110', # try-except-pass - 'S112', # try-except-continue - 'S404', # subprocess module - 'S607', # partial executable path - 'TID252', # relative imports - 'W291', # trailing whitespace (in JS strings) - 'W505', # doc line too long - 'E501', # line too long (in JS strings) -] +[tool.ruff.lint.mccabe] +# Cyclomatic complexity threshold (default is 10) +max-complexity = 10 [tool.ruff.lint.pycodestyle] -max-line-length = 100 #99# https://peps.python.org/pep-0008/#maximum-line-length -max-doc-length = 72 # https://peps.python.org/pep-0008/#maximum-line-length +# PEP 8 line length guidance: +# https://peps.python.org/pep-0008/#maximum-line-length +# Use 99 characters as the project-wide maximum for regular code lines. +max-line-length = 99 +# allow longer lines so that parameter declarations such as +# `name (Type | Type | None):` remain on a single line. Splitting these +# lines can prevent tools such as MkDocs and IDEs from correctly +# parsing and rendering parameter documentation. +# The descriptive text itself is wrapped more strictly by +# `docformatter` (see the configuration in [tool.docformatter] below) +# whenever it is treated as normal paragraph text. +# The line length for code snippets in docstrings is also more strict, +# as defined in the [tool.ruff.format] section above. +max-doc-length = 99 -[tool.ruff.lint.pydocstyle] -convention = 'google' +############################# +# Configuration for pydoclint +############################# -[tool.ruff.format] -docstring-code-format = true # Whether to format code snippets in docstrings -docstring-code-line-length = 72 # Line length for code snippets in docstrings -indent-style = 'space' # PEP 8 recommends using spaces over tabs -line-ending = 'lf' # Line endings will be converted to \n -quote-style = 'single' # But double quotes in docstrings (PEP 8, PEP 257) +# 'pydoclint' -- Docstring linter, a faster alternative to +# 'darglint' or 'darglint2'. +# https://pypi.org/project/pydoclint/ + +# This is a more advanced docstring linter compared to Ruff's built-in +# docstring check rules D or DOC. For example, among many other things, +# it can check that arguments in the docstring, which are used by MkDocs +# and IDEs to render parameter documentation, remain synchronized with +# the parameter declarations in the code (in function's signature). + +[tool.pydoclint] +style = "google" +check-style-mismatch = true +check-arg-defaults = true +allow-init-docstring = true + +################################ +# Configuration for docformatter +################################ + +# 'docformatter' -- Code formatter for docstrings +# https://docformatter.readthedocs.io/en/latest/ + +[tool.docformatter] +recursive = true +wrap-summaries = 72 +wrap-descriptions = 72 +close-quotes-on-newline = true diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 907883e8..00000000 --- a/pytest.ini +++ /dev/null @@ -1,13 +0,0 @@ -[pytest] -addopts = --import-mode=importlib -markers = - fast: mark test as fast (should be run on every push) - integration: mark test as integration (slow; opt-in) -testpaths = - tests -filterwarnings = - ignore::DeprecationWarning:cryspy\. - ignore:.*scipy\.misc is deprecated.*:DeprecationWarning - # Suppress expected UserWarnings emitted during tutorial list fetching in tests - ignore:Falling back to latest release info\...:UserWarning:easydiffraction\.utils\.logging - ignore:'tutorials\.zip' not found in the release\.:UserWarning:easydiffraction\.utils\.logging diff --git a/tools/add_assets_to_docs.sh b/tools/add_assets_to_docs.sh deleted file mode 100755 index e13eb40a..00000000 --- a/tools/add_assets_to_docs.sh +++ /dev/null @@ -1,16 +0,0 @@ -echo "📥 Add files from ../assets-docs" -cp -R ../assets-docs/docs/assets/ docs/assets/ -cp -R ../assets-docs/includes/ includes/ -cp -R ../assets-docs/overrides/ overrides/ - -echo "📥 Add files from ../assets-branding" -mkdir -p docs/assets/images/ -cp ../assets-branding/easydiffraction/hero/dark.png docs/assets/images/hero_dark.png -cp ../assets-branding/easydiffraction/hero/light.png docs/assets/images/hero_light.png -cp ../assets-branding/easydiffraction/logos/dark.svg docs/assets/images/logo_dark.svg -cp ../assets-branding/easydiffraction/logos/light.svg docs/assets/images/logo_light.svg -cp ../assets-branding/easydiffraction/icons/color.png docs/assets/images/favicon.png - -mkdir -p overrides/.icons/ -cp ../assets-branding/easydiffraction/icons/bw.svg overrides/.icons/easydiffraction.svg -cp ../assets-branding/easyscience-org/icons/eso-icon_bw.svg overrides/.icons/easyscience.svg diff --git a/tools/add_license_headers.py b/tools/add_license_headers.py new file mode 100644 index 00000000..28febde7 --- /dev/null +++ b/tools/add_license_headers.py @@ -0,0 +1,151 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Add SPDX headers to Python files. + +- SPDX-FileCopyrightText with the license holder name and organization + URL from ``pyproject.toml`` as well as the file's creation year. +- SPDX-License-Identifier is taken from the project license value in + ``pyproject.toml``. +""" + +from __future__ import annotations + +import argparse +import tomllib +from datetime import datetime +from pathlib import Path +from typing import Optional +from typing import Union + +from git import Repo +from spdx_headers.core import find_repository_root +from spdx_headers.core import get_copyright_info +from spdx_headers.data import load_license_data +from spdx_headers.operations import add_header_to_single_file + +LICENSE_DATABASE = load_license_data() + + +def load_pyproject(repo_path: Union[str, Path]) -> dict: + """Load and return parsed ``pyproject.toml`` data for the + repository. + """ + repo_root = find_repository_root(repo_path) + pyproject_path = repo_root / 'pyproject.toml' + + with open(pyproject_path, 'rb') as file_handle: + return tomllib.load(file_handle) + + +def get_file_creation_year(file_path: Union[str, Path]) -> str: + """Return the year the file was first added to Git history. + + If the year cannot be determined, fall back to the current year. + """ + file_path = Path(file_path) + + repo = Repo(file_path, search_parent_directories=True) + root = Path(repo.working_tree_dir).resolve() + rel_path = file_path.resolve().relative_to(root) + + rel_path_git = rel_path.as_posix() # IMPORTANT for git pathspec + + # Get the year when the file was first added to Git history. + # NOTE: Do not combine `--reverse` with `--max-count=1` here, as it can + # yield an empty result with some Git versions. Instead, get the full + # filtered output and take the first line. + log_output = repo.git.log( + '--follow', + '--diff-filter=A', + '--reverse', + '--format=%ad', + '--date=format:%Y', + '--', + rel_path_git, + ).strip() + + year = log_output.splitlines()[0].strip() if log_output else '' + + return year or str(datetime.now().year) + + +def get_org_url(repo_path: Union[str, Path]) -> str: + """Return the organization URL derived from the repository source + URL. + """ + pyproject_data = load_pyproject(repo_path) + repo_url = pyproject_data['project']['urls']['Source Code'] + return repo_url.rsplit('/', 1)[0] + + +def get_project_license(repo_path: Union[str, Path]) -> str: + """Return the project license value from ``pyproject.toml``.""" + pyproject_data = load_pyproject(repo_path) + return pyproject_data['project']['license'] + + +def get_copyright_holder(repo_path: Union[str, Path]) -> str: + """Return the repository copyright holder name.""" + _, name, _ = get_copyright_info(repo_path) + return name + + +def add_spdx_header( + target_file: Union[str, Path], + *, + license_key: str, + copyright_holder: str, + org_url: str, +) -> None: + """Add SPDX headers.""" + year = get_file_creation_year(target_file) + + add_header_to_single_file( + filepath=target_file, + license_key=license_key, + license_data=LICENSE_DATABASE, + year=year, + name=copyright_holder, + email=org_url, + ) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description='Add SPDX headers to Python files under the given paths.', + ) + parser.add_argument( + 'paths', + nargs='+', + help='Relative paths to scan (e.g. src tests)', + ) + return parser + + +def main(argv: Optional[list[str]] = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + + repo_path = Path('.').resolve() + license_key = get_project_license(repo_path) + copyright_holder = get_copyright_holder(repo_path) + org_url = get_org_url(repo_path) + + for base_dir in args.paths: + base_path = Path(base_dir) + if not base_path.exists(): + parser.error(f'Path does not exist: {base_dir}') + + for py_file in base_path.rglob('*.py'): + add_spdx_header( + py_file, + license_key=license_key, + copyright_holder=copyright_holder, + org_url=org_url, + ) + + return 0 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tools/check_license_headers.py b/tools/check_license_headers.py new file mode 100644 index 00000000..c8a4df21 --- /dev/null +++ b/tools/check_license_headers.py @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Check SPDX headers in Python files.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +from typing import Optional + +from spdx_headers.operations import check_headers + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description='Check SPDX headers in Python files under the given paths.', + ) + parser.add_argument( + 'paths', + nargs='+', + help='Relative paths to scan (e.g. src tests)', + ) + return parser + + +def main(argv: Optional[list[str]] = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + + exit_codes = [] + + for base_dir in args.paths: + base_path = Path(base_dir) + if not base_path.exists(): + parser.error(f'Path does not exist: {base_dir}') + + print('=' * 50) + print(f'Checking SPDX headers in: {base_dir}') + exit_codes.append(check_headers(base_dir)) + + return 0 if all(code == 0 for code in exit_codes) else 1 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tools/cleanup_docs.sh b/tools/cleanup_docs.sh deleted file mode 100755 index 757ec3e4..00000000 --- a/tools/cleanup_docs.sh +++ /dev/null @@ -1,10 +0,0 @@ -echo "🧹 Clean up after building documentation" -rm -rf site/ -rm -rf docs/assets/javascripts -rm -rf docs/assets/stylesheets -rm -rf docs/assets/images/*.png -rm -rf docs/assets/images/*.svg -rm -rf includes/ -rm -rf overrides/ -rm -rf docs/tutorials/*.ipynb -rm mkdocs.yml diff --git a/tools/create_mkdocs_yml.py b/tools/create_mkdocs_yml.py deleted file mode 100644 index fb2b8685..00000000 --- a/tools/create_mkdocs_yml.py +++ /dev/null @@ -1,158 +0,0 @@ -import os -import re -from pathlib import Path -from typing import Any -from typing import Dict -from typing import List - -import material.extensions.emoji # side-effect: register emoji tag -import pymdownx.superfences # side-effect: register superfence tag -import yaml - -# Side-effect imports above ensure tagged YAML constructors -# (e.g., !!python/name:...) can be resolved during load. - - -def _activate_yaml_tag_side_effects() -> None: # pragma: no cover - trivial - """Access imported modules' attributes so Ruff sees them as used. - - The primary purpose of importing these packages is to ensure the - tagged constructors are importable during YAML load. Accessing the - attributes makes the side-effect explicit without needing noqa. - """ - _ = material.extensions.emoji.twemoji # type: ignore[attr-defined] - _ = pymdownx.superfences.fence_code_format # type: ignore[attr-defined] - - -_activate_yaml_tag_side_effects() - - -def load_yaml_with_env_variables(file_path: str) -> Dict[str, Any]: - """Load YAML resolving env variables declared as !ENV ${VAR_NAME}. - - Args: - file_path (str): Path to the YAML file. - - Returns: - dict: Parsed YAML content with environment variables replaced. - """ - tag = '!ENV' - pattern = re.compile(r'.*?\${([A-Z0-9_]+)}.*?') - - def constructor_env_variables(loader, node): # type: ignore[all] - """YAML constructor replacing !ENV markers with values.""" - value = loader.construct_scalar(node) # type: ignore[attr-defined] - for var in pattern.findall(value): - value = value.replace(f'${{{var}}}', os.environ.get(var, var)) - return value - - loader = yaml.FullLoader - loader.add_implicit_resolver(tag, pattern, None) - loader.add_constructor(tag, constructor_env_variables) - - with Path(file_path).open('r', encoding='utf-8') as fh: - return yaml.full_load(fh) - - -def merge_yaml(base_config: Dict[str, Any], override_config: Dict[str, Any]) -> Dict[str, Any]: - """Deep merge two YAML dicts; override has priority.""" - if not isinstance(base_config, dict): - return override_config - - merged_config = base_config.copy() - - for key, override_value in override_config.items(): - if key in merged_config: - base_value = merged_config[key] - if isinstance(base_value, dict) and isinstance(override_value, dict): - merged_config[key] = merge_yaml(base_value, override_value) - elif isinstance(base_value, list) and isinstance(override_value, list): - merged_config[key] = merge_lists(base_value, override_value) - else: - merged_config[key] = override_value - else: - merged_config[key] = override_value - - return merged_config - - -def merge_lists(base_list: List[Any], override_list: List[Any]) -> List[Any]: - """Merge two lists with handling of single-key dict items. - - Single-key dicts sharing a key are deep-merged; other items are - appended if not already present. - - Args: - base_list (list): The base list. - override_list (list): The overriding list. - - Returns: - list: The merged list. - """ - merged_list = [] - seen_items = {} - - for item in base_list + override_list: - if isinstance(item, dict) and len(item) == 1: - key = next(iter(item)) # Extract dictionary key (e.g., "mkdocs-jupyter") - if key in seen_items: - seen_items[key] = merge_yaml(seen_items[key], item[key]) # Merge dictionaries - else: - seen_items[key] = item[key] - elif item not in merged_list: - merged_list.append(item) - - # Convert merged dictionary values back into list format - for key, value in seen_items.items(): - merged_list.append({key: value}) - - return merged_list - - -def save_yaml(data: Dict[str, Any], output_file: str) -> None: - """Write YAML preserving !!python/name tags and Unicode.""" - - class CustomDumper(yaml.Dumper): - """Custom dumper avoiding unnecessary anchors & quotes.""" - - def ignore_aliases(self, _): # noqa: D401 - simple override - return True - - out_path = Path(output_file) - with out_path.open('w', encoding='utf-8') as f: - f.write('# WARNING: This file is auto-generated during the build process.\n') - f.write('# DO NOT EDIT THIS FILE MANUALLY.\n') - f.write('# It is created by merging:\n') - f.write('# - Generic YAML file: ../assets-docs/mkdocs.yml\n') - f.write('# - Project specific YAML file: docs/mkdocs.yml\n\n') - - with out_path.open('a', encoding='utf-8') as f: - yaml.dump( - data, - f, - Dumper=CustomDumper, # Use custom dumper - allow_unicode=True, # Ensure Unicode characters like © are preserved - default_flow_style=False, # - sort_keys=False, # Preserve the order of keys - ) - - -def main() -> None: - """Main function to read, merge, and save YAML configurations.""" - generic_config_path = '../assets-docs/mkdocs.yml' - specific_config_path = 'docs/mkdocs.yml' - output_path = 'mkdocs.yml' - - print(f'Reading generic config: {generic_config_path}') - base_config = load_yaml_with_env_variables(generic_config_path) - - print(f'Reading project specific config: {specific_config_path}') - override_config = load_yaml_with_env_variables(specific_config_path) - - print(f'Saving merged config: {output_path}') - merged_config = merge_yaml(base_config, override_config) - save_yaml(merged_config, output_path) - - -if __name__ == '__main__': - main() diff --git a/tools/remove_license_headers.py b/tools/remove_license_headers.py new file mode 100644 index 00000000..f5d09da5 --- /dev/null +++ b/tools/remove_license_headers.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Remove SPDX headers from Python files.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +from typing import Optional + +from spdx_headers.operations import remove_header_from_py_files + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description='Remove SPDX headers from Python files under the given paths.', + ) + parser.add_argument( + 'paths', + nargs='+', + help='Relative paths to scan (e.g. src tests)', + ) + return parser + + +def main(argv: Optional[list[str]] = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + + for base_dir in args.paths: + base_path = Path(base_dir) + if not base_path.exists(): + parser.error(f'Path does not exist: {base_dir}') + + remove_header_from_py_files(base_dir) + + return 0 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tools/test_scripts.py b/tools/test_scripts.py index 327bafd2..e24b28e2 100644 --- a/tools/test_scripts.py +++ b/tools/test_scripts.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause """Test runner for tutorial scripts in the 'tutorials' directory. This test discovers and executes all Python scripts located under the @@ -16,15 +18,13 @@ import pytest -# Mark this module as 'integration' so it's excluded by default -# (see pytest.ini) -pytestmark = pytest.mark.integration - _repo_root = Path(__file__).resolve().parents[1] _src_root = _repo_root / 'src' # Discover tutorial scripts, excluding temporary checkpoint files -TUTORIALS = [p for p in Path('tutorials').rglob('*.py') if '.ipynb_checkpoints' not in p.parts] +TUTORIALS = [ + p for p in Path('docs/docs/tutorials').rglob('*.py') if '.ipynb_checkpoints' not in p.parts +] @pytest.mark.parametrize('script_path', TUTORIALS) diff --git a/tools/update_docs_assets.py b/tools/update_docs_assets.py new file mode 100644 index 00000000..b274e038 --- /dev/null +++ b/tools/update_docs_assets.py @@ -0,0 +1,91 @@ +""" +Update documentation assets from the assets-branding repository. + +This script fetches branding assets (logos, icons, images) from the +easyscience/assets-branding GitHub repository and copies them to the +appropriate locations in the documentation directory. +""" + +import shutil +from pathlib import Path + +import pooch + +# Configuration: Define what to fetch and where to copy +GITHUB_REPO = 'easyscience/assets-branding' +GITHUB_BRANCH = 'master' +BASE_URL = f'https://raw.githubusercontent.com/{GITHUB_REPO}/refs/heads/{GITHUB_BRANCH}' +PROJECT_NAME = 'easydiffraction' + +# Mapping of source files to destination paths +# Format: "source_path_in_repo": "destination_path_in_project" +ASSETS_MAP = { + # Logos + f'{PROJECT_NAME}/logos/dark.svg': 'docs/docs/assets/images/logo_dark.svg', + f'{PROJECT_NAME}/logos/light.svg': 'docs/docs/assets/images/logo_light.svg', + # Favicon + f'{PROJECT_NAME}/icons/color.png': 'docs/docs/assets/images/favicon.png', + # Icon overrides + f'{PROJECT_NAME}/icons/bw.svg': f'docs/overrides/.icons/{PROJECT_NAME}.svg', + 'easyscience-org/icons/eso-icon_bw.svg': 'docs/overrides/.icons/easyscience.svg', +} + + +def fetch_and_copy_asset( + source_path: str, + dest_path: str, + cache_dir: Path, +) -> None: + """ + Fetch an asset from GitHub and copy it to the destination. + + Args: + source_path: Path to the file in the GitHub repository + dest_path: Destination path in the project + cache_dir: Directory to cache downloaded files + """ + url = f'{BASE_URL}/{source_path}' + + # Create a unique cache filename based on source path + cache_filename = source_path.replace('/', '_') + + # Download file using pooch + file_path = pooch.retrieve( + url=url, + known_hash=None, # Skip hash verification + path=cache_dir, + fname=cache_filename, + ) + + # Create destination directory if it doesn't exist + dest = Path(dest_path) + dest.parent.mkdir(parents=True, exist_ok=True) + + # Copy the file to destination + shutil.copy2(file_path, dest) + print(f'Copied {file_path} -> {dest_path}') + + +def main(): + """Main function to update all documentation assets.""" + print('📥 Updating documentation assets...') + print(f' Repository: {GITHUB_REPO}') + print(f' Branch: {GITHUB_BRANCH}\n') + + # Use a temporary cache directory + cache_dir = Path.home() / '.cache' / GITHUB_REPO + cache_dir.mkdir(parents=True, exist_ok=True) + + # Fetch and copy each asset + for source_path, dest_path in ASSETS_MAP.items(): + try: + fetch_and_copy_asset(source_path, dest_path, cache_dir) + print() + except Exception as e: + print(f'❌ Failed to fetch {source_path}: {e}') + + print('\n✅ Documentation assets updated successfully!') + + +if __name__ == '__main__': + main() diff --git a/tools/update_github_labels.py b/tools/update_github_labels.py new file mode 100644 index 00000000..84de575e --- /dev/null +++ b/tools/update_github_labels.py @@ -0,0 +1,341 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Set/update GitHub labels for current or specified easyscience +repository. + +Requires: + - gh CLI installed + - gh auth login completed + +Usage: + python update_github_labels.py + python update_github_labels.py --dry-run + python update_github_labels.py --repo easyscience/my-repo + python update_github_labels.py --repo easyscience/my-repo --dry-run +""" + +from __future__ import annotations + +import argparse +import json +import shlex +import subprocess # noqa: S404 +import sys +from dataclasses import dataclass + +EASYSCIENCE_ORG = 'easyscience' + + +# Data structures + + +@dataclass(frozen=True) +class Label: + """A GitHub label with name, color, and description.""" + + name: str + color: str + description: str = '' + + +@dataclass(frozen=True) +class LabelRename: + """Mapping from old label name to new label name.""" + + old: str + new: str + + +class Colors: + """Hex color codes for label groups.""" + + SCOPE = 'd73a4a' + MAINTAINER = '0e8a16' + PRIORITY = 'fbca04' + BOT = '5319e7' + + +LABEL_RENAMES = [ + # Default GitHub labels to rename (if they exist) + LabelRename('bug', '[scope] bug'), + LabelRename('documentation', '[scope] documentation'), + LabelRename('duplicate', '[maintainer] duplicate'), + LabelRename('enhancement', '[scope] enhancement'), + LabelRename('good first issue', '[maintainer] good first issue'), + LabelRename('help wanted', '[maintainer] help wanted'), + LabelRename('invalid', '[maintainer] invalid'), + LabelRename('question', '[maintainer] question'), + LabelRename('wontfix', '[maintainer] wontfix'), + # Custom label renames (if they exist) + LabelRename('[bot] pull request', '[bot] release'), +] + +LABELS = [ + # Scope labels + Label( + '[scope] bug', + Colors.SCOPE, + 'Bug report or fix (major.minor.PATCH)', + ), + Label( + '[scope] documentation', + Colors.SCOPE, + 'Documentation only changes (major.minor.patch.POST)', + ), + Label( + '[scope] enhancement', + Colors.SCOPE, + 'Adds/improves features (major.MINOR.patch)', + ), + Label( + '[scope] maintenance', + Colors.SCOPE, + 'Code/tooling cleanup, no feature or bugfix (major.minor.PATCH)', + ), + Label( + '[scope] significant', + Colors.SCOPE, + 'Breaking or major changes (MAJOR.minor.patch)', + ), + Label( + '[scope] ⚠️ label needed', + Colors.SCOPE, + 'Automatically added to issues and PRs without a [scope] label', + ), + # Maintainer labels + Label( + '[maintainer] duplicate', + Colors.MAINTAINER, + 'Already reported or submitted', + ), + Label( + '[maintainer] good first issue', + Colors.MAINTAINER, + 'Good entry-level issue for newcomers', + ), + Label( + '[maintainer] help wanted', + Colors.MAINTAINER, + 'Needs additional help to resolve or implement', + ), + Label( + '[maintainer] invalid', + Colors.MAINTAINER, + 'Invalid, incorrect or outdated', + ), + Label( + '[maintainer] question', + Colors.MAINTAINER, + 'Needs clarification, discussion, or more information', + ), + Label( + '[maintainer] wontfix', + Colors.MAINTAINER, + 'Will not be fixed or continued', + ), + # Priority labels + Label( + '[priority] lowest', + Colors.PRIORITY, + 'Very low urgency', + ), + Label( + '[priority] low', + Colors.PRIORITY, + 'Low importance', + ), + Label( + '[priority] medium', + Colors.PRIORITY, + 'Normal/default priority', + ), + Label( + '[priority] high', + Colors.PRIORITY, + 'Should be prioritized soon', + ), + Label( + '[priority] highest', + Colors.PRIORITY, + 'Urgent. Needs attention ASAP', + ), + Label( + '[priority] ⚠️ label needed', + Colors.PRIORITY, + 'Automatically added to issues without a [priority] label', + ), + # Bot label + Label( + '[bot] release', + Colors.BOT, + 'Automated release PR. Excluded from changelog/versioning', + ), + Label( + '[bot] backmerge', + Colors.BOT, + 'Automated backmerge master → develop failed due to conflicts', + ), +] + + +# Helpers + + +@dataclass(frozen=True) +class CmdResult: + """Result of a shell command execution.""" + + returncode: int + stdout: str + stderr: str + + +def run_cmd( + args: list[str], + *, + dry_run: bool, + check: bool = True, +) -> CmdResult: + """Run a command (or print it in dry-run mode).""" + cmd_str = ' '.join(shlex.quote(a) for a in args) + + if dry_run: + print(f' [dry-run] {cmd_str}') + return CmdResult(0, '', '') + + proc = subprocess.run( + args=args, + text=True, + capture_output=True, + ) + result = CmdResult( + proc.returncode, + proc.stdout.strip(), + proc.stderr.strip(), + ) + + if check and proc.returncode != 0: + raise RuntimeError(f'Command failed ({proc.returncode}): {cmd_str}\n{result.stderr}') + + return result + + +def get_current_repo() -> str: + """Get the current repository name in 'owner/repo' format.""" + result = subprocess.run( + args=[ + 'gh', + 'repo', + 'view', + '--json', + 'nameWithOwner', + ], + text=True, + capture_output=True, + check=True, + ) + data = json.loads(result.stdout) + name_with_owner = data.get('nameWithOwner', '') + + if '/' not in name_with_owner: + raise RuntimeError('Could not determine current repository name') + + return name_with_owner + + +def rename_label( + repo: str, + rename: LabelRename, + *, + dry_run: bool, +) -> None: + """Rename a label, silently skipping if it doesn't exist.""" + result = run_cmd( + args=[ + 'gh', + 'label', + 'edit', + rename.old, + '--name', + rename.new, + '--repo', + repo, + ], + dry_run=dry_run, + check=False, + ) + + if dry_run or result.returncode == 0: + print(f' Rename: {rename.old!r} → {rename.new!r}') + else: + print(f' Skip (not found): {rename.old!r}') + + +def upsert_label( + repo: str, + label: Label, + *, + dry_run: bool, +) -> None: + """Create or update a label.""" + run_cmd( + [ + 'gh', + 'label', + 'create', + label.name, + '--color', + label.color, + '--description', + label.description, + '--force', + '--repo', + repo, + ], + dry_run=dry_run, + ) + print(f' Upsert: {label.name!r}') + + +# Main + + +def main() -> int: + """Entry point: parse arguments and sync labels.""" + parser = argparse.ArgumentParser(description='Sync GitHub labels for easyscience repos') + parser.add_argument( + '--repo', + help='Target repository (owner/name)', + ) + parser.add_argument( + '--dry-run', + action='store_true', + help='Print actions without applying changes', + ) + args = parser.parse_args() + + repo = args.repo or get_current_repo() + org = repo.split('/')[0] + + if org.lower() != EASYSCIENCE_ORG: + print(f"Error: repository '{repo}' is not under '{EASYSCIENCE_ORG}'", file=sys.stderr) + return 2 + + print(f'Repository: {repo}') + if args.dry_run: + print('Mode: DRY-RUN (no changes will be made)\n') + + print('\nRenaming default labels...') + for rename in LABEL_RENAMES: + rename_label(repo, rename, dry_run=args.dry_run) + + print('\nUpserting labels...') + for label in LABELS: + upsert_label(repo, label, dry_run=args.dry_run) + + print('\nDone.') + return 0 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tutorials/data/ed-3.xye b/tutorials/data/ed-3.xye deleted file mode 100644 index 0b9b63e3..00000000 --- a/tutorials/data/ed-3.xye +++ /dev/null @@ -1,3099 +0,0 @@ -# 2theta intensity su - 10.00 167.00 12.60 - 10.05 157.00 12.50 - 10.10 187.00 13.30 - 10.15 197.00 14.00 - 10.20 164.00 12.50 - 10.25 171.00 13.00 - 10.30 190.00 13.40 - 10.35 182.00 13.50 - 10.40 166.00 12.60 - 10.45 203.00 14.30 - 10.50 156.00 12.20 - 10.55 190.00 13.90 - 10.60 175.00 13.00 - 10.65 161.00 12.90 - 10.70 187.00 13.50 - 10.75 166.00 13.10 - 10.80 171.00 13.00 - 10.85 177.00 13.60 - 10.90 159.00 12.60 - 10.95 184.00 13.90 - 11.00 160.00 12.60 - 11.05 182.00 13.90 - 11.10 167.00 13.00 - 11.15 169.00 13.40 - 11.20 186.00 13.70 - 11.25 167.00 13.30 - 11.30 169.00 13.10 - 11.35 159.00 13.10 - 11.40 170.00 13.20 - 11.45 179.00 13.90 - 11.50 178.00 13.50 - 11.55 188.00 14.20 - 11.60 176.00 13.50 - 11.65 196.00 14.60 - 11.70 182.00 13.70 - 11.75 183.00 14.00 - 11.80 195.00 14.10 - 11.85 144.00 12.40 - 11.90 178.00 13.50 - 11.95 175.00 13.70 - 12.00 200.00 14.20 - 12.05 157.00 12.90 - 12.10 195.00 14.00 - 12.15 164.00 13.10 - 12.20 188.00 13.70 - 12.25 168.00 13.10 - 12.30 191.00 13.70 - 12.35 178.00 13.40 - 12.40 182.00 13.30 - 12.45 174.00 13.30 - 12.50 171.00 12.90 - 12.55 174.00 13.20 - 12.60 184.00 13.30 - 12.65 164.00 12.80 - 12.70 166.00 12.50 - 12.75 177.00 13.20 - 12.80 174.00 12.80 - 12.85 187.00 13.50 - 12.90 183.00 13.10 - 12.95 187.00 13.50 - 13.00 175.00 12.80 - 13.05 165.00 12.70 - 13.10 177.00 12.80 - 13.15 182.00 13.30 - 13.20 195.00 13.50 - 13.25 163.00 12.60 - 13.30 180.00 12.90 - 13.35 171.00 12.90 - 13.40 182.00 13.00 - 13.45 179.00 13.10 - 13.50 161.00 12.20 - 13.55 156.00 12.30 - 13.60 197.00 13.50 - 13.65 167.00 12.70 - 13.70 180.00 12.80 - 13.75 182.00 13.20 - 13.80 176.00 12.70 - 13.85 153.00 12.10 - 13.90 179.00 12.80 - 13.95 156.00 12.30 - 14.00 187.00 13.10 - 14.05 170.00 12.80 - 14.10 185.00 13.00 - 14.15 180.00 13.20 - 14.20 167.00 12.40 - 14.25 159.00 12.40 - 14.30 152.00 11.80 - 14.35 173.00 13.00 - 14.40 169.00 12.50 - 14.45 185.00 13.40 - 14.50 168.00 12.40 - 14.55 193.00 13.70 - 14.60 177.00 12.80 - 14.65 161.00 12.50 - 14.70 180.00 12.90 - 14.75 165.00 12.60 - 14.80 178.00 12.80 - 14.85 157.00 12.30 - 14.90 163.00 12.30 - 14.95 143.00 11.70 - 15.00 155.00 11.90 - 15.05 168.00 12.80 - 15.10 160.00 12.10 - 15.15 155.00 12.20 - 15.20 203.00 13.70 - 15.25 164.00 12.60 - 15.30 158.00 12.10 - 15.35 152.00 12.10 - 15.40 173.00 12.60 - 15.45 160.00 12.50 - 15.50 172.00 12.60 - 15.55 164.00 12.60 - 15.60 163.00 12.30 - 15.65 173.00 13.00 - 15.70 177.00 12.80 - 15.75 184.00 13.40 - 15.80 173.00 12.70 - 15.85 182.00 13.30 - 15.90 156.00 12.10 - 15.95 152.00 12.20 - 16.00 201.00 13.70 - 16.05 156.00 12.30 - 16.10 169.00 12.50 - 16.15 178.00 13.20 - 16.20 150.00 11.80 - 16.25 163.00 12.60 - 16.30 165.00 12.40 - 16.35 160.00 12.50 - 16.40 171.00 12.60 - 16.45 168.00 12.80 - 16.50 159.00 12.20 - 16.55 166.00 12.80 - 16.60 156.00 12.10 - 16.65 156.00 12.40 - 16.70 154.00 12.10 - 16.75 173.00 13.10 - 16.80 173.00 12.80 - 16.85 161.00 12.70 - 16.90 177.00 13.00 - 16.95 159.00 12.70 - 17.00 162.00 12.50 - 17.05 166.00 13.00 - 17.10 167.00 12.70 - 17.15 166.00 13.10 - 17.20 168.00 12.80 - 17.25 188.00 14.00 - 17.30 165.00 12.80 - 17.35 171.00 13.40 - 17.40 171.00 13.10 - 17.45 162.00 13.10 - 17.50 161.00 12.80 - 17.55 177.00 13.80 - 17.60 176.00 13.40 - 17.65 175.00 13.70 - 17.70 140.00 12.00 - 17.75 177.00 13.90 - 17.80 150.00 12.40 - 17.85 154.00 12.90 - 17.90 138.00 11.90 - 17.95 161.00 13.20 - 18.00 171.00 13.30 - 18.05 144.00 12.50 - 18.10 148.00 12.40 - 18.15 169.00 13.50 - 18.20 162.00 12.90 - 18.25 171.00 13.50 - 18.30 155.00 12.60 - 18.35 143.00 12.30 - 18.40 162.00 12.80 - 18.45 177.00 13.60 - 18.50 158.00 12.60 - 18.55 142.00 12.20 - 18.60 153.00 12.40 - 18.65 169.00 13.30 - 18.70 144.00 12.00 - 18.75 171.00 13.30 - 18.80 159.00 12.50 - 18.85 169.00 13.10 - 18.90 163.00 12.60 - 18.95 154.00 12.50 - 19.00 146.00 11.90 - 19.05 154.00 12.50 - 19.10 156.00 12.20 - 19.15 195.00 14.00 - 19.20 154.00 12.10 - 19.25 167.00 12.90 - 19.30 156.00 12.20 - 19.35 148.00 12.10 - 19.40 173.00 12.80 - 19.45 155.00 12.40 - 19.50 146.00 11.70 - 19.55 173.00 13.10 - 19.60 179.00 13.00 - 19.65 152.00 12.30 - 19.70 182.00 13.10 - 19.75 183.00 13.40 - 19.80 150.00 11.90 - 19.85 155.00 12.30 - 19.90 158.00 12.20 - 19.95 161.00 12.60 - 20.00 164.00 12.40 - 20.05 166.00 12.80 - 20.10 172.00 12.70 - 20.15 148.00 12.10 - 20.20 161.00 12.30 - 20.25 160.00 12.60 - 20.30 185.00 13.20 - 20.35 165.00 12.80 - 20.40 155.00 12.10 - 20.45 172.00 13.00 - 20.50 170.00 12.70 - 20.55 180.00 13.40 - 20.60 184.00 13.20 - 20.65 164.00 12.80 - 20.70 177.00 13.00 - 20.75 150.00 12.20 - 20.80 176.00 12.90 - 20.85 174.00 13.20 - 20.90 173.00 12.80 - 20.95 167.00 12.90 - 21.00 158.00 12.20 - 21.05 174.00 13.20 - 21.10 160.00 12.30 - 21.15 174.00 13.20 - 21.20 160.00 12.30 - 21.25 182.00 13.40 - 21.30 155.00 12.10 - 21.35 182.00 13.40 - 21.40 157.00 12.20 - 21.45 174.00 13.20 - 21.50 173.00 12.80 - 21.55 165.00 12.80 - 21.60 182.00 13.10 - 21.65 176.00 13.20 - 21.70 150.00 11.90 - 21.75 162.00 12.60 - 21.80 172.00 12.70 - 21.85 162.00 12.70 - 21.90 171.00 12.70 - 21.95 165.00 12.80 - 22.00 180.00 13.00 - 22.05 167.00 12.80 - 22.10 159.00 12.20 - 22.15 159.00 12.50 - 22.20 160.00 12.30 - 22.25 174.00 13.10 - 22.30 175.00 12.90 - 22.35 172.00 13.10 - 22.40 176.00 12.90 - 22.45 140.00 11.80 - 22.50 163.00 12.40 - 22.55 180.00 13.50 - 22.60 211.00 14.20 - 22.65 190.00 13.90 - 22.70 179.00 13.10 - 22.75 195.00 14.10 - 22.80 198.00 13.90 - 22.85 181.00 13.70 - 22.90 203.00 14.10 - 22.95 193.00 14.10 - 23.00 155.00 12.40 - 23.05 159.00 12.90 - 23.10 184.00 13.50 - 23.15 145.00 12.30 - 23.20 145.00 12.00 - 23.25 179.00 13.70 - 23.30 185.00 13.60 - 23.35 168.00 13.30 - 23.40 185.00 13.60 - 23.45 170.00 13.40 - 23.50 174.00 13.30 - 23.55 164.00 13.20 - 23.60 168.00 13.10 - 23.65 185.00 14.10 - 23.70 183.00 13.70 - 23.75 172.00 13.70 - 23.80 156.00 12.70 - 23.85 182.00 14.00 - 23.90 182.00 13.70 - 23.95 149.00 12.70 - 24.00 160.00 12.80 - 24.05 168.00 13.50 - 24.10 178.00 13.60 - 24.15 169.00 13.60 - 24.20 172.00 13.40 - 24.25 170.00 13.60 - 24.30 161.00 12.90 - 24.35 168.00 13.50 - 24.40 162.00 13.00 - 24.45 157.00 13.00 - 24.50 162.00 12.90 - 24.55 159.00 13.10 - 24.60 168.00 13.20 - 24.65 170.00 13.50 - 24.70 166.00 13.00 - 24.75 146.00 12.50 - 24.80 154.00 12.50 - 24.85 154.00 12.70 - 24.90 198.00 14.10 - 24.95 195.00 14.30 - 25.00 148.00 12.20 - 25.05 161.00 12.90 - 25.10 160.00 12.60 - 25.15 160.00 12.80 - 25.20 149.00 12.10 - 25.25 179.00 13.50 - 25.30 174.00 13.00 - 25.35 168.00 13.00 - 25.40 146.00 11.90 - 25.45 160.00 12.70 - 25.50 145.00 11.80 - 25.55 151.00 12.30 - 25.60 161.00 12.40 - 25.65 187.00 13.60 - 25.70 154.00 12.10 - 25.75 157.00 12.40 - 25.80 169.00 12.60 - 25.85 181.00 13.40 - 25.90 156.00 12.10 - 25.95 185.00 13.40 - 26.00 192.00 13.40 - 26.05 153.00 12.20 - 26.10 149.00 11.80 - 26.15 154.00 12.20 - 26.20 152.00 11.90 - 26.25 179.00 13.20 - 26.30 180.00 12.90 - 26.35 160.00 12.50 - 26.40 174.00 12.60 - 26.45 145.00 11.80 - 26.50 171.00 12.50 - 26.55 162.00 12.50 - 26.60 154.00 11.80 - 26.65 153.00 12.10 - 26.70 162.00 12.10 - 26.75 160.00 12.40 - 26.80 150.00 11.70 - 26.85 189.00 13.40 - 26.90 168.00 12.40 - 26.95 144.00 11.70 - 27.00 147.00 11.60 - 27.05 155.00 12.20 - 27.10 174.00 12.60 - 27.15 169.00 12.70 - 27.20 174.00 12.60 - 27.25 164.00 12.60 - 27.30 146.00 11.60 - 27.35 149.00 12.00 - 27.40 155.00 11.90 - 27.45 155.00 12.20 - 27.50 168.00 12.40 - 27.55 131.00 11.20 - 27.60 159.00 12.10 - 27.65 181.00 13.20 - 27.70 146.00 11.60 - 27.75 188.00 13.50 - 27.80 162.00 12.20 - 27.85 161.00 12.50 - 27.90 176.00 12.70 - 27.95 152.00 12.10 - 28.00 170.00 12.40 - 28.05 152.00 12.00 - 28.10 158.00 12.00 - 28.15 168.00 12.60 - 28.20 161.00 12.10 - 28.25 184.00 13.30 - 28.30 166.00 12.30 - 28.35 193.00 13.60 - 28.40 157.00 12.00 - 28.45 167.00 12.60 - 28.50 158.00 12.00 - 28.55 135.00 11.40 - 28.60 150.00 11.70 - 28.65 167.00 12.70 - 28.70 161.00 12.20 - 28.75 157.00 12.30 - 28.80 153.00 11.80 - 28.85 161.00 12.50 - 28.90 163.00 12.20 - 28.95 133.00 11.40 - 29.00 169.00 12.50 - 29.05 162.00 12.50 - 29.10 161.00 12.20 - 29.15 163.00 12.60 - 29.20 144.00 11.60 - 29.25 178.00 13.20 - 29.30 161.00 12.20 - 29.35 141.00 11.80 - 29.40 169.00 12.50 - 29.45 160.00 12.50 - 29.50 177.00 12.90 - 29.55 174.00 13.10 - 29.60 157.00 12.10 - 29.65 176.00 13.20 - 29.70 179.00 13.00 - 29.75 166.00 12.90 - 29.80 162.00 12.40 - 29.85 147.00 12.20 - 29.90 152.00 12.00 - 29.95 171.00 13.20 - 30.00 178.00 13.10 - 30.05 208.00 14.60 - 30.10 178.00 13.20 - 30.15 149.00 12.40 - 30.20 181.00 13.30 - 30.25 162.00 13.00 - 30.30 177.00 13.20 - 30.35 165.00 13.10 - 30.40 177.00 13.30 - 30.45 158.00 12.90 - 30.50 157.00 12.60 - 30.55 163.00 13.10 - 30.60 144.00 12.00 - 30.65 156.00 12.80 - 30.70 176.00 13.30 - 30.75 179.00 13.70 - 30.80 174.00 13.20 - 30.85 182.00 13.80 - 30.90 161.00 12.70 - 30.95 166.00 13.10 - 31.00 168.00 13.00 - 31.05 153.00 12.60 - 31.10 156.00 12.40 - 31.15 174.00 13.40 - 31.20 167.00 12.80 - 31.25 192.00 14.00 - 31.30 154.00 12.30 - 31.35 166.00 13.00 - 31.40 169.00 12.90 - 31.45 185.00 13.70 - 31.50 165.00 12.60 - 31.55 163.00 12.80 - 31.60 173.00 12.90 - 31.65 169.00 13.00 - 31.70 188.00 13.40 - 31.75 195.00 13.90 - 31.80 195.00 13.60 - 31.85 221.00 14.70 - 31.90 229.00 14.70 - 31.95 302.00 17.20 - 32.00 327.00 17.50 - 32.05 380.00 19.30 - 32.10 358.00 18.30 - 32.15 394.00 19.60 - 32.20 373.00 18.70 - 32.25 362.00 18.70 - 32.30 306.00 16.90 - 32.35 276.00 16.40 - 32.40 237.00 14.80 - 32.45 203.00 14.00 - 32.50 178.00 12.80 - 32.55 199.00 13.90 - 32.60 167.00 12.40 - 32.65 185.00 13.40 - 32.70 180.00 12.90 - 32.75 178.00 13.10 - 32.80 145.00 11.50 - 32.85 176.00 13.00 - 32.90 177.00 12.70 - 32.95 182.00 13.20 - 33.00 167.00 12.40 - 33.05 152.00 12.10 - 33.10 144.00 11.50 - 33.15 170.00 12.80 - 33.20 156.00 11.90 - 33.25 154.00 12.20 - 33.30 180.00 12.80 - 33.35 176.00 13.00 - 33.40 183.00 12.90 - 33.45 162.00 12.40 - 33.50 180.00 12.80 - 33.55 165.00 12.60 - 33.60 174.00 12.50 - 33.65 179.00 13.00 - 33.70 152.00 11.70 - 33.75 182.00 13.10 - 33.80 184.00 12.90 - 33.85 166.00 12.50 - 33.90 182.00 12.80 - 33.95 162.00 12.40 - 34.00 174.00 12.50 - 34.05 153.00 12.00 - 34.10 182.00 12.80 - 34.15 180.00 13.00 - 34.20 167.00 12.20 - 34.25 173.00 12.70 - 34.30 153.00 11.70 - 34.35 160.00 12.30 - 34.40 180.00 12.70 - 34.45 168.00 12.50 - 34.50 167.00 12.20 - 34.55 176.00 12.80 - 34.60 165.00 12.10 - 34.65 174.00 12.80 - 34.70 161.00 12.00 - 34.75 178.00 12.90 - 34.80 170.00 12.30 - 34.85 166.00 12.50 - 34.90 173.00 12.40 - 34.95 158.00 12.20 - 35.00 166.00 12.20 - 35.05 170.00 12.60 - 35.10 162.00 12.00 - 35.15 183.00 13.10 - 35.20 176.00 12.50 - 35.25 171.00 12.60 - 35.30 174.00 12.50 - 35.35 179.00 12.90 - 35.40 176.00 12.50 - 35.45 193.00 13.40 - 35.50 180.00 12.70 - 35.55 188.00 13.30 - 35.60 177.00 12.60 - 35.65 176.00 12.90 - 35.70 171.00 12.40 - 35.75 185.00 13.30 - 35.80 178.00 12.70 - 35.85 152.00 12.10 - 35.90 160.00 12.10 - 35.95 187.00 13.50 - 36.00 167.00 12.40 - 36.05 181.00 13.30 - 36.10 166.00 12.40 - 36.15 165.00 12.80 - 36.20 170.00 12.70 - 36.25 197.00 14.10 - 36.30 179.00 13.10 - 36.35 172.00 13.20 - 36.40 181.00 13.30 - 36.45 174.00 13.40 - 36.50 162.00 12.60 - 36.55 166.00 13.10 - 36.60 158.00 12.50 - 36.65 199.00 14.40 - 36.70 188.00 13.70 - 36.75 177.00 13.70 - 36.80 167.00 12.90 - 36.85 156.00 12.90 - 36.90 174.00 13.20 - 36.95 176.00 13.70 - 37.00 152.00 12.40 - 37.05 191.00 14.40 - 37.10 151.00 12.50 - 37.15 202.00 14.80 - 37.20 191.00 14.00 - 37.25 161.00 13.20 - 37.30 199.00 14.30 - 37.35 175.00 13.70 - 37.40 146.00 12.30 - 37.45 181.00 14.00 - 37.50 221.00 15.00 - 37.55 194.00 14.40 - 37.60 158.00 12.70 - 37.65 171.00 13.50 - 37.70 172.00 13.20 - 37.75 168.00 13.30 - 37.80 192.00 13.90 - 37.85 185.00 13.90 - 37.90 193.00 13.90 - 37.95 178.00 13.60 - 38.00 195.00 13.90 - 38.05 175.00 13.40 - 38.10 178.00 13.20 - 38.15 173.00 13.30 - 38.20 195.00 13.70 - 38.25 194.00 13.90 - 38.30 191.00 13.50 - 38.35 178.00 13.30 - 38.40 184.00 13.30 - 38.45 186.00 13.50 - 38.50 202.00 13.80 - 38.55 200.00 14.00 - 38.60 210.00 14.00 - 38.65 198.00 13.90 - 38.70 225.00 14.50 - 38.75 209.00 14.30 - 38.80 229.00 14.60 - 38.85 197.00 13.90 - 38.90 220.00 14.30 - 38.95 215.00 14.40 - 39.00 242.00 15.00 - 39.05 340.00 18.10 - 39.10 441.00 20.20 - 39.15 654.00 25.10 - 39.20 962.00 29.70 - 39.25 1477.00 37.70 - 39.30 2012.00 43.00 - 39.35 2634.00 50.20 - 39.40 3115.00 53.40 - 39.45 3467.00 57.50 - 39.50 3532.00 56.70 - 39.55 3337.00 56.30 - 39.60 2595.00 48.60 - 39.65 1943.00 42.90 - 39.70 1251.00 33.70 - 39.75 828.00 28.00 - 39.80 525.00 21.80 - 39.85 377.00 18.80 - 39.90 294.00 16.30 - 39.95 233.00 14.80 - 40.00 233.00 14.50 - 40.05 253.00 15.40 - 40.10 253.00 15.10 - 40.15 213.00 14.10 - 40.20 196.00 13.20 - 40.25 222.00 14.40 - 40.30 172.00 12.40 - 40.35 218.00 14.30 - 40.40 206.00 13.60 - 40.45 195.00 13.60 - 40.50 209.00 13.70 - 40.55 192.00 13.50 - 40.60 197.00 13.30 - 40.65 188.00 13.30 - 40.70 202.00 13.50 - 40.75 208.00 14.00 - 40.80 184.00 12.90 - 40.85 177.00 13.00 - 40.90 202.00 13.50 - 40.95 198.00 13.80 - 41.00 203.00 13.60 - 41.05 193.00 13.60 - 41.10 188.00 13.10 - 41.15 211.00 14.20 - 41.20 189.00 13.10 - 41.25 200.00 13.90 - 41.30 198.00 13.50 - 41.35 203.00 14.00 - 41.40 197.00 13.40 - 41.45 190.00 13.60 - 41.50 212.00 14.00 - 41.55 185.00 13.40 - 41.60 228.00 14.50 - 41.65 167.00 12.80 - 41.70 207.00 13.90 - 41.75 187.00 13.60 - 41.80 190.00 13.30 - 41.85 192.00 13.80 - 41.90 185.00 13.20 - 41.95 161.00 12.70 - 42.00 187.00 13.30 - 42.05 191.00 13.80 - 42.10 159.00 12.30 - 42.15 170.00 13.10 - 42.20 182.00 13.20 - 42.25 186.00 13.70 - 42.30 192.00 13.60 - 42.35 178.00 13.50 - 42.40 186.00 13.40 - 42.45 180.00 13.50 - 42.50 178.00 13.10 - 42.55 182.00 13.60 - 42.60 179.00 13.20 - 42.65 203.00 14.50 - 42.70 191.00 13.70 - 42.75 207.00 14.60 - 42.80 183.00 13.40 - 42.85 180.00 13.60 - 42.90 191.00 13.70 - 42.95 187.00 13.90 - 43.00 184.00 13.50 - 43.05 182.00 13.80 - 43.10 178.00 13.30 - 43.15 169.00 13.30 - 43.20 158.00 12.60 - 43.25 180.00 13.70 - 43.30 174.00 13.20 - 43.35 184.00 14.00 - 43.40 178.00 13.40 - 43.45 180.00 13.80 - 43.50 144.00 12.00 - 43.55 169.00 13.40 - 43.60 177.00 13.30 - 43.65 156.00 12.80 - 43.70 148.00 12.20 - 43.75 159.00 12.90 - 43.80 195.00 14.00 - 43.85 186.00 14.00 - 43.90 180.00 13.40 - 43.95 192.00 14.10 - 44.00 186.00 13.50 - 44.05 180.00 13.60 - 44.10 174.00 13.10 - 44.15 181.00 13.60 - 44.20 178.00 13.20 - 44.25 189.00 13.80 - 44.30 206.00 14.10 - 44.35 183.00 13.60 - 44.40 161.00 12.40 - 44.45 170.00 13.00 - 44.50 203.00 13.90 - 44.55 168.00 12.90 - 44.60 199.00 13.70 - 44.65 192.00 13.70 - 44.70 192.00 13.40 - 44.75 200.00 14.00 - 44.80 206.00 13.90 - 44.85 193.00 13.70 - 44.90 188.00 13.20 - 44.95 200.00 13.90 - 45.00 193.00 13.40 - 45.05 203.00 14.00 - 45.10 212.00 14.00 - 45.15 197.00 13.80 - 45.20 219.00 14.20 - 45.25 219.00 14.60 - 45.30 226.00 14.50 - 45.35 282.00 16.50 - 45.40 353.00 18.10 - 45.45 469.00 21.30 - 45.50 741.00 26.20 - 45.55 1176.00 33.70 - 45.60 1577.00 38.10 - 45.65 2122.00 45.30 - 45.70 2726.00 50.10 - 45.75 2990.00 53.70 - 45.80 2991.00 52.50 - 45.85 2796.00 52.00 - 45.90 2372.00 46.80 - 45.95 1752.00 41.20 - 46.00 1209.00 33.40 - 46.05 824.00 28.30 - 46.10 512.00 21.80 - 46.15 353.00 18.60 - 46.20 273.00 15.90 - 46.25 259.00 15.90 - 46.30 233.00 14.80 - 46.35 220.00 14.70 - 46.40 228.00 14.60 - 46.45 231.00 15.10 - 46.50 218.00 14.30 - 46.55 210.00 14.40 - 46.60 212.00 14.20 - 46.65 187.00 13.60 - 46.70 207.00 14.00 - 46.75 212.00 14.50 - 46.80 188.00 13.40 - 46.85 178.00 13.30 - 46.90 186.00 13.30 - 46.95 192.00 13.80 - 47.00 192.00 13.50 - 47.05 186.00 13.60 - 47.10 208.00 14.10 - 47.15 199.00 14.10 - 47.20 165.00 12.50 - 47.25 212.00 14.50 - 47.30 191.00 13.50 - 47.35 185.00 13.60 - 47.40 171.00 12.70 - 47.45 176.00 13.20 - 47.50 179.00 13.00 - 47.55 187.00 13.60 - 47.60 181.00 13.10 - 47.65 173.00 13.10 - 47.70 167.00 12.50 - 47.75 182.00 13.40 - 47.80 171.00 12.70 - 47.85 185.00 13.50 - 47.90 177.00 12.90 - 47.95 154.00 12.40 - 48.00 200.00 13.70 - 48.05 177.00 13.30 - 48.10 184.00 13.20 - 48.15 166.00 12.80 - 48.20 181.00 13.10 - 48.25 208.00 14.40 - 48.30 186.00 13.20 - 48.35 164.00 12.70 - 48.40 196.00 13.60 - 48.45 169.00 12.90 - 48.50 173.00 12.70 - 48.55 200.00 14.10 - 48.60 163.00 12.40 - 48.65 173.00 13.10 - 48.70 187.00 13.30 - 48.75 177.00 13.30 - 48.80 200.00 13.80 - 48.85 171.00 13.00 - 48.90 192.00 13.50 - 48.95 178.00 13.30 - 49.00 169.00 12.70 - 49.05 160.00 12.70 - 49.10 182.00 13.20 - 49.15 173.00 13.20 - 49.20 170.00 12.80 - 49.25 181.00 13.60 - 49.30 170.00 12.90 - 49.35 164.00 13.00 - 49.40 166.00 12.70 - 49.45 174.00 13.40 - 49.50 173.00 13.10 - 49.55 137.00 11.90 - 49.60 166.00 12.80 - 49.65 194.00 14.20 - 49.70 160.00 12.60 - 49.75 152.00 12.50 - 49.80 180.00 13.30 - 49.85 160.00 12.90 - 49.90 149.00 12.20 - 49.95 172.00 13.40 - 50.00 170.00 13.00 - 50.05 175.00 13.50 - 50.10 162.00 12.70 - 50.15 168.00 13.20 - 50.20 186.00 13.60 - 50.25 179.00 13.60 - 50.30 165.00 12.70 - 50.35 155.00 12.60 - 50.40 170.00 12.90 - 50.45 162.00 12.80 - 50.50 157.00 12.30 - 50.55 173.00 13.20 - 50.60 149.00 12.00 - 50.65 167.00 13.00 - 50.70 165.00 12.60 - 50.75 157.00 12.50 - 50.80 177.00 13.00 - 50.85 187.00 13.60 - 50.90 155.00 12.10 - 50.95 194.00 13.70 - 51.00 147.00 11.70 - 51.05 169.00 12.80 - 51.10 166.00 12.40 - 51.15 193.00 13.60 - 51.20 168.00 12.40 - 51.25 188.00 13.40 - 51.30 182.00 12.80 - 51.35 180.00 13.10 - 51.40 177.00 12.70 - 51.45 188.00 13.30 - 51.50 187.00 13.00 - 51.55 178.00 12.90 - 51.60 177.00 12.60 - 51.65 184.00 13.10 - 51.70 172.00 12.40 - 51.75 188.00 13.30 - 51.80 194.00 13.20 - 51.85 179.00 12.90 - 51.90 176.00 12.50 - 51.95 180.00 12.90 - 52.00 169.00 12.20 - 52.05 178.00 12.90 - 52.10 165.00 12.10 - 52.15 149.00 11.70 - 52.20 168.00 12.20 - 52.25 157.00 12.10 - 52.30 151.00 11.60 - 52.35 181.00 13.00 - 52.40 172.00 12.40 - 52.45 178.00 12.90 - 52.50 179.00 12.60 - 52.55 171.00 12.60 - 52.60 129.00 10.70 - 52.65 180.00 13.00 - 52.70 154.00 11.70 - 52.75 182.00 13.10 - 52.80 166.00 12.20 - 52.85 156.00 12.10 - 52.90 164.00 12.10 - 52.95 166.00 12.50 - 53.00 176.00 12.50 - 53.05 182.00 13.10 - 53.10 173.00 12.50 - 53.15 160.00 12.30 - 53.20 169.00 12.30 - 53.25 162.00 12.30 - 53.30 164.00 12.10 - 53.35 165.00 12.40 - 53.40 177.00 12.60 - 53.45 173.00 12.80 - 53.50 158.00 11.90 - 53.55 164.00 12.40 - 53.60 175.00 12.50 - 53.65 166.00 12.50 - 53.70 161.00 12.00 - 53.75 167.00 12.50 - 53.80 136.00 11.00 - 53.85 167.00 12.50 - 53.90 152.00 11.70 - 53.95 159.00 12.20 - 54.00 172.00 12.40 - 54.05 179.00 12.90 - 54.10 169.00 12.20 - 54.15 165.00 12.40 - 54.20 166.00 12.10 - 54.25 162.00 12.30 - 54.30 175.00 12.40 - 54.35 162.00 12.30 - 54.40 145.00 11.40 - 54.45 148.00 11.70 - 54.50 157.00 11.80 - 54.55 176.00 12.80 - 54.60 162.00 12.00 - 54.65 153.00 12.00 - 54.70 178.00 12.60 - 54.75 147.00 11.80 - 54.80 146.00 11.50 - 54.85 170.00 12.70 - 54.90 155.00 11.80 - 54.95 170.00 12.70 - 55.00 142.00 11.30 - 55.05 154.00 12.10 - 55.10 150.00 11.70 - 55.15 145.00 11.80 - 55.20 151.00 11.80 - 55.25 162.00 12.50 - 55.30 153.00 11.90 - 55.35 170.00 12.90 - 55.40 153.00 11.90 - 55.45 156.00 12.40 - 55.50 163.00 12.40 - 55.55 149.00 12.20 - 55.60 135.00 11.30 - 55.65 158.00 12.60 - 55.70 144.00 11.70 - 55.75 152.00 12.40 - 55.80 165.00 12.70 - 55.85 164.00 13.00 - 55.90 175.00 13.10 - 55.95 150.00 12.40 - 56.00 168.00 12.90 - 56.05 159.00 12.90 - 56.10 187.00 13.60 - 56.15 170.00 13.30 - 56.20 159.00 12.60 - 56.25 148.00 12.50 - 56.30 159.00 12.60 - 56.35 174.00 13.50 - 56.40 195.00 14.00 - 56.45 219.00 15.10 - 56.50 216.00 14.70 - 56.55 271.00 16.80 - 56.60 337.00 18.30 - 56.65 417.00 20.80 - 56.70 390.00 19.70 - 56.75 414.00 20.70 - 56.80 388.00 19.60 - 56.85 317.00 18.10 - 56.90 307.00 17.40 - 56.95 250.00 16.00 - 57.00 205.00 14.20 - 57.05 167.00 13.00 - 57.10 179.00 13.20 - 57.15 159.00 12.70 - 57.20 170.00 12.80 - 57.25 168.00 13.00 - 57.30 180.00 13.10 - 57.35 144.00 12.00 - 57.40 178.00 13.00 - 57.45 203.00 14.20 - 57.50 159.00 12.30 - 57.55 165.00 12.80 - 57.60 164.00 12.40 - 57.65 135.00 11.60 - 57.70 157.00 12.20 - 57.75 162.00 12.70 - 57.80 175.00 12.90 - 57.85 161.00 12.60 - 57.90 174.00 12.80 - 57.95 187.00 13.70 - 58.00 164.00 12.50 - 58.05 188.00 13.70 - 58.10 163.00 12.40 - 58.15 177.00 13.30 - 58.20 181.00 13.10 - 58.25 156.00 12.50 - 58.30 163.00 12.40 - 58.35 190.00 13.80 - 58.40 162.00 12.40 - 58.45 186.00 13.70 - 58.50 169.00 12.70 - 58.55 160.00 12.70 - 58.60 171.00 12.80 - 58.65 160.00 12.60 - 58.70 174.00 12.90 - 58.75 163.00 12.70 - 58.80 180.00 13.10 - 58.85 176.00 13.20 - 58.90 174.00 12.80 - 58.95 177.00 13.30 - 59.00 186.00 13.30 - 59.05 157.00 12.40 - 59.10 188.00 13.30 - 59.15 162.00 12.60 - 59.20 160.00 12.20 - 59.25 196.00 13.90 - 59.30 178.00 12.90 - 59.35 188.00 13.50 - 59.40 161.00 12.30 - 59.45 157.00 12.30 - 59.50 183.00 13.00 - 59.55 169.00 12.80 - 59.60 150.00 11.80 - 59.65 195.00 13.70 - 59.70 175.00 12.70 - 59.75 160.00 12.40 - 59.80 168.00 12.40 - 59.85 191.00 13.50 - 59.90 181.00 12.80 - 59.95 168.00 12.70 - 60.00 181.00 12.80 - 60.05 158.00 12.20 - 60.10 160.00 12.00 - 60.15 151.00 12.00 - 60.20 171.00 12.40 - 60.25 167.00 12.60 - 60.30 160.00 12.00 - 60.35 157.00 12.10 - 60.40 172.00 12.40 - 60.45 140.00 11.50 - 60.50 172.00 12.40 - 60.55 150.00 11.90 - 60.60 179.00 12.70 - 60.65 153.00 12.00 - 60.70 170.00 12.40 - 60.75 184.00 13.10 - 60.80 158.00 11.90 - 60.85 177.00 12.90 - 60.90 159.00 12.00 - 60.95 157.00 12.20 - 61.00 168.00 12.30 - 61.05 154.00 12.00 - 61.10 170.00 12.40 - 61.15 147.00 11.80 - 61.20 161.00 12.10 - 61.25 175.00 12.90 - 61.30 170.00 12.40 - 61.35 153.00 12.10 - 61.40 165.00 12.30 - 61.45 164.00 12.50 - 61.50 174.00 12.60 - 61.55 160.00 12.40 - 61.60 188.00 13.20 - 61.65 182.00 13.30 - 61.70 197.00 13.50 - 61.75 163.00 12.60 - 61.80 176.00 12.80 - 61.85 157.00 12.40 - 61.90 166.00 12.40 - 61.95 173.00 13.10 - 62.00 167.00 12.50 - 62.05 175.00 13.20 - 62.10 143.00 11.60 - 62.15 148.00 12.10 - 62.20 178.00 13.00 - 62.25 180.00 13.40 - 62.30 141.00 11.60 - 62.35 202.00 14.30 - 62.40 172.00 12.80 - 62.45 169.00 13.00 - 62.50 143.00 11.80 - 62.55 146.00 12.20 - 62.60 169.00 12.80 - 62.65 146.00 12.30 - 62.70 156.00 12.30 - 62.75 147.00 12.30 - 62.80 158.00 12.40 - 62.85 178.00 13.50 - 62.90 163.00 12.60 - 62.95 168.00 13.10 - 63.00 164.00 12.60 - 63.05 180.00 13.60 - 63.10 189.00 13.60 - 63.15 164.00 12.90 - 63.20 181.00 13.20 - 63.25 179.00 13.50 - 63.30 147.00 11.90 - 63.35 179.00 13.50 - 63.40 150.00 12.00 - 63.45 168.00 12.90 - 63.50 156.00 12.20 - 63.55 181.00 13.40 - 63.60 170.00 12.70 - 63.65 181.00 13.30 - 63.70 184.00 13.10 - 63.75 153.00 12.20 - 63.80 166.00 12.40 - 63.85 166.00 12.60 - 63.90 169.00 12.50 - 63.95 175.00 12.90 - 64.00 157.00 12.00 - 64.05 165.00 12.40 - 64.10 169.00 12.30 - 64.15 164.00 12.40 - 64.20 181.00 12.80 - 64.25 189.00 13.30 - 64.30 179.00 12.60 - 64.35 157.00 12.10 - 64.40 189.00 13.00 - 64.45 167.00 12.50 - 64.50 178.00 12.50 - 64.55 144.00 11.60 - 64.60 180.00 12.60 - 64.65 182.00 12.90 - 64.70 199.00 13.20 - 64.75 172.00 12.60 - 64.80 191.00 12.90 - 64.85 166.00 12.30 - 64.90 157.00 11.70 - 64.95 197.00 13.50 - 65.00 204.00 13.40 - 65.05 183.00 13.00 - 65.10 189.00 12.90 - 65.15 189.00 13.20 - 65.20 170.00 12.20 - 65.25 188.00 13.20 - 65.30 176.00 12.40 - 65.35 172.00 12.60 - 65.40 182.00 12.70 - 65.45 205.00 13.80 - 65.50 191.00 13.00 - 65.55 192.00 13.30 - 65.60 190.00 12.90 - 65.65 194.00 13.40 - 65.70 212.00 13.70 - 65.75 221.00 14.30 - 65.80 227.00 14.20 - 65.85 227.00 14.60 - 65.90 239.00 14.60 - 65.95 261.00 15.60 - 66.00 301.00 16.40 - 66.05 409.00 19.60 - 66.10 559.00 22.30 - 66.15 820.00 27.80 - 66.20 1276.00 33.90 - 66.25 1776.00 41.00 - 66.30 2322.00 45.70 - 66.35 2880.00 52.20 - 66.40 3051.00 52.50 - 66.45 2980.00 53.10 - 66.50 2572.00 48.20 - 66.55 1961.00 43.20 - 66.60 1315.00 34.50 - 66.65 919.00 29.60 - 66.70 548.00 22.40 - 66.75 405.00 19.70 - 66.80 299.00 16.50 - 66.85 309.00 17.20 - 66.90 279.00 15.90 - 66.95 281.00 16.40 - 67.00 235.00 14.70 - 67.05 239.00 15.10 - 67.10 212.00 14.00 - 67.15 228.00 14.80 - 67.20 231.00 14.50 - 67.25 198.00 13.80 - 67.30 223.00 14.30 - 67.35 201.00 13.90 - 67.40 208.00 13.80 - 67.45 207.00 14.10 - 67.50 217.00 14.10 - 67.55 196.00 13.70 - 67.60 182.00 12.90 - 67.65 182.00 13.20 - 67.70 186.00 13.10 - 67.75 176.00 13.00 - 67.80 192.00 13.30 - 67.85 215.00 14.50 - 67.90 178.00 12.90 - 67.95 191.00 13.70 - 68.00 178.00 12.90 - 68.05 185.00 13.50 - 68.10 171.00 12.70 - 68.15 174.00 13.30 - 68.20 193.00 13.60 - 68.25 182.00 13.60 - 68.30 178.00 13.10 - 68.35 196.00 14.10 - 68.40 178.00 13.10 - 68.45 173.00 13.30 - 68.50 175.00 13.10 - 68.55 178.00 13.60 - 68.60 177.00 13.20 - 68.65 176.00 13.60 - 68.70 200.00 14.10 - 68.75 177.00 13.60 - 68.80 185.00 13.60 - 68.85 167.00 13.20 - 68.90 158.00 12.60 - 68.95 176.00 13.60 - 69.00 192.00 13.80 - 69.05 174.00 13.50 - 69.10 154.00 12.40 - 69.15 153.00 12.70 - 69.20 167.00 12.90 - 69.25 168.00 13.30 - 69.30 167.00 12.90 - 69.35 163.00 13.10 - 69.40 157.00 12.50 - 69.45 185.00 13.90 - 69.50 151.00 12.30 - 69.55 176.00 13.50 - 69.60 187.00 13.60 - 69.65 170.00 13.20 - 69.70 164.00 12.70 - 69.75 204.00 14.50 - 69.80 169.00 12.80 - 69.85 191.00 13.90 - 69.90 177.00 13.10 - 69.95 157.00 12.60 - 70.00 173.00 12.80 - 70.05 199.00 14.10 - 70.10 168.00 12.60 - 70.15 191.00 13.70 - 70.20 165.00 12.40 - 70.25 156.00 12.30 - 70.30 163.00 12.30 - 70.35 149.00 12.00 - 70.40 199.00 13.60 - 70.45 158.00 12.30 - 70.50 158.00 12.10 - 70.55 150.00 12.00 - 70.60 197.00 13.50 - 70.65 167.00 12.60 - 70.70 180.00 12.80 - 70.75 187.00 13.40 - 70.80 190.00 13.20 - 70.85 169.00 12.70 - 70.90 214.00 14.00 - 70.95 188.00 13.50 - 71.00 200.00 13.50 - 71.05 186.00 13.30 - 71.10 169.00 12.40 - 71.15 166.00 12.60 - 71.20 175.00 12.60 - 71.25 170.00 12.80 - 71.30 191.00 13.20 - 71.35 185.00 13.30 - 71.40 191.00 13.20 - 71.45 181.00 13.20 - 71.50 188.00 13.10 - 71.55 164.00 12.60 - 71.60 185.00 13.00 - 71.65 168.00 12.70 - 71.70 168.00 12.40 - 71.75 167.00 12.60 - 71.80 158.00 12.00 - 71.85 173.00 12.90 - 71.90 177.00 12.70 - 71.95 193.00 13.60 - 72.00 190.00 13.20 - 72.05 174.00 12.90 - 72.10 161.00 12.10 - 72.15 147.00 11.80 - 72.20 165.00 12.30 - 72.25 188.00 13.40 - 72.30 172.00 12.50 - 72.35 176.00 12.90 - 72.40 167.00 12.30 - 72.45 186.00 13.30 - 72.50 178.00 12.70 - 72.55 158.00 12.20 - 72.60 168.00 12.30 - 72.65 180.00 13.10 - 72.70 154.00 11.80 - 72.75 162.00 12.40 - 72.80 168.00 12.30 - 72.85 194.00 13.50 - 72.90 164.00 12.10 - 72.95 169.00 12.60 - 73.00 160.00 12.00 - 73.05 164.00 12.50 - 73.10 171.00 12.40 - 73.15 169.00 12.60 - 73.20 167.00 12.30 - 73.25 150.00 12.00 - 73.30 173.00 12.50 - 73.35 183.00 13.20 - 73.40 169.00 12.40 - 73.45 180.00 13.10 - 73.50 173.00 12.50 - 73.55 195.00 13.70 - 73.60 178.00 12.80 - 73.65 193.00 13.60 - 73.70 179.00 12.80 - 73.75 153.00 12.20 - 73.80 169.00 12.40 - 73.85 165.00 12.60 - 73.90 172.00 12.60 - 73.95 171.00 12.80 - 74.00 178.00 12.80 - 74.05 180.00 13.20 - 74.10 168.00 12.50 - 74.15 169.00 12.80 - 74.20 190.00 13.20 - 74.25 170.00 12.80 - 74.30 178.00 12.80 - 74.35 158.00 12.40 - 74.40 185.00 13.10 - 74.45 181.00 13.30 - 74.50 173.00 12.70 - 74.55 163.00 12.60 - 74.60 184.00 13.10 - 74.65 181.00 13.40 - 74.70 192.00 13.50 - 74.75 166.00 12.90 - 74.80 168.00 12.60 - 74.85 200.00 14.20 - 74.90 188.00 13.40 - 74.95 190.00 13.90 - 75.00 211.00 14.30 - 75.05 172.00 13.20 - 75.10 198.00 13.90 - 75.15 230.00 15.40 - 75.20 264.00 16.10 - 75.25 227.00 15.20 - 75.30 289.00 16.80 - 75.35 290.00 17.20 - 75.40 284.00 16.70 - 75.45 250.00 16.10 - 75.50 233.00 15.10 - 75.55 239.00 15.70 - 75.60 239.00 15.30 - 75.65 204.00 14.40 - 75.70 178.00 13.20 - 75.75 189.00 13.90 - 75.80 202.00 14.00 - 75.85 181.00 13.50 - 75.90 190.00 13.50 - 75.95 177.00 13.30 - 76.00 199.00 13.80 - 76.05 193.00 13.90 - 76.10 170.00 12.70 - 76.15 170.00 13.00 - 76.20 165.00 12.50 - 76.25 192.00 13.70 - 76.30 171.00 12.70 - 76.35 169.00 12.80 - 76.40 168.00 12.50 - 76.45 183.00 13.30 - 76.50 173.00 12.60 - 76.55 178.00 13.10 - 76.60 175.00 12.70 - 76.65 191.00 13.50 - 76.70 166.00 12.30 - 76.75 187.00 13.40 - 76.80 191.00 13.20 - 76.85 184.00 13.30 - 76.90 168.00 12.40 - 76.95 177.00 13.00 - 77.00 205.00 13.70 - 77.05 188.00 13.40 - 77.10 166.00 12.30 - 77.15 180.00 13.10 - 77.20 179.00 12.80 - 77.25 179.00 13.10 - 77.30 163.00 12.20 - 77.35 188.00 13.40 - 77.40 169.00 12.40 - 77.45 179.00 13.00 - 77.50 169.00 12.40 - 77.55 201.00 13.80 - 77.60 184.00 12.90 - 77.65 187.00 13.30 - 77.70 207.00 13.70 - 77.75 170.00 12.70 - 77.80 193.00 13.20 - 77.85 189.00 13.50 - 77.90 205.00 13.70 - 77.95 183.00 13.20 - 78.00 179.00 12.80 - 78.05 188.00 13.40 - 78.10 194.00 13.30 - 78.15 220.00 14.50 - 78.20 195.00 13.40 - 78.25 176.00 13.00 - 78.30 208.00 13.80 - 78.35 185.00 13.30 - 78.40 217.00 14.10 - 78.45 203.00 14.00 - 78.50 200.00 13.50 - 78.55 196.00 13.70 - 78.60 197.00 13.40 - 78.65 217.00 14.40 - 78.70 179.00 12.80 - 78.75 184.00 13.30 - 78.80 187.00 13.10 - 78.85 219.00 14.40 - 78.90 193.00 13.30 - 78.95 214.00 14.30 - 79.00 207.00 13.70 - 79.05 199.00 13.80 - 79.10 224.00 14.30 - 79.15 244.00 15.20 - 79.20 217.00 14.10 - 79.25 266.00 15.90 - 79.30 281.00 16.00 - 79.35 425.00 20.10 - 79.40 527.00 21.90 - 79.45 735.00 26.50 - 79.50 1057.00 31.10 - 79.55 1483.00 37.70 - 79.60 1955.00 42.20 - 79.65 2315.00 47.10 - 79.70 2552.00 48.30 - 79.75 2506.00 49.00 - 79.80 2261.00 45.50 - 79.85 1842.00 42.10 - 79.90 1328.00 34.90 - 79.95 911.00 29.60 - 80.00 592.00 23.40 - 80.05 430.00 20.40 - 80.10 312.00 17.00 - 80.15 284.00 16.60 - 80.20 285.00 16.20 - 80.25 247.00 15.50 - 80.30 250.00 15.20 - 80.35 231.00 15.00 - 80.40 272.00 15.90 - 80.45 235.00 15.20 - 80.50 188.00 13.20 - 80.55 223.00 14.80 - 80.60 218.00 14.30 - 80.65 221.00 14.80 - 80.70 210.00 14.10 - 80.75 199.00 14.00 - 80.80 207.00 14.00 - 80.85 208.00 14.40 - 80.90 178.00 13.00 - 80.95 194.00 14.00 - 81.00 202.00 13.90 - 81.05 226.00 15.10 - 81.10 209.00 14.20 - 81.15 194.00 14.10 - 81.20 179.00 13.20 - 81.25 183.00 13.70 - 81.30 187.00 13.50 - 81.35 198.00 14.30 - 81.40 198.00 14.00 - 81.45 209.00 14.70 - 81.50 187.00 13.60 - 81.55 211.00 14.90 - 81.60 198.00 14.10 - 81.65 164.00 13.10 - 81.70 200.00 14.10 - 81.75 212.00 14.90 - 81.80 197.00 14.00 - 81.85 191.00 14.20 - 81.90 195.00 14.00 - 81.95 217.00 15.10 - 82.00 189.00 13.80 - 82.05 182.00 13.80 - 82.10 174.00 13.20 - 82.15 182.00 13.80 - 82.20 199.00 14.00 - 82.25 179.00 13.60 - 82.30 197.00 13.90 - 82.35 228.00 15.30 - 82.40 170.00 12.90 - 82.45 203.00 14.40 - 82.50 232.00 15.10 - 82.55 178.00 13.50 - 82.60 216.00 14.50 - 82.65 205.00 14.30 - 82.70 185.00 13.30 - 82.75 212.00 14.60 - 82.80 199.00 13.70 - 82.85 169.00 12.90 - 82.90 165.00 12.50 - 82.95 203.00 14.10 - 83.00 215.00 14.20 - 83.05 199.00 13.90 - 83.10 200.00 13.60 - 83.15 174.00 12.90 - 83.20 192.00 13.30 - 83.25 206.00 14.10 - 83.30 191.00 13.20 - 83.35 203.00 13.90 - 83.40 210.00 13.90 - 83.45 194.00 13.60 - 83.50 245.00 14.90 - 83.55 242.00 15.10 - 83.60 255.00 15.20 - 83.65 310.00 17.10 - 83.70 408.00 19.20 - 83.75 498.00 21.70 - 83.80 729.00 25.60 - 83.85 934.00 29.60 - 83.90 1121.00 31.70 - 83.95 1320.00 35.20 - 84.00 1476.00 36.30 - 84.05 1276.00 34.60 - 84.10 1129.00 31.80 - 84.15 887.00 28.80 - 84.20 643.00 23.90 - 84.25 490.00 21.40 - 84.30 343.00 17.50 - 84.35 284.00 16.30 - 84.40 263.00 15.30 - 84.45 229.00 14.60 - 84.50 235.00 14.50 - 84.55 246.00 15.10 - 84.60 205.00 13.50 - 84.65 217.00 14.20 - 84.70 217.00 13.90 - 84.75 197.00 13.50 - 84.80 195.00 13.10 - 84.85 232.00 14.70 - 84.90 182.00 12.70 - 84.95 192.00 13.40 - 85.00 172.00 12.40 - 85.05 191.00 13.30 - 85.10 200.00 13.30 - 85.15 186.00 13.10 - 85.20 190.00 13.00 - 85.25 211.00 14.00 - 85.30 184.00 12.80 - 85.35 180.00 12.90 - 85.40 182.00 12.70 - 85.45 184.00 13.10 - 85.50 175.00 12.40 - 85.55 176.00 12.80 - 85.60 166.00 12.10 - 85.65 180.00 12.90 - 85.70 195.00 13.10 - 85.75 183.00 13.10 - 85.80 182.00 12.70 - 85.85 168.00 12.50 - 85.90 177.00 12.60 - 85.95 190.00 13.30 - 86.00 178.00 12.60 - 86.05 180.00 13.00 - 86.10 181.00 12.70 - 86.15 177.00 12.90 - 86.20 171.00 12.40 - 86.25 193.00 13.50 - 86.30 181.00 12.70 - 86.35 180.00 13.00 - 86.40 198.00 13.30 - 86.45 177.00 12.90 - 86.50 161.00 12.00 - 86.55 166.00 12.50 - 86.60 176.00 12.60 - 86.65 190.00 13.40 - 86.70 185.00 12.90 - 86.75 173.00 12.90 - 86.80 176.00 12.60 - 86.85 159.00 12.30 - 86.90 188.00 13.10 - 86.95 199.00 13.90 - 87.00 180.00 12.90 - 87.05 164.00 12.60 - 87.10 180.00 12.90 - 87.15 190.00 13.60 - 87.20 179.00 12.90 - 87.25 177.00 13.20 - 87.30 183.00 13.10 - 87.35 174.00 13.20 - 87.40 164.00 12.50 - 87.45 165.00 12.90 - 87.50 185.00 13.30 - 87.55 191.00 13.90 - 87.60 181.00 13.20 - 87.65 143.00 12.10 - 87.70 170.00 12.90 - 87.75 150.00 12.40 - 87.80 187.00 13.50 - 87.85 181.00 13.60 - 87.90 171.00 12.90 - 87.95 179.00 13.60 - 88.00 146.00 12.00 - 88.05 175.00 13.40 - 88.10 182.00 13.40 - 88.15 176.00 13.50 - 88.20 164.00 12.70 - 88.25 152.00 12.60 - 88.30 188.00 13.60 - 88.35 152.00 12.50 - 88.40 172.00 13.00 - 88.45 140.00 12.00 - 88.50 176.00 13.10 - 88.55 168.00 13.10 - 88.60 197.00 13.80 - 88.65 190.00 13.90 - 88.70 176.00 13.10 - 88.75 167.00 13.00 - 88.80 182.00 13.30 - 88.85 175.00 13.20 - 88.90 154.00 12.10 - 88.95 168.00 12.90 - 89.00 187.00 13.30 - 89.05 163.00 12.70 - 89.10 173.00 12.80 - 89.15 161.00 12.50 - 89.20 170.00 12.60 - 89.25 178.00 13.10 - 89.30 174.00 12.70 - 89.35 172.00 12.80 - 89.40 167.00 12.40 - 89.45 168.00 12.60 - 89.50 164.00 12.20 - 89.55 183.00 13.10 - 89.60 141.00 11.30 - 89.65 173.00 12.80 - 89.70 190.00 13.10 - 89.75 180.00 13.00 - 89.80 162.00 12.10 - 89.85 166.00 12.50 - 89.90 164.00 12.10 - 89.95 166.00 12.50 - 90.00 170.00 12.40 - 90.05 176.00 12.90 - 90.10 181.00 12.80 - 90.15 175.00 12.90 - 90.20 161.00 12.10 - 90.25 170.00 12.70 - 90.30 166.00 12.30 - 90.35 175.00 12.90 - 90.40 171.00 12.50 - 90.45 172.00 12.80 - 90.50 183.00 12.90 - 90.55 165.00 12.50 - 90.60 181.00 12.80 - 90.65 168.00 12.70 - 90.70 179.00 12.70 - 90.75 157.00 12.20 - 90.80 172.00 12.50 - 90.85 187.00 13.30 - 90.90 181.00 12.80 - 90.95 163.00 12.40 - 91.00 163.00 12.10 - 91.05 166.00 12.50 - 91.10 161.00 12.00 - 91.15 167.00 12.50 - 91.20 148.00 11.50 - 91.25 175.00 12.80 - 91.30 195.00 13.20 - 91.35 181.00 13.00 - 91.40 173.00 12.50 - 91.45 160.00 12.30 - 91.50 180.00 12.70 - 91.55 183.00 13.10 - 91.60 156.00 11.90 - 91.65 163.00 12.40 - 91.70 175.00 12.50 - 91.75 189.00 13.30 - 91.80 181.00 12.70 - 91.85 186.00 13.20 - 91.90 184.00 12.80 - 91.95 187.00 13.20 - 92.00 191.00 13.10 - 92.05 203.00 13.70 - 92.10 194.00 13.10 - 92.15 237.00 14.80 - 92.20 242.00 14.60 - 92.25 307.00 16.90 - 92.30 299.00 16.30 - 92.35 340.00 17.70 - 92.40 357.00 17.70 - 92.45 354.00 18.10 - 92.50 370.00 18.00 - 92.55 375.00 18.60 - 92.60 303.00 16.30 - 92.65 264.00 15.60 - 92.70 243.00 14.60 - 92.75 207.00 13.90 - 92.80 199.00 13.20 - 92.85 180.00 12.90 - 92.90 202.00 13.30 - 92.95 188.00 13.20 - 93.00 183.00 12.70 - 93.05 170.00 12.60 - 93.10 180.00 12.60 - 93.15 182.00 13.10 - 93.20 186.00 12.90 - 93.25 196.00 13.60 - 93.30 177.00 12.60 - 93.35 198.00 13.70 - 93.40 182.00 12.80 - 93.45 183.00 13.20 - 93.50 184.00 12.90 - 93.55 181.00 13.20 - 93.60 190.00 13.20 - 93.65 176.00 13.10 - 93.70 197.00 13.50 - 93.75 174.00 13.10 - 93.80 159.00 12.20 - 93.85 171.00 13.00 - 93.90 159.00 12.20 - 93.95 170.00 13.00 - 94.00 172.00 12.70 - 94.05 159.00 12.60 - 94.10 160.00 12.30 - 94.15 173.00 13.20 - 94.20 147.00 11.90 - 94.25 143.00 12.00 - 94.30 150.00 12.00 - 94.35 155.00 12.50 - 94.40 160.00 12.40 - 94.45 155.00 12.60 - 94.50 176.00 13.00 - 94.55 198.00 14.20 - 94.60 179.00 13.20 - 94.65 161.00 12.80 - 94.70 175.00 13.10 - 94.75 157.00 12.70 - 94.80 173.00 13.00 - 94.85 168.00 13.10 - 94.90 171.00 12.90 - 94.95 173.00 13.20 - 95.00 183.00 13.30 - 95.05 148.00 12.20 - 95.10 160.00 12.40 - 95.15 171.00 13.10 - 95.20 167.00 12.60 - 95.25 195.00 13.90 - 95.30 175.00 12.90 - 95.35 200.00 14.10 - 95.40 176.00 12.90 - 95.45 175.00 13.10 - 95.50 194.00 13.50 - 95.55 190.00 13.60 - 95.60 154.00 12.00 - 95.65 166.00 12.70 - 95.70 164.00 12.30 - 95.75 166.00 12.60 - 95.80 162.00 12.20 - 95.85 183.00 13.20 - 95.90 149.00 11.60 - 95.95 171.00 12.80 - 96.00 165.00 12.30 - 96.05 181.00 13.10 - 96.10 188.00 13.00 - 96.15 184.00 13.20 - 96.20 162.00 12.10 - 96.25 163.00 12.40 - 96.30 165.00 12.20 - 96.35 183.00 13.10 - 96.40 182.00 12.80 - 96.45 156.00 12.10 - 96.50 159.00 11.90 - 96.55 139.00 11.40 - 96.60 165.00 12.10 - 96.65 164.00 12.40 - 96.70 184.00 12.80 - 96.75 159.00 12.10 - 96.80 159.00 11.90 - 96.85 155.00 12.00 - 96.90 162.00 12.00 - 96.95 157.00 12.00 - 97.00 160.00 11.90 - 97.05 168.00 12.50 - 97.10 168.00 12.20 - 97.15 151.00 11.80 - 97.20 162.00 11.90 - 97.25 163.00 12.20 - 97.30 166.00 12.10 - 97.35 161.00 12.20 - 97.40 158.00 11.80 - 97.45 151.00 11.80 - 97.50 163.00 12.00 - 97.55 179.00 12.80 - 97.60 166.00 12.10 - 97.65 155.00 11.90 - 97.70 160.00 11.80 - 97.75 152.00 11.80 - 97.80 184.00 12.70 - 97.85 175.00 12.60 - 97.90 161.00 11.80 - 97.95 166.00 12.30 - 98.00 150.00 11.40 - 98.05 179.00 12.80 - 98.10 184.00 12.70 - 98.15 151.00 11.80 - 98.20 173.00 12.30 - 98.25 164.00 12.30 - 98.30 178.00 12.50 - 98.35 176.00 12.80 - 98.40 162.00 11.90 - 98.45 173.00 12.70 - 98.50 154.00 11.60 - 98.55 184.00 13.10 - 98.60 142.00 11.20 - 98.65 184.00 13.00 - 98.70 156.00 11.70 - 98.75 177.00 12.80 - 98.80 163.00 12.00 - 98.85 173.00 12.70 - 98.90 180.00 12.70 - 98.95 181.00 13.00 - 99.00 165.00 12.10 - 99.05 177.00 12.90 - 99.10 155.00 11.80 - 99.15 147.00 11.70 - 99.20 163.00 12.10 - 99.25 172.00 12.70 - 99.30 145.00 11.40 - 99.35 156.00 12.10 - 99.40 161.00 12.00 - 99.45 189.00 13.50 - 99.50 182.00 12.90 - 99.55 172.00 12.80 - 99.60 176.00 12.70 - 99.65 166.00 12.60 - 99.70 190.00 13.20 - 99.75 154.00 12.20 - 99.80 198.00 13.50 - 99.85 152.00 12.20 - 99.90 160.00 12.20 - 99.95 174.00 13.00 - 100.00 187.00 13.20 - 100.05 178.00 13.20 - 100.10 149.00 11.80 - 100.15 171.00 13.00 - 100.20 185.00 13.20 - 100.25 207.00 14.40 - 100.30 184.00 13.20 - 100.35 187.00 13.70 - 100.40 231.00 14.90 - 100.45 226.00 15.10 - 100.50 203.00 14.00 - 100.55 214.00 14.80 - 100.60 279.00 16.50 - 100.65 319.00 18.10 - 100.70 397.00 19.70 - 100.75 435.00 21.20 - 100.80 539.00 23.00 - 100.85 665.00 26.30 - 100.90 724.00 26.80 - 100.95 723.00 27.50 - 101.00 783.00 27.90 - 101.05 719.00 27.50 - 101.10 585.00 24.20 - 101.15 465.00 22.10 - 101.20 371.00 19.30 - 101.25 328.00 18.50 - 101.30 277.00 16.70 - 101.35 248.00 16.10 - 101.40 209.00 14.40 - 101.45 221.00 15.10 - 101.50 198.00 14.00 - 101.55 203.00 14.50 - 101.60 188.00 13.60 - 101.65 207.00 14.50 - 101.70 195.00 13.80 - 101.75 170.00 13.10 - 101.80 192.00 13.60 - 101.85 172.00 13.10 - 101.90 185.00 13.30 - 101.95 183.00 13.40 - 102.00 211.00 14.10 - 102.05 147.00 12.00 - 102.10 176.00 12.80 - 102.15 186.00 13.40 - 102.20 171.00 12.60 - 102.25 169.00 12.70 - 102.30 192.00 13.20 - 102.35 215.00 14.30 - 102.40 146.00 11.50 - 102.45 169.00 12.60 - 102.50 188.00 13.10 - 102.55 175.00 12.80 - 102.60 165.00 12.20 - 102.65 184.00 13.10 - 102.70 172.00 12.40 - 102.75 179.00 13.00 - 102.80 163.00 12.10 - 102.85 167.00 12.50 - 102.90 179.00 12.70 - 102.95 171.00 12.70 - 103.00 181.00 12.70 - 103.05 171.00 12.70 - 103.10 180.00 12.70 - 103.15 173.00 12.80 - 103.20 167.00 12.20 - 103.25 186.00 13.20 - 103.30 176.00 12.50 - 103.35 191.00 13.40 - 103.40 170.00 12.30 - 103.45 167.00 12.50 - 103.50 165.00 12.10 - 103.55 182.00 13.00 - 103.60 173.00 12.40 - 103.65 186.00 13.20 - 103.70 161.00 12.00 - 103.75 166.00 12.40 - 103.80 157.00 11.80 - 103.85 170.00 12.50 - 103.90 183.00 12.70 - 103.95 179.00 12.90 - 104.00 164.00 12.00 - 104.05 169.00 12.50 - 104.10 161.00 11.90 - 104.15 156.00 12.00 - 104.20 163.00 12.00 - 104.25 174.00 12.70 - 104.30 161.00 11.90 - 104.35 169.00 12.50 - 104.40 158.00 11.80 - 104.45 180.00 12.90 - 104.50 171.00 12.30 - 104.55 165.00 12.30 - 104.60 163.00 12.00 - 104.65 172.00 12.60 - 104.70 164.00 12.00 - 104.75 174.00 12.60 - 104.80 178.00 12.50 - 104.85 154.00 11.90 - 104.90 176.00 12.40 - 104.95 142.00 11.40 - 105.00 163.00 12.00 - 105.05 177.00 12.80 - 105.10 194.00 13.00 - 105.15 176.00 12.70 - 105.20 207.00 13.50 - 105.25 158.00 12.10 - 105.30 151.00 11.50 - 105.35 183.00 13.00 - 105.40 159.00 11.80 - 105.45 179.00 12.90 - 105.50 170.00 12.20 - 105.55 192.00 13.30 - 105.60 160.00 11.90 - 105.65 168.00 12.40 - 105.70 183.00 12.70 - 105.75 163.00 12.30 - 105.80 162.00 11.90 - 105.85 182.00 12.90 - 105.90 154.00 11.60 - 105.95 180.00 12.90 - 106.00 168.00 12.20 - 106.05 166.00 12.40 - 106.10 155.00 11.70 - 106.15 190.00 13.30 - 106.20 165.00 12.10 - 106.25 163.00 12.30 - 106.30 183.00 12.80 - 106.35 165.00 12.50 - 106.40 173.00 12.50 - 106.45 163.00 12.50 - 106.50 151.00 11.70 - 106.55 198.00 13.80 - 106.60 165.00 12.20 - 106.65 157.00 12.30 - 106.70 159.00 12.10 - 106.75 177.00 13.10 - 106.80 156.00 12.00 - 106.85 182.00 13.40 - 106.90 181.00 13.00 - 106.95 158.00 12.50 - 107.00 176.00 12.80 - 107.05 163.00 12.70 - 107.10 156.00 12.10 - 107.15 213.00 14.60 - 107.20 172.00 12.80 - 107.25 170.00 13.00 - 107.30 168.00 12.60 - 107.35 169.00 13.00 - 107.40 169.00 12.70 - 107.45 168.00 13.00 - 107.50 155.00 12.10 - 107.55 164.00 12.80 - 107.60 168.00 12.70 - 107.65 144.00 12.00 - 107.70 166.00 12.60 - 107.75 172.00 13.10 - 107.80 156.00 12.20 - 107.85 154.00 12.40 - 107.90 143.00 11.60 - 107.95 152.00 12.30 - 108.00 174.00 12.80 - 108.05 168.00 12.80 - 108.10 164.00 12.40 - 108.15 160.00 12.50 - 108.20 176.00 12.80 - 108.25 174.00 13.00 - 108.30 175.00 12.70 - 108.35 163.00 12.60 - 108.40 169.00 12.50 - 108.45 180.00 13.10 - 108.50 159.00 12.00 - 108.55 173.00 12.80 - 108.60 148.00 11.60 - 108.65 169.00 12.60 - 108.70 167.00 12.30 - 108.75 168.00 12.50 - 108.80 175.00 12.50 - 108.85 163.00 12.30 - 108.90 164.00 12.10 - 108.95 189.00 13.30 - 109.00 192.00 13.10 - 109.05 181.00 13.00 - 109.10 202.00 13.40 - 109.15 190.00 13.30 - 109.20 163.00 12.00 - 109.25 216.00 14.10 - 109.30 220.00 14.00 - 109.35 230.00 14.60 - 109.40 255.00 15.00 - 109.45 253.00 15.30 - 109.50 273.00 15.50 - 109.55 296.00 16.50 - 109.60 300.00 16.30 - 109.65 331.00 17.50 - 109.70 347.00 17.50 - 109.75 349.00 18.00 - 109.80 341.00 17.40 - 109.85 332.00 17.50 - 109.90 298.00 16.20 - 109.95 259.00 15.50 - 110.00 227.00 14.10 - 110.05 203.00 13.70 - 110.10 222.00 14.00 - 110.15 175.00 12.70 - 110.20 183.00 12.70 - 110.25 197.00 13.50 - 110.30 176.00 12.40 - 110.35 179.00 12.90 - 110.40 176.00 12.50 - 110.45 178.00 12.80 - 110.50 210.00 13.60 - 110.55 181.00 13.00 - 110.60 167.00 12.20 - 110.65 165.00 12.40 - 110.70 172.00 12.30 - 110.75 175.00 12.80 - 110.80 177.00 12.50 - 110.85 194.00 13.40 - 110.90 171.00 12.30 - 110.95 177.00 12.80 - 111.00 188.00 12.90 - 111.05 175.00 12.80 - 111.10 194.00 13.10 - 111.15 179.00 12.90 - 111.20 171.00 12.30 - 111.25 165.00 12.40 - 111.30 183.00 12.70 - 111.35 184.00 13.00 - 111.40 187.00 12.90 - 111.45 178.00 12.80 - 111.50 172.00 12.30 - 111.55 179.00 12.90 - 111.60 205.00 13.40 - 111.65 168.00 12.50 - 111.70 161.00 11.90 - 111.75 182.00 13.00 - 111.80 167.00 12.20 - 111.85 193.00 13.40 - 111.90 188.00 12.90 - 111.95 204.00 13.80 - 112.00 179.00 12.60 - 112.05 176.00 12.80 - 112.10 185.00 12.80 - 112.15 174.00 12.70 - 112.20 175.00 12.50 - 112.25 198.00 13.60 - 112.30 199.00 13.30 - 112.35 207.00 13.90 - 112.40 204.00 13.50 - 112.45 180.00 13.00 - 112.50 137.00 11.10 - 112.55 179.00 13.00 - 112.60 183.00 12.80 - 112.65 166.00 12.60 - 112.70 166.00 12.30 - 112.75 189.00 13.40 - 112.80 181.00 12.80 - 112.85 194.00 13.60 - 112.90 171.00 12.50 - 112.95 202.00 13.90 - 113.00 216.00 14.10 - 113.05 198.00 14.00 - 113.10 189.00 13.30 - 113.15 170.00 13.00 - 113.20 182.00 13.10 - 113.25 195.00 14.00 - 113.30 177.00 13.00 - 113.35 180.00 13.50 - 113.40 195.00 13.70 - 113.45 201.00 14.30 - 113.50 203.00 14.00 - 113.55 200.00 14.30 - 113.60 209.00 14.20 - 113.65 231.00 15.40 - 113.70 281.00 16.60 - 113.75 287.00 17.20 - 113.80 324.00 17.80 - 113.85 395.00 20.20 - 113.90 457.00 21.20 - 113.95 580.00 24.40 - 114.00 685.00 26.00 - 114.05 873.00 30.00 - 114.10 964.00 30.80 - 114.15 1126.00 34.00 - 114.20 1266.00 35.20 - 114.25 1307.00 36.50 - 114.30 1221.00 34.50 - 114.35 1096.00 33.30 - 114.40 978.00 30.70 - 114.45 792.00 28.20 - 114.50 600.00 24.00 - 114.55 487.00 22.00 - 114.60 358.00 18.50 - 114.65 279.00 16.60 - 114.70 265.00 15.80 - 114.75 258.00 15.90 - 114.80 244.00 15.10 - 114.85 226.00 14.80 - 114.90 227.00 14.50 - 114.95 188.00 13.50 - 115.00 195.00 13.40 - 115.05 211.00 14.20 - 115.10 205.00 13.70 - 115.15 198.00 13.70 - 115.20 218.00 14.00 - 115.25 200.00 13.70 - 115.30 200.00 13.40 - 115.35 188.00 13.30 - 115.40 209.00 13.70 - 115.45 184.00 13.10 - 115.50 186.00 12.90 - 115.55 202.00 13.70 - 115.60 183.00 12.70 - 115.65 187.00 13.10 - 115.70 182.00 12.60 - 115.75 185.00 13.10 - 115.80 213.00 13.70 - 115.85 177.00 12.80 - 115.90 199.00 13.20 - 115.95 185.00 13.00 - 116.00 184.00 12.70 - 116.05 191.00 13.30 - 116.10 173.00 12.30 - 116.15 196.00 13.50 - 116.20 201.00 13.30 - 116.25 173.00 12.70 - 116.30 178.00 12.60 - 116.35 161.00 12.30 - 116.40 208.00 13.60 - 116.45 183.00 13.10 - 116.50 183.00 12.80 - 116.55 173.00 12.80 - 116.60 184.00 12.80 - 116.65 215.00 14.20 - 116.70 201.00 13.40 - 116.75 193.00 13.40 - 116.80 190.00 13.00 - 116.85 216.00 14.20 - 116.90 195.00 13.10 - 116.95 203.00 13.80 - 117.00 183.00 12.80 - 117.05 203.00 13.70 - 117.10 187.00 12.90 - 117.15 216.00 14.20 - 117.20 191.00 13.00 - 117.25 189.00 13.30 - 117.30 189.00 13.00 - 117.35 226.00 14.50 - 117.40 185.00 12.90 - 117.45 194.00 13.50 - 117.50 185.00 12.80 - 117.55 213.00 14.10 - 117.60 197.00 13.30 - 117.65 198.00 14.50 - 117.70 168.00 13.00 - 117.75 209.00 14.90 - 117.80 185.00 13.70 - 117.85 208.00 14.90 - 117.90 213.00 14.70 - 117.95 203.00 14.70 - 118.00 225.00 15.10 - 118.05 214.00 15.10 - 118.10 233.00 15.40 - 118.15 245.00 16.20 - 118.20 236.00 15.50 - 118.25 245.00 16.20 - 118.30 305.00 17.60 - 118.35 287.00 17.10 - 118.40 317.00 17.40 - 118.45 421.00 20.60 - 118.50 422.00 20.10 - 118.55 590.00 24.40 - 118.60 701.00 26.80 - 118.65 861.00 28.60 - 118.70 1054.00 31.00 - 118.75 1232.00 34.30 - 118.80 1483.00 36.80 - 118.85 1694.00 40.30 - 118.90 1819.00 40.80 - 118.95 1845.00 42.30 - 119.00 1866.00 41.50 - 119.05 1726.00 41.00 - 119.10 1492.00 37.20 - 119.15 1232.00 34.80 - 119.20 971.00 30.10 - 119.25 753.00 27.20 - 119.30 626.00 24.20 - 119.35 487.00 21.90 - 119.40 409.00 19.60 - 119.45 342.00 18.50 - 119.50 307.00 17.10 - 119.55 296.00 17.20 - 119.60 231.00 14.90 - 119.65 246.00 15.80 - 119.70 220.00 14.50 - 119.75 255.00 16.10 - 119.80 214.00 14.40 - 119.85 247.00 15.90 - 119.90 238.00 15.20 - 119.95 218.00 15.00 - 120.00 222.00 14.70 - 120.05 218.00 15.00 - 120.10 253.00 15.80 - 120.15 197.00 14.30 - 120.20 190.00 13.60 - 120.25 221.00 15.10 - 120.30 204.00 14.20 - 120.35 206.00 14.60 - 120.40 189.00 13.60 - 120.45 231.00 15.40 - 120.50 190.00 13.60 - 120.55 191.00 13.90 - 120.60 211.00 14.30 - 120.65 204.00 14.30 - 120.70 200.00 13.90 - 120.75 199.00 14.10 - 120.80 190.00 13.50 - 120.85 195.00 13.90 - 120.90 179.00 13.00 - 120.95 189.00 13.60 - 121.00 190.00 13.30 - 121.05 195.00 13.80 - 121.10 193.00 13.40 - 121.15 173.00 12.80 - 121.20 183.00 13.00 - 121.25 181.00 13.10 - 121.30 203.00 13.50 - 121.35 177.00 12.90 - 121.40 201.00 13.40 - 121.45 179.00 12.90 - 121.50 179.00 12.60 - 121.55 194.00 13.40 - 121.60 158.00 11.90 - 121.65 195.00 13.40 - 121.70 201.00 13.40 - 121.75 192.00 13.40 - 121.80 189.00 13.00 - 121.85 186.00 13.10 - 121.90 170.00 12.30 - 121.95 166.00 12.40 - 122.00 185.00 12.80 - 122.05 197.00 13.60 - 122.10 177.00 12.60 - 122.15 198.00 13.60 - 122.20 174.00 12.50 - 122.25 171.00 12.60 - 122.30 190.00 13.00 - 122.35 214.00 14.20 - 122.40 189.00 13.00 - 122.45 174.00 12.80 - 122.50 171.00 12.40 - 122.55 163.00 12.40 - 122.60 174.00 12.40 - 122.65 177.00 12.80 - 122.70 180.00 12.60 - 122.75 186.00 13.10 - 122.80 190.00 13.00 - 122.85 170.00 12.60 - 122.90 175.00 12.50 - 122.95 194.00 13.40 - 123.00 175.00 12.50 - 123.05 194.00 13.40 - 123.10 189.00 12.90 - 123.15 222.00 14.30 - 123.20 178.00 12.50 - 123.25 158.00 12.10 - 123.30 191.00 13.00 - 123.35 184.00 13.00 - 123.40 190.00 12.90 - 123.45 183.00 13.00 - 123.50 178.00 12.50 - 123.55 204.00 13.70 - 123.60 192.00 13.00 - 123.65 200.00 13.50 - 123.70 182.00 12.60 - 123.75 171.00 12.50 - 123.80 186.00 12.70 - 123.85 197.00 13.40 - 123.90 174.00 12.30 - 123.95 167.00 12.30 - 124.00 178.00 12.40 - 124.05 198.00 13.40 - 124.10 205.00 13.30 - 124.15 216.00 14.00 - 124.20 200.00 13.20 - 124.25 204.00 13.60 - 124.30 190.00 12.80 - 124.35 188.00 13.10 - 124.40 191.00 12.90 - 124.45 186.00 13.00 - 124.50 175.00 12.30 - 124.55 175.00 12.60 - 124.60 174.00 12.30 - 124.65 194.00 13.30 - 124.70 181.00 12.50 - 124.75 161.00 12.10 - 124.80 186.00 12.70 - 124.85 200.00 13.50 - 124.90 168.00 12.10 - 124.95 177.00 12.70 - 125.00 188.00 12.80 - 125.05 177.00 12.70 - 125.10 163.00 11.90 - 125.15 175.00 12.70 - 125.20 188.00 12.80 - 125.25 176.00 12.80 - 125.30 172.00 12.30 - 125.35 172.00 12.60 - 125.40 181.00 12.70 - 125.45 186.00 13.20 - 125.50 181.00 12.70 - 125.55 193.00 13.40 - 125.60 177.00 12.60 - 125.65 176.00 12.90 - 125.70 194.00 13.20 - 125.75 179.00 13.00 - 125.80 147.00 11.50 - 125.85 186.00 13.30 - 125.90 182.00 12.90 - 125.95 165.00 12.70 - 126.00 164.00 12.30 - 126.05 199.00 13.90 - 126.10 167.00 12.40 - 126.15 184.00 13.40 - 126.20 203.00 13.80 - 126.25 190.00 13.70 - 126.30 182.00 13.10 - 126.35 180.00 13.40 - 126.40 179.00 13.00 - 126.45 179.00 13.40 - 126.50 170.00 12.70 - 126.55 176.00 13.30 - 126.60 178.00 13.10 - 126.65 185.00 13.70 - 126.70 193.00 13.60 - 126.75 192.00 14.00 - 126.80 198.00 13.80 - 126.85 195.00 14.00 - 126.90 165.00 12.60 - 126.95 189.00 13.80 - 127.00 175.00 13.00 - 127.05 176.00 13.30 - 127.10 184.00 13.30 - 127.15 179.00 13.40 - 127.20 187.00 13.40 - 127.25 176.00 13.20 - 127.30 191.00 13.50 - 127.35 194.00 13.90 - 127.40 177.00 12.90 - 127.45 177.00 13.20 - 127.50 180.00 13.00 - 127.55 158.00 12.40 - 127.60 193.00 13.40 - 127.65 177.00 13.10 - 127.70 185.00 13.10 - 127.75 178.00 13.10 - 127.80 184.00 13.00 - 127.85 188.00 13.40 - 127.90 182.00 12.90 - 127.95 190.00 13.50 - 128.00 191.00 13.20 - 128.05 165.00 12.50 - 128.10 174.00 12.50 - 128.15 158.00 12.20 - 128.20 197.00 13.30 - 128.25 183.00 13.10 - 128.30 196.00 13.30 - 128.35 166.00 12.50 - 128.40 218.00 14.00 - 128.45 206.00 13.80 - 128.50 184.00 12.80 - 128.55 176.00 12.70 - 128.60 198.00 13.20 - 128.65 215.00 14.10 - 128.70 179.00 12.60 - 128.75 192.00 13.30 - 128.80 201.00 13.30 - 128.85 221.00 14.20 - 128.90 227.00 14.10 - 128.95 229.00 14.40 - 129.00 254.00 14.90 - 129.05 256.00 15.30 - 129.10 272.00 15.40 - 129.15 239.00 14.80 - 129.20 228.00 14.10 - 129.25 255.00 15.20 - 129.30 213.00 13.60 - 129.35 203.00 13.60 - 129.40 228.00 14.10 - 129.45 220.00 14.10 - 129.50 185.00 12.60 - 129.55 192.00 13.20 - 129.60 187.00 12.70 - 129.65 182.00 12.80 - 129.70 209.00 13.40 - 129.75 173.00 12.50 - 129.80 202.00 13.20 - 129.85 178.00 12.70 - 129.90 189.00 12.80 - 129.95 177.00 12.60 - 130.00 177.00 12.30 - 130.05 190.00 13.10 - 130.10 178.00 12.40 - 130.15 177.00 12.60 - 130.20 164.00 11.90 - 130.25 185.00 12.90 - 130.30 153.00 11.40 - 130.35 174.00 12.50 - 130.40 197.00 13.00 - 130.45 192.00 13.10 - 130.50 174.00 12.20 - 130.55 177.00 12.60 - 130.60 172.00 12.10 - 130.65 173.00 12.50 - 130.70 178.00 12.40 - 130.75 180.00 12.80 - 130.80 203.00 13.20 - 130.85 192.00 13.20 - 130.90 184.00 12.60 - 130.95 197.00 13.30 - 131.00 169.00 12.10 - 131.05 187.00 13.00 - 131.10 175.00 12.30 - 131.15 177.00 12.60 - 131.20 199.00 13.10 - 131.25 180.00 12.80 - 131.30 203.00 13.20 - 131.35 175.00 12.60 - 131.40 183.00 12.50 - 131.45 192.00 13.20 - 131.50 174.00 12.30 - 131.55 180.00 12.80 - 131.60 179.00 12.50 - 131.65 191.00 13.20 - 131.70 182.00 12.60 - 131.75 174.00 12.60 - 131.80 191.00 12.90 - 131.85 195.00 13.40 - 131.90 171.00 12.30 - 131.95 198.00 13.60 - 132.00 193.00 13.10 - 132.05 175.00 12.80 - 132.10 207.00 13.60 - 132.15 189.00 13.40 - 132.20 174.00 12.50 - 132.25 196.00 13.70 - 132.30 175.00 12.60 - 132.35 196.00 13.80 - 132.40 183.00 13.00 - 132.45 198.00 13.80 - 132.50 196.00 13.40 - 132.55 169.00 12.90 - 132.60 189.00 13.30 - 132.65 171.00 13.00 - 132.70 193.00 13.50 - 132.75 170.00 13.00 - 132.80 175.00 12.90 - 132.85 166.00 12.90 - 132.90 188.00 13.40 - 132.95 186.00 13.70 - 133.00 165.00 12.60 - 133.05 201.00 14.20 - 133.10 182.00 13.20 - 133.15 151.00 12.40 - 133.20 156.00 12.20 - 133.25 187.00 13.70 - 133.30 153.00 12.10 - 133.35 193.00 14.00 - 133.40 200.00 13.90 - 133.45 165.00 12.90 - 133.50 172.00 12.90 - 133.55 162.00 12.70 - 133.60 165.00 12.50 - 133.65 218.00 14.70 - 133.70 197.00 13.60 - 133.75 206.00 14.20 - 133.80 186.00 13.20 - 133.85 162.00 12.50 - 133.90 176.00 12.80 - 133.95 174.00 12.90 - 134.00 196.00 13.40 - 134.05 174.00 12.90 - 134.10 177.00 12.70 - 134.15 183.00 13.10 - 134.20 184.00 12.90 - 134.25 185.00 13.10 - 134.30 200.00 13.40 - 134.35 175.00 12.70 - 134.40 190.00 13.00 - 134.45 195.00 13.40 - 134.50 192.00 13.00 - 134.55 171.00 12.50 - 134.60 194.00 13.00 - 134.65 190.00 13.10 - 134.70 165.00 12.00 - 134.75 192.00 13.20 - 134.80 160.00 11.70 - 134.85 192.00 13.10 - 134.90 181.00 12.50 - 134.95 208.00 13.70 - 135.00 179.00 12.40 - 135.05 172.00 12.40 - 135.10 183.00 12.50 - 135.15 187.00 12.90 - 135.20 185.00 12.50 - 135.25 182.00 12.70 - 135.30 184.00 12.50 - 135.35 163.00 11.90 - 135.40 201.00 13.00 - 135.45 189.00 12.80 - 135.50 204.00 13.10 - 135.55 178.00 12.50 - 135.60 178.00 12.20 - 135.65 193.00 13.00 - 135.70 215.00 13.40 - 135.75 203.00 13.30 - 135.80 216.00 13.40 - 135.85 165.00 12.10 - 135.90 196.00 12.80 - 135.95 178.00 12.50 - 136.00 170.00 11.90 - 136.05 173.00 12.40 - 136.10 188.00 12.60 - 136.15 176.00 12.50 - 136.20 186.00 12.50 - 136.25 189.00 12.90 - 136.30 166.00 11.80 - 136.35 177.00 12.50 - 136.40 169.00 11.90 - 136.45 171.00 12.30 - 136.50 194.00 12.80 - 136.55 187.00 12.90 - 136.60 162.00 11.70 - 136.65 160.00 11.90 - 136.70 183.00 12.40 - 136.75 150.00 11.50 - 136.80 180.00 12.40 - 136.85 194.00 13.20 - 136.90 185.00 12.60 - 136.95 158.00 11.90 - 137.00 193.00 12.90 - 137.05 165.00 12.20 - 137.10 178.00 12.30 - 137.15 183.00 12.90 - 137.20 180.00 12.40 - 137.25 176.00 12.70 - 137.30 183.00 12.60 - 137.35 189.00 13.20 - 137.40 180.00 12.50 - 137.45 160.00 12.20 - 137.50 202.00 13.30 - 137.55 201.00 13.60 - 137.60 173.00 12.30 - 137.65 176.00 12.80 - 137.70 195.00 13.10 - 137.75 197.00 13.50 - 137.80 186.00 12.80 - 137.85 183.00 13.00 - 137.90 175.00 12.40 - 137.95 178.00 12.80 - 138.00 190.00 12.90 - 138.05 174.00 12.70 - 138.10 163.00 12.00 - 138.15 190.00 13.30 - 138.20 169.00 12.20 - 138.25 198.00 13.60 - 138.30 199.00 13.30 - 138.35 184.00 13.10 - 138.40 216.00 13.90 - 138.45 183.00 13.10 - 138.50 200.00 13.40 - 138.55 186.00 13.30 - 138.60 177.00 12.70 - 138.65 186.00 13.40 - 138.70 193.00 13.30 - 138.75 200.00 14.00 - 138.80 180.00 12.90 - 138.85 178.00 13.20 - 138.90 198.00 13.60 - 138.95 236.00 15.30 - 139.00 203.00 13.80 - 139.05 207.00 14.30 - 139.10 190.00 13.40 - 139.15 171.00 13.10 - 139.20 203.00 13.90 - 139.25 203.00 14.20 - 139.30 198.00 13.70 - 139.35 200.00 14.20 - 139.40 187.00 13.30 - 139.45 214.00 14.70 - 139.50 198.00 13.70 - 139.55 220.00 14.80 - 139.60 196.00 13.70 - 139.65 239.00 15.50 - 139.70 212.00 14.20 - 139.75 219.00 14.80 - 139.80 248.00 15.40 - 139.85 220.00 14.80 - 139.90 241.00 15.10 - 139.95 245.00 15.50 - 140.00 269.00 15.90 - 140.05 294.00 17.00 - 140.10 323.00 17.40 - 140.15 302.00 17.20 - 140.20 312.00 17.10 - 140.25 371.00 18.90 - 140.30 420.00 19.70 - 140.35 516.00 22.30 - 140.40 596.00 23.40 - 140.45 644.00 24.70 - 140.50 711.00 25.40 - 140.55 833.00 28.10 - 140.60 895.00 28.40 - 140.65 1010.00 30.70 - 140.70 1058.00 30.80 - 140.75 1183.00 33.10 - 140.80 1278.00 33.70 - 140.85 1298.00 34.60 - 140.90 1419.00 35.40 - 140.95 1381.00 35.60 - 141.00 1299.00 33.80 - 141.05 1371.00 35.40 - 141.10 1273.00 33.30 - 141.15 1131.00 32.10 - 141.20 992.00 29.40 - 141.25 918.00 28.90 - 141.30 832.00 26.90 - 141.35 655.00 24.50 - 141.40 629.00 23.50 - 141.45 522.00 21.90 - 141.50 472.00 20.30 - 141.55 409.00 19.30 - 141.60 371.00 18.00 - 141.65 325.00 17.30 - 141.70 306.00 16.30 - 141.75 270.00 15.70 - 141.80 238.00 14.40 - 141.85 231.00 14.50 - 141.90 232.00 14.20 - 141.95 223.00 14.30 - 142.00 221.00 13.90 - 142.05 244.00 14.90 - 142.10 228.00 14.10 - 142.15 212.00 13.90 - 142.20 226.00 14.00 - 142.25 197.00 13.40 - 142.30 204.00 13.30 - 142.35 189.00 13.10 - 142.40 201.00 13.20 - 142.45 226.00 14.30 - 142.50 210.00 13.50 - 142.55 213.00 13.90 - 142.60 202.00 13.30 - 142.65 206.00 13.70 - 142.70 189.00 12.80 - 142.75 213.00 13.90 - 142.80 193.00 12.90 - 142.85 206.00 13.70 - 142.90 204.00 13.30 - 142.95 188.00 13.10 - 143.00 221.00 13.80 - 143.05 203.00 13.60 - 143.10 192.00 12.90 - 143.15 197.00 13.40 - 143.20 187.00 12.70 - 143.25 206.00 13.70 - 143.30 197.00 13.10 - 143.35 182.00 12.80 - 143.40 186.00 12.70 - 143.45 228.00 14.40 - 143.50 201.00 13.20 - 143.55 176.00 12.60 - 143.60 193.00 12.90 - 143.65 200.00 13.50 - 143.70 189.00 12.80 - 143.75 198.00 13.40 - 143.80 188.00 12.80 - 143.85 169.00 12.40 - 143.90 183.00 12.60 - 143.95 198.00 13.40 - 144.00 156.00 11.60 - 144.05 172.00 12.50 - 144.10 190.00 12.80 - 144.15 166.00 12.30 - 144.20 163.00 11.90 - 144.25 184.00 13.00 - 144.30 182.00 12.60 - 144.35 173.00 12.60 - 144.40 182.00 12.60 - 144.45 183.00 13.00 - 144.50 186.00 12.80 - 144.55 195.00 13.40 - 144.60 204.00 13.40 - 144.65 179.00 13.00 - 144.70 192.00 13.10 - 144.75 213.00 14.10 - 144.80 187.00 12.90 - 144.85 194.00 13.50 - 144.90 185.00 12.90 - 144.95 183.00 13.20 - 145.00 192.00 13.20 - 145.05 201.00 13.90 - 145.10 211.00 13.90 - 145.15 163.00 12.50 - 145.20 202.00 13.60 - 145.25 197.00 13.80 - 145.30 183.00 13.00 - 145.35 177.00 13.20 - 145.40 188.00 13.20 - 145.45 158.00 12.50 - 145.50 184.00 13.20 - 145.55 162.00 12.70 - 145.60 169.00 12.70 - 145.65 171.00 13.10 - 145.70 188.00 13.40 - 145.75 167.00 13.00 - 145.80 182.00 13.20 - 145.85 197.00 14.10 - 145.90 179.00 13.10 - 145.95 172.00 13.20 - 146.00 163.00 12.50 - 146.05 172.00 13.10 - 146.10 178.00 13.00 - 146.15 179.00 13.40 - 146.20 171.00 12.80 - 146.25 189.00 13.70 - 146.30 190.00 13.40 - 146.35 185.00 13.50 - 146.40 169.00 12.60 - 146.45 165.00 12.70 - 146.50 185.00 13.10 - 146.55 158.00 12.40 - 146.60 190.00 13.30 - 146.65 165.00 12.60 - 146.70 173.00 12.60 - 146.75 206.00 14.10 - 146.80 170.00 12.50 - 146.85 193.00 13.60 - 146.90 167.00 12.30 - 146.95 182.00 13.10 - 147.00 191.00 13.20 - 147.05 175.00 12.90 - 147.10 184.00 12.90 - 147.15 163.00 12.40 - 147.20 174.00 12.50 - 147.25 176.00 12.90 - 147.30 163.00 12.10 - 147.35 174.00 12.80 - 147.40 155.00 11.80 - 147.45 153.00 12.00 - 147.50 190.00 13.00 - 147.55 190.00 13.30 - 147.60 169.00 12.30 - 147.65 189.00 13.30 - 147.70 177.00 12.60 - 147.75 167.00 12.50 - 147.80 163.00 12.00 - 147.85 196.00 13.50 - 147.90 175.00 12.50 - 147.95 146.00 11.60 - 148.00 170.00 12.20 - 148.05 179.00 12.90 - 148.10 182.00 12.60 - 148.15 175.00 12.70 - 148.20 171.00 12.30 - 148.25 201.00 13.60 - 148.30 181.00 12.60 - 148.35 152.00 11.80 - 148.40 194.00 13.00 - 148.45 160.00 12.20 - 148.50 179.00 12.50 - 148.55 181.00 12.90 - 148.60 175.00 12.40 - 148.65 178.00 12.80 - 148.70 186.00 12.80 - 148.75 195.00 13.40 - 148.80 166.00 12.00 - 148.85 184.00 13.00 - 148.90 215.00 13.70 - 148.95 183.00 12.90 - 149.00 184.00 12.60 - 149.05 174.00 12.60 - 149.10 175.00 12.30 - 149.15 171.00 12.50 - 149.20 166.00 12.00 - 149.25 188.00 13.00 - 149.30 165.00 11.90 - 149.35 184.00 12.90 - 149.40 181.00 12.60 - 149.45 174.00 12.60 - 149.50 178.00 12.40 - 149.55 191.00 13.20 - 149.60 181.00 12.50 - 149.65 174.00 12.60 - 149.70 180.00 12.50 - 149.75 177.00 12.70 - 149.80 164.00 11.90 - 149.85 203.00 13.60 - 149.90 178.00 12.40 - 149.95 162.00 12.20 - 150.00 192.00 12.90 - 150.05 164.00 12.20 - 150.10 151.00 11.40 - 150.15 170.00 12.50 - 150.20 166.00 12.00 - 150.25 194.00 13.30 - 150.30 168.00 12.10 - 150.35 173.00 12.50 - 150.40 175.00 12.30 - 150.45 193.00 13.30 - 150.50 177.00 12.40 - 150.55 185.00 13.00 - 150.60 178.00 12.40 - 150.65 178.00 12.70 - 150.70 179.00 12.50 - 150.75 180.00 12.90 - 150.80 169.00 12.20 - 150.85 177.00 12.80 - 150.90 159.00 11.80 - 150.95 167.00 12.40 - 151.00 180.00 12.60 - 151.05 158.00 12.20 - 151.10 173.00 12.40 - 151.15 172.00 12.70 - 151.20 163.00 12.10 - 151.25 168.00 12.60 - 151.30 166.00 12.20 - 151.35 179.00 13.00 - 151.40 159.00 12.00 - 151.45 173.00 12.90 - 151.50 170.00 12.40 - 151.55 151.00 12.10 - 151.60 174.00 12.60 - 151.65 182.00 13.20 - 151.70 182.00 12.90 - 151.75 172.00 12.90 - 151.80 157.00 12.00 - 151.85 156.00 12.30 - 151.90 168.00 12.50 - 151.95 194.00 13.80 - 152.00 177.00 12.80 - 152.05 170.00 12.90 - 152.10 169.00 12.60 - 152.15 173.00 13.00 - 152.20 161.00 12.30 - 152.25 169.00 12.90 - 152.30 167.00 12.50 - 152.35 194.00 13.80 - 152.40 150.00 11.90 - 152.45 159.00 12.50 - 152.50 181.00 13.10 - 152.55 180.00 13.30 - 152.60 193.00 13.40 - 152.65 192.00 13.70 - 152.70 152.00 11.90 - 152.75 159.00 12.50 - 152.80 147.00 11.70 - 152.85 190.00 13.60 - 152.90 167.00 12.40 - 152.95 193.00 13.60 - 153.00 159.00 12.10 - 153.05 195.00 13.60 - 153.10 172.00 12.50 - 153.15 148.00 11.90 - 153.20 174.00 12.50 - 153.25 194.00 13.50 - 153.30 159.00 11.90 - 153.35 190.00 13.30 - 153.40 181.00 12.70 - 153.45 159.00 12.10 - 153.50 168.00 12.20 - 153.55 175.00 12.70 - 153.60 184.00 12.70 - 153.65 200.00 13.50 - 153.70 161.00 11.90 - 153.75 162.00 12.10 - 153.80 152.00 11.50 - 153.85 177.00 12.70 - 153.90 173.00 12.20 - 153.95 184.00 12.90 - 154.00 169.00 12.10 - 154.05 163.00 12.10 - 154.10 177.00 12.40 - 154.15 171.00 12.50 - 154.20 180.00 12.50 - 154.25 201.00 13.40 - 154.30 206.00 13.30 - 154.35 181.00 12.70 - 154.40 170.00 12.00 - 154.45 177.00 12.60 - 154.50 196.00 12.90 - 154.55 201.00 13.40 - 154.60 161.00 11.70 - 154.65 179.00 12.60 - 154.70 185.00 12.50 - 154.75 167.00 12.10 - 154.80 162.00 11.70 - 154.85 178.00 12.60 - 154.90 203.00 13.10 - 154.95 193.00 13.10 - 155.00 164.00 11.70 - 155.05 191.00 13.00 - 155.10 173.00 12.10 - 155.15 165.00 12.00 - 155.20 178.00 12.20 - 155.25 196.00 13.20 - 155.30 188.00 12.50 - 155.35 183.00 12.70 - 155.40 188.00 12.60 - 155.45 166.00 12.10 - 155.50 189.00 12.60 - 155.55 175.00 12.40 - 155.60 173.00 12.00 - 155.65 201.00 13.30 - 155.70 177.00 12.20 - 155.75 202.00 13.30 - 155.80 169.00 11.90 - 155.85 198.00 13.20 - 155.90 191.00 12.70 - 155.95 207.00 13.50 - 156.00 226.00 13.80 - 156.05 184.00 12.80 - 156.10 218.00 13.50 - 156.15 215.00 13.80 - 156.20 239.00 14.20 - 156.25 292.00 16.10 - 156.30 251.00 14.60 - 156.35 255.00 15.10 - 156.40 244.00 14.40 - 156.45 259.00 15.20 - 156.50 260.00 14.90 - 156.55 294.00 16.30 - 156.60 303.00 16.10 - 156.65 282.00 15.90 - 156.70 312.00 16.40 - 156.75 317.00 16.90 - 156.80 342.00 17.20 - 156.85 338.00 17.50 - 156.90 351.00 17.40 - 156.95 359.00 18.10 - 157.00 394.00 18.50 - 157.05 316.00 17.00 - 157.10 379.00 18.20 - 157.15 359.00 18.20 - 157.20 404.00 18.80 - 157.25 381.00 18.80 - 157.30 359.00 17.80 - 157.35 364.00 18.40 - 157.40 347.00 17.60 - 157.45 328.00 17.50 - 157.50 344.00 17.50 - 157.55 320.00 17.40 - 157.60 333.00 17.40 - 157.65 319.00 17.50 - 157.70 289.00 16.30 - 157.75 284.00 16.60 - 157.80 283.00 16.20 - 157.85 305.00 17.20 - 157.90 281.00 16.20 - 157.95 244.00 15.60 - 158.00 253.00 15.40 - 158.05 245.00 15.60 - 158.10 210.00 14.10 - 158.15 201.00 14.20 - 158.20 226.00 14.70 - 158.25 206.00 14.40 - 158.30 218.00 14.40 - 158.35 201.00 14.30 - 158.40 226.00 14.70 - 158.45 201.00 14.20 - 158.50 210.00 14.20 - 158.55 207.00 14.40 - 158.60 176.00 13.00 - 158.65 172.00 13.10 - 158.70 173.00 12.90 - 158.75 195.00 13.90 - 158.80 168.00 12.70 - 158.85 177.00 13.30 - 158.90 186.00 13.30 - 158.95 170.00 13.00 - 159.00 190.00 13.40 - 159.05 175.00 13.10 - 159.10 191.00 13.40 - 159.15 164.00 12.70 - 159.20 189.00 13.30 - 159.25 176.00 13.10 - 159.30 175.00 12.80 - 159.35 162.00 12.50 - 159.40 184.00 13.00 - 159.45 163.00 12.50 - 159.50 179.00 12.80 - 159.55 194.00 13.60 - 159.60 165.00 12.20 - 159.65 180.00 13.00 - 159.70 174.00 12.60 - 159.75 180.00 13.00 - 159.80 179.00 12.60 - 159.85 189.00 13.30 - 159.90 185.00 12.90 - 159.95 151.00 11.80 - 160.00 176.00 12.50 - 160.05 165.00 12.30 - 160.10 163.00 12.00 - 160.15 184.00 13.00 - 160.20 157.00 11.70 - 160.25 166.00 12.30 - 160.30 160.00 11.80 - 160.35 183.00 12.90 - 160.40 167.00 12.10 - 160.45 180.00 12.80 - 160.50 183.00 12.60 - 160.55 163.00 12.20 - 160.60 178.00 12.40 - 160.65 179.00 12.80 - 160.70 161.00 11.80 - 160.75 168.00 12.40 - 160.80 173.00 12.30 - 160.85 202.00 13.60 - 160.90 145.00 11.30 - 160.95 162.00 12.20 - 161.00 180.00 12.50 - 161.05 186.00 13.10 - 161.10 166.00 12.10 - 161.15 177.00 12.70 - 161.20 194.00 13.10 - 161.25 177.00 12.80 - 161.30 178.00 12.50 - 161.35 190.00 13.20 - 161.40 160.00 11.90 - 161.45 173.00 12.60 - 161.50 191.00 12.90 - 161.55 161.00 12.20 - 161.60 181.00 12.60 - 161.65 152.00 11.80 - 161.70 195.00 13.00 - 161.75 171.00 12.50 - 161.80 188.00 12.80 - 161.85 164.00 12.20 - 161.90 185.00 12.70 - 161.95 173.00 12.60 - 162.00 162.00 11.90 - 162.05 166.00 12.30 - 162.10 201.00 13.20 - 162.15 173.00 12.60 - 162.20 172.00 12.20 - 162.25 181.00 12.80 - 162.30 159.00 11.70 - 162.35 185.00 13.00 - 162.40 170.00 12.10 - 162.45 200.00 13.50 - 162.50 196.00 13.00 - 162.55 176.00 12.60 - 162.60 197.00 13.00 - 162.65 176.00 12.60 - 162.70 181.00 12.50 - 162.75 176.00 12.60 - 162.80 184.00 12.60 - 162.85 179.00 12.70 - 162.90 165.00 11.90 - 162.95 146.00 11.50 - 163.00 165.00 11.90 - 163.05 151.00 11.70 - 163.10 164.00 11.90 - 163.15 179.00 12.80 - 163.20 186.00 12.70 - 163.25 182.00 13.00 - 163.30 168.00 12.20 - 163.35 193.00 13.50 - 163.40 177.00 12.60 - 163.45 180.00 13.10 - 163.50 171.00 12.40 - 163.55 207.00 14.10 - 163.60 180.00 12.90 - 163.65 159.00 12.40 - 163.70 165.00 12.40 - 163.75 178.00 13.20 - 163.80 150.00 11.80 - 163.85 177.00 13.20 - 163.90 174.00 12.80 - 163.95 180.00 13.40 - 164.00 184.00 13.20 - 164.05 166.00 13.60 - 164.10 182.00 13.90 - 164.15 188.00 15.60 - 164.20 186.00 15.00 - 164.25 152.00 15.20 - 164.30 200.00 16.90 - 164.35 177.00 18.00 - 164.40 202.00 18.50 - 164.45 178.00 20.40 - 164.50 153.00 18.00 - 164.55 197.00 25.30 - 164.60 153.00 20.70 - 164.65 173.00 30.10 - 164.70 187.00 27.90 - 164.75 175.00 38.20 - 164.80 168.00 30.90 - 164.85 109.00 41.20 From f85a4271a92ff27fc75a2832f753cdd107b9bded Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 25 Mar 2026 21:53:01 +0100 Subject: [PATCH 02/48] pixi run nonpy-format-fix --- .github/copilot-instructions.md | 182 +++++---- .github/workflows/tutorial-tests-colab.yaml | 11 +- CONTRIBUTING.md | 7 +- DEVELOPMENT.md | 12 +- README.md | 45 ++- docs/architecture/architecture.md | 363 ++++++++++-------- docs/architecture/issues_closed.md | 75 ++-- docs/architecture/issues_open.md | 246 ++++++------ docs/docs/api-reference/index.md | 17 +- docs/docs/index.md | 27 +- docs/docs/installation-and-setup/index.md | 82 ++-- docs/docs/introduction/index.md | 32 +- docs/docs/tutorials/index.md | 119 +++--- .../user-guide/analysis-workflow/analysis.md | 184 ++++----- .../analysis-workflow/experiment.md | 130 ++++--- .../user-guide/analysis-workflow/index.md | 31 +- .../user-guide/analysis-workflow/model.md | 71 ++-- .../user-guide/analysis-workflow/project.md | 59 +-- .../user-guide/analysis-workflow/summary.md | 30 +- docs/docs/user-guide/concept.md | 73 ++-- docs/docs/user-guide/data-format.md | 88 +++-- docs/docs/user-guide/first-steps.md | 136 ++++--- docs/docs/user-guide/glossary.md | 24 +- docs/docs/user-guide/index.md | 25 +- docs/docs/user-guide/parameters.md | 110 +++--- .../parameters/_diffrn_radiation.md | 8 +- .../_diffrn_radiation_wavelength.md | 4 +- docs/docs/user-guide/parameters/_pd_calib.md | 4 +- docs/docs/user-guide/parameters/atom_site.md | 29 +- docs/docs/user-guide/parameters/background.md | 19 +- docs/docs/user-guide/parameters/cell.md | 4 +- docs/docs/user-guide/parameters/expt_type.md | 4 +- docs/docs/user-guide/parameters/instrument.md | 24 +- .../user-guide/parameters/linked_phases.md | 8 +- docs/docs/user-guide/parameters/pd_meas.md | 4 +- docs/docs/user-guide/parameters/peak.md | 4 +- .../docs/user-guide/parameters/space_group.md | 10 +- pixi.lock | 4 +- pixi.toml | 7 +- pycrysfml.md | 3 +- pyproject.toml | 43 ++- 41 files changed, 1260 insertions(+), 1098 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 58e7d29d..7dce8780 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,8 +2,8 @@ ## Project Context -- Python library for crystallographic diffraction analysis, such as refinement - of the structural model against experimental data. +- Python library for crystallographic diffraction analysis, such as + refinement of the structural model against experimental data. - Support for - sample_form: powder and single crystal - beam_mode: time-of-flight and constant wavelength @@ -13,124 +13,140 @@ - `cryspy` for Bragg diffraction - `crysfml` for Bragg diffraction - `pdffit2` for Total scattering -- Follow CIF naming conventions where possible. In some places, we deviate for - better API design, but we try to keep the spirit of the CIF names. +- Follow CIF naming conventions where possible. In some places, we + deviate for better API design, but we try to keep the spirit of the + CIF names. - Reusing the concept of datablocks and categories from CIF. We have `DatablockItem` (structure or experiment) and `DatablockCollection` - (collection of structures or experiments), as well as `CategoryItem` (single - categories in CIF) and `CategoryCollection` (loop categories in CIF). + (collection of structures or experiments), as well as `CategoryItem` + (single categories in CIF) and `CategoryCollection` (loop categories + in CIF). - Metadata via frozen dataclasses: `TypeInfo`, `Compatibility`, `CalculatorSupport`. -- The API is designed for scientists who use EasyDiffraction as a final product - in a user-friendly, intuitive way. The target users are not software - developers and may have little or no Python experience. The design is not - oriented toward developers building their own tooling on top of the library, - although experienced developers will find their own way. Prioritize - discoverability, clear error messages, and safe defaults so that - non-programmers are not stuck by standard API conventions. -- This project must be developed to be as error-free as possible, with the same - rigour applied to critical software (e.g. nuclear-plant control systems). - Every code path must be tested, edge cases must be handled explicitly, and - silent failures are not acceptable. +- The API is designed for scientists who use EasyDiffraction as a final + product in a user-friendly, intuitive way. The target users are not + software developers and may have little or no Python experience. The + design is not oriented toward developers building their own tooling on + top of the library, although experienced developers will find their + own way. Prioritize discoverability, clear error messages, and safe + defaults so that non-programmers are not stuck by standard API + conventions. +- This project must be developed to be as error-free as possible, with + the same rigour applied to critical software (e.g. nuclear-plant + control systems). Every code path must be tested, edge cases must be + handled explicitly, and silent failures are not acceptable. ## Code Style -- Use snake_case for functions and variables, PascalCase for classes, and - UPPER_SNAKE_CASE for constants. +- Use snake_case for functions and variables, PascalCase for classes, + and UPPER_SNAKE_CASE for constants. - Use `from __future__ import annotations` in every module. - Type-annotate all public function signatures. - Docstrings on all public classes and methods (Google style). - Prefer flat over nested, explicit over clever. -- Write straightforward code; do not add defensive checks for unlikely edge - cases. +- Write straightforward code; do not add defensive checks for unlikely + edge cases. - Prefer composition over deep inheritance. -- One class per file when the class is substantial; group small related classes. -- Avoid `**kwargs`; use explicit keyword arguments for clarity, autocomplete, - and typo detection. -- Do not use string-based dispatch (e.g. `getattr(self, f'_{name}')`) to route - to attributes or methods. Instead, write explicit named methods (e.g. - `_set_sample_form`, `_set_beam_mode`). This keeps the code greppable, - autocomplete-friendly, and type-safe. -- Public parameters and descriptors are either **editable** (property with both - getter and setter) or **read-only** (property with getter only). If internal - code needs to mutate a read-only property, add a private `_set_` method - instead of exposing a public setter. +- One class per file when the class is substantial; group small related + classes. +- Avoid `**kwargs`; use explicit keyword arguments for clarity, + autocomplete, and typo detection. +- Do not use string-based dispatch (e.g. `getattr(self, f'_{name}')`) to + route to attributes or methods. Instead, write explicit named methods + (e.g. `_set_sample_form`, `_set_beam_mode`). This keeps the code + greppable, autocomplete-friendly, and type-safe. +- Public parameters and descriptors are either **editable** (property + with both getter and setter) or **read-only** (property with getter + only). If internal code needs to mutate a read-only property, add a + private `_set_` method instead of exposing a public setter. ## Architecture -- Eager imports at the top of the module by default. Use lazy imports (inside a - method body) only when necessary to break circular dependencies or to keep - `core/` free of heavy utility imports on rarely-called paths (e.g. `help()`). +- Eager imports at the top of the module by default. Use lazy imports + (inside a method body) only when necessary to break circular + dependencies or to keep `core/` free of heavy utility imports on + rarely-called paths (e.g. `help()`). - No `pkgutil` / `importlib` auto-discovery patterns. - No background/daemon threads. - No monkey-patching or runtime class mutation. - Do not use `__all__` in modules; instead, rely on explicit imports in `__init__.py` to control the public API. -- Do not use redundant `import X as X` aliases in `__init__.py`. Use plain - `from module import X`. -- Concrete classes use `@Factory.register` decorators. To trigger registration, - each package's `__init__.py` must explicitly import every concrete class (e.g. - `from .chebyshev import ChebyshevPolynomialBackground`). When adding a new - concrete class, always add its import to the corresponding `__init__.py`. -- Switchable categories (those whose implementation can be swapped at runtime - via a factory) follow a fixed naming convention on the owner (experiment, - structure, or analysis): `` (read-only property), `_type` - (getter + setter), `show_supported__types()`, - `show_current__type()`. The owner class owns the type setter and the - show methods; the show methods delegate to `Factory.show_supported(...)` - passing context. Every factory-created category must have this full API, even - if only one implementation exists today. -- Categories are flat siblings within their owner (datablock or analysis). A - category must never be a child of another category of a different type. - Categories can reference each other via IDs, but not via parent-child nesting. -- Every finite, closed set of values (factory tags, experiment axes, category - descriptors with enumerated choices) must use a `(str, Enum)` class. Internal - code compares against enum members, never raw strings. +- Do not use redundant `import X as X` aliases in `__init__.py`. Use + plain `from module import X`. +- Concrete classes use `@Factory.register` decorators. To trigger + registration, each package's `__init__.py` must explicitly import + every concrete class (e.g. + `from .chebyshev import ChebyshevPolynomialBackground`). When adding a + new concrete class, always add its import to the corresponding + `__init__.py`. +- Switchable categories (those whose implementation can be swapped at + runtime via a factory) follow a fixed naming convention on the owner + (experiment, structure, or analysis): `` (read-only + property), `_type` (getter + setter), + `show_supported__types()`, `show_current__type()`. + The owner class owns the type setter and the show methods; the show + methods delegate to `Factory.show_supported(...)` passing context. + Every factory-created category must have this full API, even if only + one implementation exists today. +- Categories are flat siblings within their owner (datablock or + analysis). A category must never be a child of another category of a + different type. Categories can reference each other via IDs, but not + via parent-child nesting. +- Every finite, closed set of values (factory tags, experiment axes, + category descriptors with enumerated choices) must use a `(str, Enum)` + class. Internal code compares against enum members, never raw strings. - Keep `core/` free of domain logic — only base classes and utilities. -- Don't introduce a new abstraction until there is a concrete second use case. +- Don't introduce a new abstraction until there is a concrete second use + case. - Don't add dependencies without asking. ## Changes -- Before implementing any structural or design change (new categories, new - factories, switchable-category wiring, new datablocks, CIF serialisation - changes), read `docs/architecture/architecture.md` to understand the current - design choices and conventions. Follow the documented patterns (factory - registration, switchable-category naming, metadata classification, etc.) to - stay consistent with the rest of the codebase. For localised bug fixes or test - updates, the rules in this file are sufficient. -- The project is in beta; do not keep legacy code or add deprecation warnings. - Instead, update tests and tutorials to follow the current API. +- Before implementing any structural or design change (new categories, + new factories, switchable-category wiring, new datablocks, CIF + serialisation changes), read `docs/architecture/architecture.md` to + understand the current design choices and conventions. Follow the + documented patterns (factory registration, switchable-category naming, + metadata classification, etc.) to stay consistent with the rest of the + codebase. For localised bug fixes or test updates, the rules in this + file are sufficient. +- The project is in beta; do not keep legacy code or add deprecation + warnings. Instead, update tests and tutorials to follow the current + API. - Minimal diffs: don't rewrite working code just to reformat it. -- Never remove or replace existing functionality as part of a new change without - explicit confirmation. If a refactor would drop features, options, or - configurations, highlight every removal and wait for approval. -- Fix only what's asked; flag adjacent issues as comments, don't fix them - silently. -- Don't add new features or refactor existing code unless explicitly asked. +- Never remove or replace existing functionality as part of a new change + without explicit confirmation. If a refactor would drop features, + options, or configurations, highlight every removal and wait for + approval. +- Fix only what's asked; flag adjacent issues as comments, don't fix + them silently. +- Don't add new features or refactor existing code unless explicitly + asked. - Do not remove TODOs or comments unless the change fully resolves them. - When renaming, grep the entire project (code, tests, tutorials, docs). -- Every change should be atomic and self-contained, small enough to be described - by a single commit message. Make one change, suggest the commit message, then - stop and wait for confirmation before starting the next change. +- Every change should be atomic and self-contained, small enough to be + described by a single commit message. Make one change, suggest the + commit message, then stop and wait for confirmation before starting + the next change. - When in doubt, ask for clarification before making changes. ## Workflow -- All open issues, design questions, and planned improvements are tracked in - `docs/architecture/issues_open.md`, ordered by priority. When an issue is - fully implemented, move it from that file to +- All open issues, design questions, and planned improvements are + tracked in `docs/architecture/issues_open.md`, ordered by priority. + When an issue is fully implemented, move it from that file to `docs/architecture/issues_closed.md`. When the resolution affects the architecture, update the relevant sections of `docs/architecture/architecture.md`. -- After changes, run linting and formatting fixes with `pixi run fix`. Do not - check what was auto-fixed, just accept the fixes and move on. +- After changes, run linting and formatting fixes with `pixi run fix`. + Do not check what was auto-fixed, just accept the fixes and move on. - After changes, run unit tests with `pixi run unit-tests`. -- After changes, run integration tests with `pixi run integration-tests`. +- After changes, run integration tests with + `pixi run integration-tests`. - After changes, run tutorial tests with `pixi run script-tests`. -- Suggest a concise commit message (as a code block) after each change (less - than 72 characters, imperative mood, without prefixing with the type of - change). E.g.: +- Suggest a concise commit message (as a code block) after each change + (less than 72 characters, imperative mood, without prefixing with the + type of change). E.g.: - Add ChebyshevPolynomialBackground class - Implement background_type setter on Experiment - Standardize switchable-category naming convention diff --git a/.github/workflows/tutorial-tests-colab.yaml b/.github/workflows/tutorial-tests-colab.yaml index 08e2e2b6..35c4a753 100644 --- a/.github/workflows/tutorial-tests-colab.yaml +++ b/.github/workflows/tutorial-tests-colab.yaml @@ -7,8 +7,7 @@ on: # Allow only one concurrent workflow, skipping runs queued between the run in-progress and latest queued. # And cancel in-progress runs. concurrency: - group: - ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: @@ -37,10 +36,10 @@ jobs: - name: Install Python dependencies run: - python -m pip install 'easydiffraction[visualization]' nbconvert - nbmake pytest pytest-xdist + python -m pip install 'easydiffraction[visualization]' nbconvert nbmake pytest + pytest-xdist - name: Check if Jupyter Notebooks run without errors run: > - python -m pytest --nbmake docs/tutorials/ --nbmake-timeout=600 - --color=yes -n=auto + python -m pytest --nbmake docs/tutorials/ --nbmake-timeout=600 --color=yes + -n=auto diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb06f9de..e3a4a55f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,8 +42,8 @@ Please make sure you follow the EasyScience organization-wide If you are not planning to contribute code, you may want to: - 🐞 Report a bug — see [Reporting Issues](#11-reporting-issues) -- 🛡 Report a security issue — - see [Security Issues](#12-security-issues) +- 🛡 Report a security issue — see + [Security Issues](#12-security-issues) - 💬 Ask a question or start a discussion at [Project Discussions](https://github.com/easyscience/diffraction-lib/discussions) @@ -84,7 +84,8 @@ strategy. If you are not a core maintainer of this repository, follow these steps. -1. Open the repository page: `https://github.com/easyscience/diffraction-lib` +1. Open the repository page: + `https://github.com/easyscience/diffraction-lib` 2. Click the **Fork** button (top-right corner). This creates your own copy of the repository. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2acffeb3..0e063133 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -19,8 +19,8 @@ This is an example of a workflow that describes the development process. ```bash cd diffraction-lib ``` -- Create the environment defined in `pixi.toml` and install all necessary - dependencies: +- Create the environment defined in `pixi.toml` and install all + necessary dependencies: ```bash pixi install ``` @@ -111,8 +111,8 @@ This is an example of a workflow that describes the development process. ```bash pixi run docs-build ``` -- Test the documentation locally (built in the `site/` directory). E.g., on - macOS, open the site in the default browser via the terminal +- Test the documentation locally (built in the `site/` directory). E.g., + on macOS, open the site in the default browser via the terminal ```bash open http://127.0.0.1:8000 ``` @@ -142,8 +142,8 @@ This is an example of a workflow that describes the development process. - `[scope] enhancement` - `[scope] maintenance` - `[scope] significant` -- After approval, merge the pull request into the `develop` branch using "Squash - and merge" option +- After approval, merge the pull request into the `develop` branch using + "Squash and merge" option - Delete the branch remotely ```bash git push origin --delete new-feature diff --git a/README.md b/README.md index c4965e42..a9fccc67 100644 --- a/README.md +++ b/README.md @@ -9,40 +9,45 @@

-**EasyDiffraction** is a software for calculating neutron powder diffraction patterns based on a structural model and refining its parameters against experimental data. +**EasyDiffraction** is a software for calculating neutron powder +diffraction patterns based on a structural model and refining its +parameters against experimental data. - **EasyDiffraction** is developed both as a Python library and as a cross-platform desktop application. - Here, we focus on the Python library. For the graphical user interface -(GUI), please see the corresponding [GUI resources](https://github.com/easyscience/diffraction-app). - - +(GUI), please see the corresponding +[GUI resources](https://github.com/easyscience/diffraction-app). - - -License: [BSD 3-Clause](https://github.com/easyscience/diffraction-lib/blob/master/LICENSE) +License: +[BSD 3-Clause](https://github.com/easyscience/diffraction-lib/blob/master/LICENSE) ## Useful Links ### For Users -- 📖 [Documentation](https://easyscience.github.io/diffraction-lib/latest) -- 🚀 [Getting Started](https://easyscience.github.io/diffraction-lib/latest/introduction) -- 🧪 [Tutorials](https://easyscience.github.io/diffraction-lib/latest/tutorials) -- 💬 [Get in Touch](https://easyscience.github.io/diffraction-lib/latest/introduction/#get-in-touch) -- 🧾 [Citation](https://easyscience.github.io/diffraction-lib/latest/introduction/#citation) +- 📖 + [Documentation](https://easyscience.github.io/diffraction-lib/latest) +- 🚀 + [Getting Started](https://easyscience.github.io/diffraction-lib/latest/introduction) +- 🧪 + [Tutorials](https://easyscience.github.io/diffraction-lib/latest/tutorials) +- 💬 + [Get in Touch](https://easyscience.github.io/diffraction-lib/latest/introduction/#get-in-touch) +- 🧾 + [Citation](https://easyscience.github.io/diffraction-lib/latest/introduction/#citation) ### For Contributors - 🧑‍💻 [Source Code](https://github.com/easyscience/diffraction-lib) -- 🐞 [Issue Tracker](https://github.com/easyscience/diffraction-lib/issues) -- 💡 [Discussions](https://github.com/easyscience/diffraction-lib/discussions) -- 🤝 [Contributing Guide](https://github.com/easyscience/diffraction-lib/blob/master/CONTRIBUTING.md) -- 🛡 [Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md) - - +- 🐞 + [Issue Tracker](https://github.com/easyscience/diffraction-lib/issues) +- 💡 + [Discussions](https://github.com/easyscience/diffraction-lib/discussions) +- 🤝 + [Contributing Guide](https://github.com/easyscience/diffraction-lib/blob/master/CONTRIBUTING.md) +- 🛡 + [Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md) diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index 3770f3ea..4e3a2bb3 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -8,11 +8,11 @@ ## 1. Overview -EasyDiffraction is a Python library for crystallographic diffraction analysis -(Rietveld refinement, pair-distribution-function fitting, etc.). It models the -domain using **CIF-inspired abstractions** — datablocks, categories, and -parameters — while providing a high-level, user-friendly API through a single -`Project` façade. +EasyDiffraction is a Python library for crystallographic diffraction +analysis (Rietveld refinement, pair-distribution-function fitting, +etc.). It models the domain using **CIF-inspired abstractions** — +datablocks, categories, and parameters — while providing a high-level, +user-friendly API through a single `Project` façade. ### 1.1 Supported Experiment Dimensions @@ -25,8 +25,8 @@ Every experiment is fully described by four orthogonal axes: | Beam mode | constant wavelength, time-of-flight | `BeamModeEnum` | | Radiation probe | neutron, X-ray | `RadiationProbeEnum` | -> **Planned extensions:** 1D / 2D data dimensionality, polarised / unpolarised -> neutron beam. +> **Planned extensions:** 1D / 2D data dimensionality, polarised / +> unpolarised neutron beam. ### 1.2 Calculation Engines @@ -56,50 +56,56 @@ GuardedBase # Controlled attribute access, parent lin └── DatablockItem # CIF data block (e.g. Structure, Experiment) ``` -`CollectionBase` provides a unified dict-like API over an ordered item list with -name-based indexing. All key operations — `__getitem__`, `__setitem__`, -`__delitem__`, `__contains__`, `remove()` — resolve keys through a single -`_key_for(item)` method that returns `category_entry_name` for category items or -`datablock_entry_name` for datablock items. Subclasses `CategoryCollection` and +`CollectionBase` provides a unified dict-like API over an ordered item +list with name-based indexing. All key operations — `__getitem__`, +`__setitem__`, `__delitem__`, `__contains__`, `remove()` — resolve keys +through a single `_key_for(item)` method that returns +`category_entry_name` for category items or `datablock_entry_name` for +datablock items. Subclasses `CategoryCollection` and `DatablockCollection` inherit this consistently. ### 2.2 GuardedBase — Controlled Attribute Access -`GuardedBase` is the root ABC. It enforces that only **declared `@property` -attributes** are accessible publicly: +`GuardedBase` is the root ABC. It enforces that only **declared +`@property` attributes** are accessible publicly: -- **`__getattr__`** rejects any attribute not declared as a `@property` on the - class hierarchy. Shows diagnostics with closest-match suggestions on typos. +- **`__getattr__`** rejects any attribute not declared as a `@property` + on the class hierarchy. Shows diagnostics with closest-match + suggestions on typos. - **`__setattr__`** distinguishes: - **Private** (`_`-prefixed) — always allowed, no diagnostics. - - **Read-only public** (property without setter) — blocked with a clear error. - - **Writable public** (property with setter) — goes through the property - setter, which is where validation happens. - - **Unknown** — blocked with diagnostics showing allowed writable attrs. -- **Parent linkage** — when a `GuardedBase` child is assigned to another, the - child's `_parent` is set automatically, forming an implicit ownership tree. -- **Identity** — every instance gets an `_identity: Identity` object for lazy - CIF-style name resolution (`datablock_entry_name`, `category_code`, - `category_entry_name`) by walking the `_parent` chain. - -**Key design rule:** if a parameter has a public setter, it is writable for the -user. If only a getter — it is read-only. If internal code needs to set it, a -private method (underscore prefix) is used. See § 2.2.1 below for the full -pattern. + - **Read-only public** (property without setter) — blocked with a + clear error. + - **Writable public** (property with setter) — goes through the + property setter, which is where validation happens. + - **Unknown** — blocked with diagnostics showing allowed writable + attrs. +- **Parent linkage** — when a `GuardedBase` child is assigned to + another, the child's `_parent` is set automatically, forming an + implicit ownership tree. +- **Identity** — every instance gets an `_identity: Identity` object for + lazy CIF-style name resolution (`datablock_entry_name`, + `category_code`, `category_entry_name`) by walking the `_parent` + chain. + +**Key design rule:** if a parameter has a public setter, it is writable +for the user. If only a getter — it is read-only. If internal code needs +to set it, a private method (underscore prefix) is used. See § 2.2.1 +below for the full pattern. #### 2.2.1 Public Property Convention — Editable vs Read-Only -Every public parameter or descriptor exposed on a `GuardedBase` subclass follows -one of two patterns: +Every public parameter or descriptor exposed on a `GuardedBase` subclass +follows one of two patterns: | Kind | Getter | Setter | Internal mutation | | ------------- | ------ | ------ | ---------------------------------- | | **Editable** | yes | yes | Via the public setter | | **Read-only** | yes | no | Via a private `_set_` method | -**Editable property** — the user can both read and write the value. The setter -runs through `GuardedBase.__setattr__` and into the property setter, where -validation happens: +**Editable property** — the user can both read and write the value. The +setter runs through `GuardedBase.__setattr__` and into the property +setter, where validation happens: ```python @property @@ -113,11 +119,11 @@ def name(self, new: str) -> None: self._name = new ``` -**Read-only property** — the user can read but cannot assign. Any attempt to set -the attribute is blocked by `GuardedBase.__setattr__` with a clear error -message. If _internal_ code (factory builders, CIF loaders, etc.) needs to set -the value, it calls a private `_set_` method instead of exposing a public -setter: +**Read-only property** — the user can read but cannot assign. Any +attempt to set the attribute is blocked by `GuardedBase.__setattr__` +with a clear error message. If _internal_ code (factory builders, CIF +loaders, etc.) needs to set the value, it calls a private `_set_` +method instead of exposing a public setter: ```python @property @@ -133,12 +139,13 @@ def _set_sample_form(self, value: str) -> None: **Why this matters:** -- `GuardedBase.__setattr__` uses the presence of a setter to decide writability. - Adding a setter "just for internal use" would open the attribute to users. +- `GuardedBase.__setattr__` uses the presence of a setter to decide + writability. Adding a setter "just for internal use" would open the + attribute to users. - Private `_set_` methods keep the public API surface minimal and intention-clear, while remaining greppable and type-safe. -- The pattern avoids string-based dispatch — every mutator has an explicit named - method. +- The pattern avoids string-based dispatch — every mutator has an + explicit named method. ### 2.3 CategoryItem and CategoryCollection @@ -153,8 +160,8 @@ def _set_sample_form(self, value: str) -> None: | Display | `show()` on concrete subclasses | `show()` on concrete subclasses | | Building items | N/A | `add(item)`, `create(**kwargs)` | -**Update priority:** lower values run first. This ensures correct execution -order within a datablock (e.g. background before data). +**Update priority:** lower values run first. This ensures correct +execution order within a datablock (e.g. background before data). ### 2.4 DatablockItem and DatablockCollection @@ -170,8 +177,9 @@ order within a datablock (e.g. background before data). | Dirty flag | `_need_categories_update` | N/A | When any `Parameter.value` is set, it propagates -`_need_categories_update = True` up to the owning `DatablockItem`. Serialisation -(`as_cif`) and plotting trigger `_update_categories()` if the flag is set. +`_need_categories_update = True` up to the owning `DatablockItem`. +Serialisation (`as_cif`) and plotting trigger `_update_categories()` if +the flag is set. ### 2.5 Variable System — Parameters and Descriptors @@ -191,18 +199,19 @@ CIF-bound concrete classes add a `CifHandler` for serialisation: | `NumericDescriptor` | `GenericNumericDescriptor` | Read-only or writable number | | `Parameter` | `GenericParameter` | Fittable numeric value | -**Initialisation rule:** all Parameters/Descriptors are initialised with their -default values from `value_spec` (an `AttributeSpec`) **without any validation** -— we trust internal definitions. Changes go through public property setters, -which run both type and value validation. +**Initialisation rule:** all Parameters/Descriptors are initialised with +their default values from `value_spec` (an `AttributeSpec`) **without +any validation** — we trust internal definitions. Changes go through +public property setters, which run both type and value validation. -**Mixin safety:** Parameter/Descriptor classes must not have init arguments so -they can be used as mixins safely (e.g. `PdTofDataPointMixin`). +**Mixin safety:** Parameter/Descriptor classes must not have init +arguments so they can be used as mixins safely (e.g. +`PdTofDataPointMixin`). ### 2.6 Validation -`AttributeSpec` bundles `default`, `data_type`, `validator`, `allow_none`. -Validators include: +`AttributeSpec` bundles `default`, `data_type`, `validator`, +`allow_none`. Validators include: | Validator | Purpose | | --------------------- | -------------------------------------- | @@ -217,13 +226,14 @@ Validators include: ### 3.1 Experiment Type -An experiment's type is defined by the four enum axes and is **immutable after -creation**. This avoids the complexity of transforming all internal state when -the experiment type changes. The type is stored in an `ExperimentType` category -with four `StringDescriptor`s validated by `MembershipValidator`s. Public -properties are read-only; factory and CIF-loading code use private setters -(`_set_sample_form`, `_set_beam_mode`, `_set_radiation_probe`, -`_set_scattering_type`) during construction only. +An experiment's type is defined by the four enum axes and is **immutable +after creation**. This avoids the complexity of transforming all +internal state when the experiment type changes. The type is stored in +an `ExperimentType` category with four `StringDescriptor`s validated by +`MembershipValidator`s. Public properties are read-only; factory and +CIF-loading code use private setters (`_set_sample_form`, +`_set_beam_mode`, `_set_radiation_probe`, `_set_scattering_type`) during +construction only. ### 3.2 Experiment Hierarchy @@ -245,8 +255,8 @@ Each concrete experiment class carries: ### 3.3 Category Ownership -Every experiment owns its categories as private attributes with public read-only -or read-write properties: +Every experiment owns its categories as private attributes with public +read-only or read-write properties: ```python # Read-only — user cannot replace the object, only modify its contents @@ -265,9 +275,10 @@ experiment.excluded_regions_type = 'default' # triggers ExcludedRegionsFactory. experiment.linked_phases_type = 'default' # triggers LinkedPhasesFactory.create(...) ``` -**Type switching pattern:** `expt.background_type = 'chebyshev'` rather than -`expt.background.type = 'chebyshev'`. This keeps the API at the experiment level -and makes it clear that the entire category object is being replaced. +**Type switching pattern:** `expt.background_type = 'chebyshev'` rather +than `expt.background.type = 'chebyshev'`. This keeps the API at the +experiment level and makes it clear that the entire category object is +being replaced. --- @@ -286,8 +297,8 @@ A `Structure` contains three categories: - `SpaceGroup` — symmetry information (`CategoryItem`) - `AtomSites` — atomic positions collection (`CategoryCollection`) -Symmetry constraints (cell metric, atomic coordinates, ADPs) are applied via the -`crystallography` module during `_update_categories()`. +Symmetry constraints (cell metric, atomic coordinates, ADPs) are applied +via the `crystallography` module during `_update_categories()`. --- @@ -308,13 +319,13 @@ All factories inherit from `FactoryBase`, which provides: | Display | `show_supported(**filters)` | Pretty-print table of type + description | | Tag listing | `supported_tags()` | List of all registered tags | -Each `__init_subclass__` gives every factory its own independent `_registry` and -`_default_rules`. +Each `__init_subclass__` gives every factory its own independent +`_registry` and `_default_rules`. ### 5.2 Default Rules -`_default_rules` maps frozensets of `(axis_name, enum_value)` tuples to tag -strings (preferably enum values for type safety): +`_default_rules` maps frozensets of `(axis_name, enum_value)` tuples to +tag strings (preferably enum values for type safety): ```python class PeakFactory(FactoryBase): @@ -333,13 +344,14 @@ class PeakFactory(FactoryBase): } ``` -Resolution uses **largest-subset matching**: the rule whose frozenset is the -biggest subset of the given conditions wins. `frozenset()` acts as a universal -fallback. +Resolution uses **largest-subset matching**: the rule whose frozenset is +the biggest subset of the given conditions wins. `frozenset()` acts as a +universal fallback. ### 5.3 Metadata on Registered Classes -Every `@Factory.register`-ed class carries three frozen dataclass attributes: +Every `@Factory.register`-ed class carries three frozen dataclass +attributes: ```python @PeakFactory.register @@ -365,8 +377,9 @@ class CwlPseudoVoigt(PeakBase, CwlBroadeningMixin): ### 5.4 Registration Trigger -Concrete classes use `@Factory.register` decorators. To trigger registration, -each package's `__init__.py` must **explicitly import** every concrete class: +Concrete classes use `@Factory.register` decorators. To trigger +registration, each package's `__init__.py` must **explicitly import** +every concrete class: ```python # datablocks/experiment/categories/background/__init__.py @@ -397,12 +410,13 @@ from .line_segment import LineSegmentBackground | `CalculatorFactory` | Calculation engines | `CryspyCalculator`, `CrysfmlCalculator`, `PdffitCalculator` | | `MinimizerFactory` | Minimisers | `LmfitMinimizer`, `DfolsMinimizer`, … | -> **Note:** `ExperimentFactory` and `StructureFactory` are _builder_ factories -> with `from_cif_path`, `from_cif_str`, `from_data_path`, and `from_scratch` -> classmethods. `ExperimentFactory` inherits `FactoryBase` and uses `@register` -> on all four concrete experiment classes; `_resolve_class` looks up the -> registered class via `default_tag()` + `_supported_map()`. `StructureFactory` -> is a plain class without `FactoryBase` inheritance (only one structure type +> **Note:** `ExperimentFactory` and `StructureFactory` are _builder_ +> factories with `from_cif_path`, `from_cif_str`, `from_data_path`, and +> `from_scratch` classmethods. `ExperimentFactory` inherits +> `FactoryBase` and uses `@register` on all four concrete experiment +> classes; `_resolve_class` looks up the registered class via +> `default_tag()` + `_supported_map()`. `StructureFactory` is a plain +> class without `FactoryBase` inheritance (only one structure type > exists today). ### 5.6 Tag Naming Convention @@ -502,9 +516,9 @@ Tags are the user-facing identifiers for selecting types. They must be: | `lmfit (least_squares)` | `LmfitMinimizer` (method=`least_squares`) | | `dfols` | `DfolsMinimizer` | -> **Note:** minimizer variant tags (`lmfit (leastsq)`, `lmfit (least_squares)`) -> are planned but not yet re-implemented after the `FactoryBase` migration. See -> `issues_open.md` for details. +> **Note:** minimizer variant tags (`lmfit (leastsq)`, +> `lmfit (least_squares)`) are planned but not yet re-implemented after +> the `FactoryBase` migration. See `issues_open.md` for details. ### 5.7 Metadata Classification — Which Classes Get What @@ -514,16 +528,17 @@ Tags are the user-facing identifiers for selecting types. They must be: > `compatibility`, and `calculator_support`.** > > **If a `CategoryItem` only exists as a child row inside a -> `CategoryCollection`, it does NOT get these attributes — the collection -> does.** +> `CategoryCollection`, it does NOT get these attributes — the +> collection does.** #### Rationale -A `LineSegment` item (a single background control point) is never selected, -created, or queried by a factory. It is always instantiated internally by its -parent `LineSegmentBackground` collection. The meaningful unit of selection is -the _collection_, not the item. The user picks "line-segment background" (the -collection type), not individual line-segment points. +A `LineSegment` item (a single background control point) is never +selected, created, or queried by a factory. It is always instantiated +internally by its parent `LineSegmentBackground` collection. The +meaningful unit of selection is the _collection_, not the item. The user +picks "line-segment background" (the collection type), not individual +line-segment points. #### Singleton CategoryItems — factory-created (get all three) @@ -601,28 +616,32 @@ collection type), not individual line-segment points. ### 6.1 Calculator -The calculator performs the actual diffraction computation. It is attached -per-experiment on the `ExperimentBase` object. Each experiment auto-resolves its -calculator on first access based on the data category's `calculator_support` -metadata and `CalculatorFactory._default_rules`. The `CalculatorFactory` filters -its registry by `engine_imported` (whether the third-party library is available -in the environment). +The calculator performs the actual diffraction computation. It is +attached per-experiment on the `ExperimentBase` object. Each experiment +auto-resolves its calculator on first access based on the data +category's `calculator_support` metadata and +`CalculatorFactory._default_rules`. The `CalculatorFactory` filters its +registry by `engine_imported` (whether the third-party library is +available in the environment). The experiment exposes the standard switchable-category API: -- `calculator` — read-only property (lazy, auto-resolved on first access) +- `calculator` — read-only property (lazy, auto-resolved on first + access) - `calculator_type` — getter + setter -- `show_supported_calculator_types()` — filtered by data category support +- `show_supported_calculator_types()` — filtered by data category + support - `show_current_calculator_type()` ### 6.2 Minimiser -The minimiser drives the optimisation loop. `MinimizerFactory` creates instances -by tag (e.g. `'lmfit'`, `'dfols'`). +The minimiser drives the optimisation loop. `MinimizerFactory` creates +instances by tag (e.g. `'lmfit'`, `'dfols'`). ### 6.3 Fitter -`Fitter` wraps a minimiser instance and orchestrates the fitting workflow: +`Fitter` wraps a minimiser instance and orchestrates the fitting +workflow: 1. Collect `free_parameters` from structures + experiments. 2. Record start values. @@ -634,10 +653,12 @@ by tag (e.g. `'lmfit'`, `'dfols'`). `Analysis` is bound to a `Project` and provides the high-level API: -- Minimiser selection: `current_minimizer`, `show_available_minimizers()` -- Fit mode: `fit_mode` (`CategoryItem` with a `mode` descriptor validated by - `FitModeEnum`); `'single'` fits each experiment independently, `'joint'` fits - all simultaneously with weights from `joint_fit_experiments`. +- Minimiser selection: `current_minimizer`, + `show_available_minimizers()` +- Fit mode: `fit_mode` (`CategoryItem` with a `mode` descriptor + validated by `FitModeEnum`); `'single'` fits each experiment + independently, `'joint'` fits all simultaneously with weights from + `joint_fit_experiments`. - Joint-fit weights: `joint_fit_experiments` (`CategoryCollection` of per-experiment weight entries); sibling of `fit_mode`, not a child. - Parameter tables: `show_all_params()`, `show_fittable_params()`, @@ -845,26 +866,26 @@ project.experiments['xray_pdf'].peak_profile_type = 'gaussian-damped-sinc' ### 9.1 Naming and CIF Conventions -- Follow CIF naming conventions where possible. Deviate for better API design - when necessary, but keep the spirit of CIF names. +- Follow CIF naming conventions where possible. Deviate for better API + design when necessary, but keep the spirit of CIF names. - Reuse the concept of datablocks and categories from CIF. -- `DatablockItem` = one CIF `data_` block, `DatablockCollection` = set of - blocks. +- `DatablockItem` = one CIF `data_` block, `DatablockCollection` = set + of blocks. - `CategoryItem` = one CIF category, `CategoryCollection` = CIF loop. ### 9.2 Immutability of Experiment Type -The experiment type (the four enum axes) can only be set at creation time. It -cannot be changed afterwards. This avoids the complexity of maintaining -different state transformations when switching between fundamentally different -experiment configurations. +The experiment type (the four enum axes) can only be set at creation +time. It cannot be changed afterwards. This avoids the complexity of +maintaining different state transformations when switching between +fundamentally different experiment configurations. ### 9.3 Category Type Switching -In contrast to experiment type, categories that have multiple implementations -(peak profiles, backgrounds, instruments) can be switched at runtime by the -user. The API pattern uses a type property on the **experiment**, not on the -category itself: +In contrast to experiment type, categories that have multiple +implementations (peak profiles, backgrounds, instruments) can be +switched at runtime by the user. The API pattern uses a type property on +the **experiment**, not on the category itself: ```python # ✅ Correct — type property on the experiment @@ -874,16 +895,16 @@ expt.background_type = 'chebyshev' expt.background.type = 'chebyshev' ``` -This makes it clear that the entire category object is being replaced and -simplifies maintenance. +This makes it clear that the entire category object is being replaced +and simplifies maintenance. ### 9.4 Switchable-Category Convention -Categories whose concrete implementation can be swapped at runtime (background, -peak profile, etc.) are called **switchable categories**. **Every category must -be factory-based** — even if only one implementation exists today. This ensures -a uniform API, consistent discoverability, and makes adding a second -implementation trivial. +Categories whose concrete implementation can be swapped at runtime +(background, peak profile, etc.) are called **switchable categories**. +**Every category must be factory-based** — even if only one +implementation exists today. This ensures a uniform API, consistent +discoverability, and makes adding a second implementation trivial. | Facet | Naming pattern | Example | | --------------- | -------------------------------------------- | ------------------------------------------------ | @@ -894,26 +915,31 @@ implementation trivial. The convention applies universally: -- **Experiment:** `calculator_type`, `background_type`, `peak_profile_type`, - `extinction_type`, `linked_crystal_type`, `excluded_regions_type`, - `linked_phases_type`, `instrument_type`, `data_type`. +- **Experiment:** `calculator_type`, `background_type`, + `peak_profile_type`, `extinction_type`, `linked_crystal_type`, + `excluded_regions_type`, `linked_phases_type`, `instrument_type`, + `data_type`. - **Structure:** `cell_type`, `space_group_type`, `atom_sites_type`. - **Analysis:** `aliases_type`, `constraints_type`, `fit_mode_type`, `joint_fit_experiments_type`. **Design decisions:** -- The **experiment owns** the `_type` setter because switching replaces the - entire category object (`self._background = BackgroundFactory.create(...)`). -- The **experiment owns** the `show_*` methods because they are one-liners that - delegate to `Factory.show_supported(...)` and can pass experiment-specific - context (e.g. `scattering_type`, `beam_mode` for peak filtering). -- Concrete category subclasses provide a public `show()` method for displaying - the current content (not on the base `CategoryItem`/`CategoryCollection`). +- The **experiment owns** the `_type` setter because switching replaces + the entire category object + (`self._background = BackgroundFactory.create(...)`). +- The **experiment owns** the `show_*` methods because they are + one-liners that delegate to `Factory.show_supported(...)` and can pass + experiment-specific context (e.g. `scattering_type`, `beam_mode` for + peak filtering). +- Concrete category subclasses provide a public `show()` method for + displaying the current content (not on the base + `CategoryItem`/`CategoryCollection`). ### 9.5 Discoverable Supported Options -The user can always discover what is supported for the current experiment: +The user can always discover what is supported for the current +experiment: ```python expt.show_supported_peak_profile_types() @@ -935,36 +961,38 @@ project.analysis.show_supported_joint_fit_experiments_types() project.analysis.show_available_minimizers() ``` -Available calculators are filtered by `engine_imported` (whether the library is -installed) and by the experiment's data category `calculator_support` metadata. +Available calculators are filtered by `engine_imported` (whether the +library is installed) and by the experiment's data category +`calculator_support` metadata. ### 9.6 Enums for Finite Value Sets -Every attribute, descriptor, or configuration option that accepts a **finite, -closed set of values** must be represented by a `(str, Enum)` class. This -applies to: +Every attribute, descriptor, or configuration option that accepts a +**finite, closed set of values** must be represented by a `(str, Enum)` +class. This applies to: - Factory tags (§5.6) — e.g. `PeakProfileTypeEnum`, `CalculatorEnum`. - Experiment-axis values — e.g. `SampleFormEnum`, `BeamModeEnum`. - Category descriptors with enumerated choices — e.g. fit mode (`FitModeEnum.SINGLE`, `FitModeEnum.JOINT`). -The enum serves as the **single source of truth** for valid values, their -user-facing string representations, and their descriptions. Benefits: +The enum serves as the **single source of truth** for valid values, +their user-facing string representations, and their descriptions. +Benefits: -- **Autocomplete and typo safety** — IDEs list valid members; misspellings are - caught at assignment time. -- **Greppable** — searching for `FitModeEnum.JOINT` finds every code path that - handles joint fitting. -- **Type-safe dispatch** — `if mode == FitModeEnum.JOINT:` is checked by type - checkers; `if mode == 'joint':` is not. -- **Consistent validation** — use `MembershipValidator` with the enum members - instead of `RegexValidator` with hand-written patterns. +- **Autocomplete and typo safety** — IDEs list valid members; + misspellings are caught at assignment time. +- **Greppable** — searching for `FitModeEnum.JOINT` finds every code + path that handles joint fitting. +- **Type-safe dispatch** — `if mode == FitModeEnum.JOINT:` is checked by + type checkers; `if mode == 'joint':` is not. +- **Consistent validation** — use `MembershipValidator` with the enum + members instead of `RegexValidator` with hand-written patterns. -**Rule:** internal code must compare against enum members, never raw strings. -User-facing setters accept either the enum member or its string value (because -`str(EnumMember) == EnumMember.value` for `(str, Enum)`), but internal dispatch -always uses the enum: +**Rule:** internal code must compare against enum members, never raw +strings. User-facing setters accept either the enum member or its string +value (because `str(EnumMember) == EnumMember.value` for `(str, Enum)`), +but internal dispatch always uses the enum: ```python # ✅ Correct — compare with enum @@ -976,10 +1004,10 @@ if self._fit_mode.mode.value == 'joint': ### 9.7 Flat Category Structure — No Nested Categories -Following CIF conventions, categories are **flat siblings** within their owner -(datablock or analysis object). A category must never be a child of another -category of a different type. Categories can reference each other via IDs, but -the ownership hierarchy is always: +Following CIF conventions, categories are **flat siblings** within their +owner (datablock or analysis object). A category must never be a child +of another category of a different type. Categories can reference each +other via IDs, but the ownership hierarchy is always: ``` Owner (DatablockItem / Analysis) @@ -999,7 +1027,8 @@ Owner **Example — `fit_mode` and `joint_fit_experiments`:** `fit_mode` is a `CategoryItem` holding the active strategy (`'single'` or `'joint'`). `joint_fit_experiments` is a separate `CategoryCollection` holding -per-experiment weights. Both are direct children of `Analysis`, not nested: +per-experiment weights. Both are direct children of `Analysis`, not +nested: ```python # ✅ Correct — sibling categories on Analysis @@ -1027,8 +1056,8 @@ xrd 0.3 ## 10. Issues - **Open:** [`issues_open.md`](issues_open.md) — prioritised backlog. -- **Closed:** [`issues_closed.md`](issues_closed.md) — resolved items for - reference. +- **Closed:** [`issues_closed.md`](issues_closed.md) — resolved items + for reference. When a resolution affects the architecture described above, the relevant sections of this document are updated accordingly. diff --git a/docs/architecture/issues_closed.md b/docs/architecture/issues_closed.md index bc8ea19b..a67edcbe 100644 --- a/docs/architecture/issues_closed.md +++ b/docs/architecture/issues_closed.md @@ -6,26 +6,29 @@ Issues that have been fully resolved. Kept for historical reference. ## Dirty-Flag Guard Was Disabled -**Resolution:** added `_set_value_from_minimizer()` on `GenericDescriptorBase` -that writes `_value` directly (no validation) but sets the dirty flag on the -parent `DatablockItem`. Both `LmfitMinimizer` and `DfolsMinimizer` now use it. -The guard in `DatablockItem._update_categories()` is enabled and skips redundant -updates on the user-facing path (CIF export, plotting). During fitting the guard -is bypassed (`called_by_minimizer=True`) because experiment calculations depend -on structure parameters owned by a different `DatablockItem`. +**Resolution:** added `_set_value_from_minimizer()` on +`GenericDescriptorBase` that writes `_value` directly (no validation) +but sets the dirty flag on the parent `DatablockItem`. Both +`LmfitMinimizer` and `DfolsMinimizer` now use it. The guard in +`DatablockItem._update_categories()` is enabled and skips redundant +updates on the user-facing path (CIF export, plotting). During fitting +the guard is bypassed (`called_by_minimizer=True`) because experiment +calculations depend on structure parameters owned by a different +`DatablockItem`. --- ## Move Calculator from Global to Per-Experiment -**Resolution:** removed the global calculator from `Analysis`. Each experiment -now owns its calculator, auto-resolved on first access from -`CalculatorFactory._default_rules` (maps `scattering_type` → default tag) and -filtered by the data category's `calculator_support` metadata (e.g. `PdCwlData` -→ `{CRYSPY}`, `TotalData` → `{PDFFIT}`). Calculator classes no longer carry -`compatibility` attributes — limitations are expressed on categories. The -experiment exposes the standard switchable-category API: `calculator` -(read-only, lazy), `calculator_type` (getter + setter), +**Resolution:** removed the global calculator from `Analysis`. Each +experiment now owns its calculator, auto-resolved on first access from +`CalculatorFactory._default_rules` (maps `scattering_type` → default +tag) and filtered by the data category's `calculator_support` metadata +(e.g. `PdCwlData` → `{CRYSPY}`, `TotalData` → `{PDFFIT}`). Calculator +classes no longer carry `compatibility` attributes — limitations are +expressed on categories. The experiment exposes the standard +switchable-category API: `calculator` (read-only, lazy), +`calculator_type` (getter + setter), `show_supported_calculator_types()`, `show_current_calculator_type()`. Tutorials, tests, and docs updated. @@ -33,28 +36,32 @@ Tutorials, tests, and docs updated. ## Add Universal Factories for All Categories -**Resolution:** converted every category to use the `FactoryBase` pattern. Each -former single-file category is now a package with `factory.py` (trivial -`FactoryBase` subclass), `default.py` (concrete class with `@register` + -`type_info`), and `__init__.py` (re-exports preserving import compatibility). +**Resolution:** converted every category to use the `FactoryBase` +pattern. Each former single-file category is now a package with +`factory.py` (trivial `FactoryBase` subclass), `default.py` (concrete +class with `@register` + `type_info`), and `__init__.py` (re-exports +preserving import compatibility). -Experiment categories: `Extinction` → `ShelxExtinction` / `ExtinctionFactory` -(tag `shelx`), `LinkedCrystal` / `LinkedCrystalFactory` (tag `default`), -`ExcludedRegions` / `ExcludedRegionsFactory`, `LinkedPhases` / -`LinkedPhasesFactory`, `ExperimentType` / `ExperimentTypeFactory`. +Experiment categories: `Extinction` → `ShelxExtinction` / +`ExtinctionFactory` (tag `shelx`), `LinkedCrystal` / +`LinkedCrystalFactory` (tag `default`), `ExcludedRegions` / +`ExcludedRegionsFactory`, `LinkedPhases` / `LinkedPhasesFactory`, +`ExperimentType` / `ExperimentTypeFactory`. Structure categories: `Cell` / `CellFactory`, `SpaceGroup` / `SpaceGroupFactory`, `AtomSites` / `AtomSitesFactory`. Analysis categories: `Aliases` / `AliasesFactory`, `Constraints` / -`ConstraintsFactory`, `JointFitExperiments` / `JointFitExperimentsFactory`. - -`ShelxExtinction` and `LinkedCrystal` get the full switchable-category API on -`ScExperimentBase` (`extinction_type`, `linked_crystal_type` getter+setter, -`show_supported_*_types()`, `show_current_*_type()`). `ExcludedRegions` and -`LinkedPhases` get the same API on `PdExperimentBase`. `Cell`, `SpaceGroup`, and -`AtomSites` get it on `Structure`. `Aliases` and `Constraints` get it on -`Analysis`. Architecture §3.3, §5.5, §5.7, §9.4, §9.5 updated. Copilot -instructions updated with universal switchable-category scope and -architecture-first workflow rule. Unit tests extended with factory tests for -extinction and linked-crystal. +`ConstraintsFactory`, `JointFitExperiments` / +`JointFitExperimentsFactory`. + +`ShelxExtinction` and `LinkedCrystal` get the full switchable-category +API on `ScExperimentBase` (`extinction_type`, `linked_crystal_type` +getter+setter, `show_supported_*_types()`, `show_current_*_type()`). +`ExcludedRegions` and `LinkedPhases` get the same API on +`PdExperimentBase`. `Cell`, `SpaceGroup`, and `AtomSites` get it on +`Structure`. `Aliases` and `Constraints` get it on `Analysis`. +Architecture §3.3, §5.5, §5.7, §9.4, §9.5 updated. Copilot instructions +updated with universal switchable-category scope and architecture-first +workflow rule. Unit tests extended with factory tests for extinction and +linked-crystal. diff --git a/docs/architecture/issues_open.md b/docs/architecture/issues_open.md index d8bd37a2..05ed175f 100644 --- a/docs/architecture/issues_open.md +++ b/docs/architecture/issues_open.md @@ -1,9 +1,10 @@ # EasyDiffraction — Open Issues -Prioritised list of issues, improvements, and design questions to address. Items -are ordered by a combination of user impact, blocking potential, and -implementation readiness. When an item is fully implemented, remove it from this -file and update `architecture.md` if needed. +Prioritised list of issues, improvements, and design questions to +address. Items are ordered by a combination of user impact, blocking +potential, and implementation readiness. When an item is fully +implemented, remove it from this file and update `architecture.md` if +needed. **Legend:** 🔴 High · 🟡 Medium · 🟢 Low @@ -13,15 +14,16 @@ file and update `architecture.md` if needed. **Type:** Completeness -`save()` serialises all components to CIF files but `load()` is a stub that -raises `NotImplementedError`. Users cannot round-trip a project. +`save()` serialises all components to CIF files but `load()` is a stub +that raises `NotImplementedError`. Users cannot round-trip a project. **Why first:** this is the highest-severity gap. Without it the save -functionality is only half useful — CIF files are written but cannot be read -back. Tutorials that demonstrate save/load are blocked. +functionality is only half useful — CIF files are written but cannot be +read back. Tutorials that demonstrate save/load are blocked. -**Fix:** implement `load()` that reads CIF files from the project directory and -reconstructs structures, experiments, and analysis settings. +**Fix:** implement `load()` that reads CIF files from the project +directory and reconstructs structures, experiments, and analysis +settings. **Depends on:** nothing (standalone). @@ -35,11 +37,12 @@ After the `FactoryBase` migration only `'lmfit'` and `'dfols'` remain as registered tags. The ability to select a specific lmfit algorithm (e.g. `'lmfit (leastsq)'`, `'lmfit (least_squares)'`) raises a `ValueError`. -The root cause is that `FactoryBase` assumes one class ↔ one tag; registering -the same class twice with different constructor arguments is not supported. +The root cause is that `FactoryBase` assumes one class ↔ one tag; +registering the same class twice with different constructor arguments is +not supported. -**Fix:** decide on an approach (thin subclasses, extended registry, or two-level -selection) and implement. Thin subclasses is the quickest. +**Fix:** decide on an approach (thin subclasses, extended registry, or +two-level selection) and implement. Thin subclasses is the quickest. **Planned tags:** @@ -58,8 +61,8 @@ selection) and implement. Thin subclasses is the quickest. | **B. Extend registry to store `(class, kwargs)` tuples** | No extra classes; factory handles variants natively | `_supported_map` changes shape; `TypeInfo` moves from class attribute to registration-time data | | **C. Two-level selection** (`engine` + `algorithm`) | Clean separation; engine maps to class, algorithm is a constructor arg | More complex API (`current_minimizer = ('lmfit', 'least_squares')`); needs new `FactoryBase` protocol | -**Depends on:** nothing (standalone, but should be decided before more factories -adopt variants). +**Depends on:** nothing (standalone, but should be decided before more +factories adopt variants). --- @@ -67,13 +70,14 @@ adopt variants). **Type:** Fragility -`joint_fit_experiments` is created once when `fit_mode` becomes `'joint'`. If -experiments are added, removed, or renamed afterwards, the weight collection is -stale. Joint fitting can fail with missing keys or run with incorrect weights. +`joint_fit_experiments` is created once when `fit_mode` becomes +`'joint'`. If experiments are added, removed, or renamed afterwards, the +weight collection is stale. Joint fitting can fail with missing keys or +run with incorrect weights. -**Fix:** rebuild or validate `joint_fit_experiments` at the start of every joint -fit. At minimum, `fit()` should assert that the weight keys exactly match -`project.experiments.names`. +**Fix:** rebuild or validate `joint_fit_experiments` at the start of +every joint fit. At minimum, `fit()` should assert that the weight keys +exactly match `project.experiments.names`. **Depends on:** nothing. @@ -85,18 +89,20 @@ fit. At minimum, `fit()` should assert that the weight keys exactly match `ConstraintsHandler` is only synchronised from `analysis.aliases` and `analysis.constraints` when the user explicitly calls -`project.analysis.apply_constraints()`. The normal fit / serialisation path -calls `constraints_handler.apply()` directly, so newly added or edited aliases -and constraints can be ignored until that manual sync step happens. +`project.analysis.apply_constraints()`. The normal fit / serialisation +path calls `constraints_handler.apply()` directly, so newly added or +edited aliases and constraints can be ignored until that manual sync +step happens. -**Why high:** this produces silently incorrect results. A user can define -constraints, run a fit, and believe they were applied when the active singleton -still contains stale state from a previous run or no state at all. +**Why high:** this produces silently incorrect results. A user can +define constraints, run a fit, and believe they were applied when the +active singleton still contains stale state from a previous run or no +state at all. **Fix:** before any automatic constraint application, always refresh the -singleton from the current `Aliases` and `Constraints` collections. The sync -should happen inside `Analysis._update_categories()` or inside the constraints -category itself, not only in a user-facing helper method. +singleton from the current `Aliases` and `Constraints` collections. The +sync should happen inside `Analysis._update_categories()` or inside the +constraints category itself, not only in a user-facing helper method. **Depends on:** nothing. @@ -106,10 +112,11 @@ category itself, not only in a user-facing helper method. **Type:** Consistency -`Analysis` owns categories (`Aliases`, `Constraints`, `JointFitExperiments`) but -does not extend `DatablockItem`. Its ad-hoc `_update_categories()` iterates over -a hard-coded list and does not participate in standard category discovery, -parameter enumeration, or CIF serialisation. +`Analysis` owns categories (`Aliases`, `Constraints`, +`JointFitExperiments`) but does not extend `DatablockItem`. Its ad-hoc +`_update_categories()` iterates over a hard-coded list and does not +participate in standard category discovery, parameter enumeration, or +CIF serialisation. **Fix:** make `Analysis` extend `DatablockItem`, or extract a shared `_update_categories()` protocol. @@ -122,20 +129,22 @@ parameter enumeration, or CIF serialisation. **Type:** Correctness + Data safety -`Experiment.data_type` currently validates against all registered data tags -rather than only those compatible with the experiment's `sample_form` / -`scattering_type` / `beam_mode`. This allows users to switch an experiment to an -incompatible data collection class. The setter also replaces the existing data -object with a fresh empty instance, discarding loaded data without warning. +`Experiment.data_type` currently validates against all registered data +tags rather than only those compatible with the experiment's +`sample_form` / `scattering_type` / `beam_mode`. This allows users to +switch an experiment to an incompatible data collection class. The +setter also replaces the existing data object with a fresh empty +instance, discarding loaded data without warning. -**Why high:** the current API can create internally inconsistent experiments and -silently lose measured data, which is especially dangerous for notebook and -tutorial workflows. +**Why high:** the current API can create internally inconsistent +experiments and silently lose measured data, which is especially +dangerous for notebook and tutorial workflows. -**Fix:** filter supported data types through `DataFactory.supported_for(...)` -using the current experiment context, and warn or block when a switch would -discard existing data. If runtime data-type switching is not a real user need, -consider making `data` effectively fixed after experiment creation. +**Fix:** filter supported data types through +`DataFactory.supported_for(...)` using the current experiment context, +and warn or block when a switch would discard existing data. If runtime +data-type switching is not a real user need, consider making `data` +effectively fixed after experiment creation. **Depends on:** nothing. @@ -145,16 +154,17 @@ consider making `data` effectively fixed after experiment creation. **Type:** Fragility -Single-fit mode creates a throw-away `Experiments` collection per experiment, -manually forces `_parent` via `object.__setattr__`, and passes it to `Fitter`. -This bypasses `GuardedBase` parent tracking and is fragile. +Single-fit mode creates a throw-away `Experiments` collection per +experiment, manually forces `_parent` via `object.__setattr__`, and +passes it to `Fitter`. This bypasses `GuardedBase` parent tracking and +is fragile. -**Fix:** make `Fitter.fit()` accept a list of experiment objects (or a single -experiment) instead of requiring an `Experiments` collection. Or add a -`fit_single(experiment)` method. +**Fix:** make `Fitter.fit()` accept a list of experiment objects (or a +single experiment) instead of requiring an `Experiments` collection. Or +add a `fit_single(experiment)` method. -**Depends on:** nothing, but simpler after issue 5 (Analysis refactor) clarifies -the fitting orchestration. +**Depends on:** nothing, but simpler after issue 5 (Analysis refactor) +clarifies the fitting orchestration. --- @@ -162,14 +172,15 @@ the fitting orchestration. **Type:** API safety -`CategoryCollection.create(**kwargs)` accepts arbitrary keyword arguments and -applies them via `setattr`. Typos are silently dropped (GuardedBase logs a -warning but does not raise), so items are created with incorrect defaults. +`CategoryCollection.create(**kwargs)` accepts arbitrary keyword +arguments and applies them via `setattr`. Typos are silently dropped +(GuardedBase logs a warning but does not raise), so items are created +with incorrect defaults. -**Fix:** concrete collection subclasses (e.g. `AtomSites`, `Background`) should -override `create()` with explicit parameters for IDE autocomplete and typo -detection. The base `create(**kwargs)` remains as an internal implementation -detail. +**Fix:** concrete collection subclasses (e.g. `AtomSites`, `Background`) +should override `create()` with explicit parameters for IDE autocomplete +and typo detection. The base `create(**kwargs)` remains as an internal +implementation detail. **Depends on:** nothing. @@ -179,7 +190,8 @@ detail. **Type:** Design improvement -The four current experiment axes will be extended with at least two more: +The four current experiment axes will be extended with at least two +more: | New axis | Options | Enum (proposed) | | ------------------- | ---------------------- | ------------------------ | @@ -187,12 +199,12 @@ The four current experiment axes will be extended with at least two more: | Beam polarisation | unpolarised, polarised | `PolarisationEnum` | These should follow the same `str, Enum` pattern and integrate into -`Compatibility` (new `FrozenSet` fields), `_default_rules`, and `ExperimentType` -(new `StringDescriptor`s with `MembershipValidator`s). +`Compatibility` (new `FrozenSet` fields), `_default_rules`, and +`ExperimentType` (new `StringDescriptor`s with `MembershipValidator`s). -**Migration path:** existing `Compatibility` objects that don't specify the new -fields use `frozenset()` (empty = "any"), so all existing classes remain -compatible without changes. +**Migration path:** existing `Compatibility` objects that don't specify +the new fields use `frozenset()` (empty = "any"), so all existing +classes remain compatible without changes. **Depends on:** nothing. @@ -202,17 +214,18 @@ compatible without changes. **Type:** Maintainability -`Project._update_categories(expt_name)` hard-codes the update order (structures -→ analysis → one experiment). The `_update_priority` system exists on categories -but is not used across datablocks. The `expt_name` parameter means only one -experiment is updated per call, inconsistent with joint-fit workflows. +`Project._update_categories(expt_name)` hard-codes the update order +(structures → analysis → one experiment). The `_update_priority` system +exists on categories but is not used across datablocks. The `expt_name` +parameter means only one experiment is updated per call, inconsistent +with joint-fit workflows. -**Fix:** consider a project-level `_update_priority` on datablocks, or at -minimum document the required update order. For joint fitting, all experiments -should be updateable in a single call. +**Fix:** consider a project-level `_update_priority` on datablocks, or +at minimum document the required update order. For joint fitting, all +experiments should be updateable in a single call. -**Depends on:** benefits from issue 5 (Analysis as DatablockItem) and issue 7 -(fitter refactor). +**Depends on:** benefits from issue 5 (Analysis as DatablockItem) and +issue 7 (fitter refactor). --- @@ -220,17 +233,18 @@ should be updateable in a single call. **Type:** Maintainability -`_update()` is an optional override with a no-op default. A clearer contract -would help contributors: +`_update()` is an optional override with a no-op default. A clearer +contract would help contributors: -- **Active categories** (those that compute something, e.g. `Background`, - `Data`) should have an explicit `_update()` implementation. +- **Active categories** (those that compute something, e.g. + `Background`, `Data`) should have an explicit `_update()` + implementation. - **Passive categories** (those that only store parameters, e.g. `Cell`, `SpaceGroup`) keep the no-op default. The distinction is already implicit in the code; making it explicit in -documentation (and possibly via a naming convention or flag) would reduce -confusion for new contributors. +documentation (and possibly via a naming convention or flag) would +reduce confusion for new contributors. **Depends on:** nothing. @@ -240,10 +254,10 @@ confusion for new contributors. **Type:** Quality -Ensuring every parameter survives a `save()` → `load()` cycle is critical for -reproducibility. A systematic integration test that creates a project, populates -all categories, saves, reloads, and compares all parameter values would -strengthen confidence in the serialisation layer. +Ensuring every parameter survives a `save()` → `load()` cycle is +critical for reproducibility. A systematic integration test that creates +a project, populates all categories, saves, reloads, and compares all +parameter values would strengthen confidence in the serialisation layer. **Depends on:** issue 1 (`Project.load()` implementation). @@ -253,17 +267,18 @@ strengthen confidence in the serialisation layer. **Type:** Performance -Symmetry constraint application (cell metric, atomic coordinates, ADPs) goes -through the public `value` setter for each parameter, setting the dirty flag -repeatedly during what is logically a single batch operation. +Symmetry constraint application (cell metric, atomic coordinates, ADPs) +goes through the public `value` setter for each parameter, setting the +dirty flag repeatedly during what is logically a single batch operation. No correctness issue — the dirty-flag guard handles this correctly. The -redundant sets are a minor inefficiency that only matters if profiling shows it -is a bottleneck. +redundant sets are a minor inefficiency that only matters if profiling +shows it is a bottleneck. **Fix:** introduce a private `_set_value_no_notify()` method on -`GenericDescriptorBase` for internal batch operations, or a context manager / -flag on the owning datablock to suppress notifications during a batch. +`GenericDescriptorBase` for internal batch operations, or a context +manager / flag on the owning datablock to suppress notifications during +a batch. **Depends on:** nothing, but low priority. @@ -273,11 +288,12 @@ flag on the owning datablock to suppress notifications during a batch. **Type:** Performance -The current dirty-flag approach (`_need_categories_update` on `DatablockItem`) -triggers a full update of all categories when any parameter changes. This is -simple and correct. If performance becomes a concern with many categories, a -more granular approach could track which specific categories are dirty. Only -implement when profiling proves it is needed. +The current dirty-flag approach (`_need_categories_update` on +`DatablockItem`) triggers a full update of all categories when any +parameter changes. This is simple and correct. If performance becomes a +concern with many categories, a more granular approach could track which +specific categories are dirty. Only implement when profiling proves it +is needed. **Depends on:** nothing, but low priority. @@ -287,14 +303,16 @@ implement when profiling proves it is needed. **Type:** Correctness -Joint-fit weights currently allow invalid numeric values such as negatives or an -all-zero set. The residual code then normalises by the total weight and applies -`sqrt(weight)`, which can produce division-by-zero or `nan` residuals. +Joint-fit weights currently allow invalid numeric values such as +negatives or an all-zero set. The residual code then normalises by the +total weight and applies `sqrt(weight)`, which can produce +division-by-zero or `nan` residuals. -**Fix:** require weights to be strictly positive, or at minimum validate that -all weights are non-negative and their total is greater than zero before -normalisation. This should fail with a clear user-facing error instead of -letting invalid floating-point values propagate into the minimiser. +**Fix:** require weights to be strictly positive, or at minimum validate +that all weights are non-negative and their total is greater than zero +before normalisation. This should fail with a clear user-facing error +instead of letting invalid floating-point values propagate into the +minimiser. **Depends on:** related to issue 3, but independent. @@ -304,14 +322,16 @@ letting invalid floating-point values propagate into the minimiser. **Type:** Completeness -The current architecture moved calculator selection to the experiment level via -`calculator_type`, but this selection is not written to CIF during `save()` / -`show_as_cif()`. Reloading or exporting a project therefore loses explicit -calculator choices and falls back to auto-resolution. +The current architecture moved calculator selection to the experiment +level via `calculator_type`, but this selection is not written to CIF +during `save()` / `show_as_cif()`. Reloading or exporting a project +therefore loses explicit calculator choices and falls back to +auto-resolution. -**Fix:** serialise `calculator_type` as part of the experiment or analysis -state, and make sure `load()` restores it. The saved project should represent -the exact active calculator configuration, not just a re-derivable default. +**Fix:** serialise `calculator_type` as part of the experiment or +analysis state, and make sure `load()` restores it. The saved project +should represent the exact active calculator configuration, not just a +re-derivable default. **Depends on:** issue 1 (`Project.load()` implementation). diff --git a/docs/docs/api-reference/index.md b/docs/docs/api-reference/index.md index 6a6f8be4..351d7f56 100644 --- a/docs/docs/api-reference/index.md +++ b/docs/docs/api-reference/index.md @@ -7,19 +7,20 @@ icon: material/code-braces-box This section contains the reference detailing the functions and modules available in EasyDiffraction: -- [core](core.md) – Contains core utilities and foundational objects used across - the package. -- [crystallography](crystallography.md) – Handles crystallographic calculations, - space groups, and symmetry operations. +- [core](core.md) – Contains core utilities and foundational objects + used across the package. +- [crystallography](crystallography.md) – Handles crystallographic + calculations, space groups, and symmetry operations. - [utils](utils.md) – Miscellaneous utility functions for formatting, decorators, and general helpers. - datablocks - - [experiments](datablocks/experiment.md) – Manages experimental setups and - instrument parameters, as well as the associated diffraction data. + - [experiments](datablocks/experiment.md) – Manages experimental + setups and instrument parameters, as well as the associated + diffraction data. - [structures](datablocks/structure.md) – Defines structures, such as crystallographic structures, and manages their properties. - [display](display.md) – Tools for plotting data and rendering tables. - [project](project.md) – Defines the project and manages its state. -- [analysis](analysis.md) – Provides tools for analyzing diffraction data, - including fitting and minimization. +- [analysis](analysis.md) – Provides tools for analyzing diffraction + data, including fitting and minimization. - [summary](summary.md) – Provides a summary of the project. diff --git a/docs/docs/index.md b/docs/docs/index.md index a34d3023..73c35423 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -4,17 +4,18 @@ Here is a brief overview of the main documentation sections: -- [:material-information-slab-circle: Introduction](introduction/index.md) – - Provides an overview of EasyDiffraction, including its purpose, licensing, - latest release details, and contact information. -- [:material-cog-box: Installation & Setup](installation-and-setup/index.md) – - Guides users through system requirements, environment configuration, and the - installation process. -- [:material-book-open-variant: User Guide](user-guide/index.md) – Covers core - concepts, key terminology, workflow steps, and essential parameters for - effective use of EasyDiffraction. +- [:material-information-slab-circle: Introduction](introduction/index.md) + – Provides an overview of EasyDiffraction, including its purpose, + licensing, latest release details, and contact information. +- [:material-cog-box: Installation & Setup](installation-and-setup/index.md) + – Guides users through system requirements, environment configuration, + and the installation process. +- [:material-book-open-variant: User Guide](user-guide/index.md) – + Covers core concepts, key terminology, workflow steps, and essential + parameters for effective use of EasyDiffraction. - [:material-school: Tutorials](tutorials/index.md) – Offers practical, - step-by-step examples demonstrating common workflows and data analysis tasks. -- [:material-code-braces-box: API Reference](api-reference/index.md) – An - auto-generated reference detailing the available functions and modules in - EasyDiffraction. + step-by-step examples demonstrating common workflows and data analysis + tasks. +- [:material-code-braces-box: API Reference](api-reference/index.md) – + An auto-generated reference detailing the available functions and + modules in EasyDiffraction. diff --git a/docs/docs/installation-and-setup/index.md b/docs/docs/installation-and-setup/index.md index 80b64235..0891a55f 100644 --- a/docs/docs/installation-and-setup/index.md +++ b/docs/docs/installation-and-setup/index.md @@ -6,16 +6,16 @@ icon: material/cog-box ## Requirements -EasyDiffraction is a cross-platform Python library compatible with **Python 3.11 -through 3.13**. +EasyDiffraction is a cross-platform Python library compatible with +**Python 3.11 through 3.13**. Make sure Python is installed on your system before proceeding with the installation. ## Environment Setup optional { #environment-setup data-toc-label="Environment Setup" } -We recommend using a **virtual environment** to isolate dependencies and avoid -conflicts with system-wide packages. If any issues arise, you can simply delete -and recreate the environment. +We recommend using a **virtual environment** to isolate dependencies and +avoid conflicts with system-wide packages. If any issues arise, you can +simply delete and recreate the environment. #### Creating and Activating a Virtual Environment: @@ -76,22 +76,23 @@ and recreate the environment. ### Installing from PyPI recommended { #from-pypi data-toc-label="Installing from PyPI" } -EasyDiffraction is available on **PyPI (Python Package Index)** and can be -installed using `pip`. We strongly recommend installing it within a virtual -environment, as described in the [Environment Setup](#environment-setup) -section. +EasyDiffraction is available on **PyPI (Python Package Index)** and can +be installed using `pip`. We strongly recommend installing it within a +virtual environment, as described in the +[Environment Setup](#environment-setup) section. We recommend installing the latest release of EasyDiffraction with the -`visualization` extras, which include optional dependencies used for simplified -visualization of charts and tables. This can be especially useful for running -the Jupyter Notebook examples. To do so, use the following command: +`visualization` extras, which include optional dependencies used for +simplified visualization of charts and tables. This can be especially +useful for running the Jupyter Notebook examples. To do so, use the +following command: ```bash pip install 'easydiffraction[visualization]' ``` -If only the core functionality is needed, the library can be installed simply -with: +If only the core functionality is needed, the library can be installed +simply with: ```bash pip install easydiffraction @@ -117,8 +118,8 @@ pip show easydiffraction ### Installing from GitHub -Installing unreleased versions is generally not recommended but may be useful -for testing. +Installing unreleased versions is generally not recommended but may be +useful for testing. To install EasyDiffraction from, e.g., the `develop` branch of GitHub: @@ -134,18 +135,20 @@ pip install 'easydiffraction[visualization] @ git+https://github.com/easyscience ## How to Run Tutorials -EasyDiffraction includes a collection of **Jupyter Notebook examples** that -demonstrate key functionality. These tutorials serve as **step-by-step guides** -to help users understand the diffraction data analysis workflow. +EasyDiffraction includes a collection of **Jupyter Notebook examples** +that demonstrate key functionality. These tutorials serve as +**step-by-step guides** to help users understand the diffraction data +analysis workflow. They are available as **static HTML pages** in the -[:material-school: Tutorials](../tutorials/index.md) section. You can also run -them interactively in two ways: +[:material-school: Tutorials](../tutorials/index.md) section. You can +also run them interactively in two ways: - **Run Locally** – Download the notebook via the :material-download: **Download** button and run it on your computer. -- **Run Online** – Use the :google-colab: **Open in Google Colab** button to run - the tutorial directly in your browser (no setup required). +- **Run Online** – Use the :google-colab: **Open in Google Colab** + button to run the tutorial directly in your browser (no setup + required). !!! note @@ -154,8 +157,9 @@ them interactively in two ways: ### Run Tutorials Locally -To run tutorials locally, install **Jupyter Notebook** or **JupyterLab**. Here -are the steps to follow in the case of **Jupyter Notebook**: +To run tutorials locally, install **Jupyter Notebook** or +**JupyterLab**. Here are the steps to follow in the case of **Jupyter +Notebook**: - Install Jupyter Notebook and IPython kernel: ```bash @@ -177,32 +181,34 @@ are the steps to follow in the case of **Jupyter Notebook**: ```bash http://localhost:8888/ ``` -- Open one of the `*.ipynb` files and select the `EasyDiffraction Python kernel` - to get started. +- Open one of the `*.ipynb` files and select the + `EasyDiffraction Python kernel` to get started. ### Run Tutorials via Google Colab -**Google Colab** lets you run Jupyter Notebooks in the cloud without any local -installation. +**Google Colab** lets you run Jupyter Notebooks in the cloud without any +local installation. To use Google Colab: - Ensure you have a **Google account**. -- Go to the **[:material-school: Tutorials](../tutorials/index.md)** section. -- Click the :google-colab: **Open in Google Colab** button on any tutorial. +- Go to the **[:material-school: Tutorials](../tutorials/index.md)** + section. +- Click the :google-colab: **Open in Google Colab** button on any + tutorial. -This is the fastest way to start experimenting with EasyDiffraction, without -setting up Python on your system. +This is the fastest way to start experimenting with EasyDiffraction, +without setting up Python on your system. ## Installing with Pixi alternative { #installing-with-pixi data-toc-label="Installing with Pixi" } -[Pixi](https://pixi.sh) is a modern package and environment manager for Python -and Conda-compatible packages. It simplifies dependency management, environment -isolation, and reproducibility. +[Pixi](https://pixi.sh) is a modern package and environment manager for +Python and Conda-compatible packages. It simplifies dependency +management, environment isolation, and reproducibility. The following simple steps provide an alternative setup method for -EasyDiffraction using Pixi, replacing the traditional virtual environment -approach. +EasyDiffraction using Pixi, replacing the traditional virtual +environment approach. diff --git a/docs/docs/introduction/index.md b/docs/docs/introduction/index.md index 2996386b..a2b42b59 100644 --- a/docs/docs/introduction/index.md +++ b/docs/docs/introduction/index.md @@ -8,21 +8,22 @@ icon: material/information-slab-circle **EasyDiffraction** is scientific software for calculating diffraction patterns -based on structural models and refining model parameters against experimental -data. +based on structural models and refining model parameters against +experimental data. -It is available as both a cross-platform desktop application and a Python -library. +It is available as both a cross-platform desktop application and a +Python library. -This documentation covers the usage of the EasyDiffraction Python library. +This documentation covers the usage of the EasyDiffraction Python +library. For the graphical user interface (GUI) version, refer to the [GUI documentation](https://docs.easydiffraction.org/app). ## EasyScience EasyDiffraction is developed using the -[EasyScience framework](https://easyscience.software), which provides tools -for +[EasyScience framework](https://easyscience.software), which provides +tools for building modular and flexible scientific libraries and applications. ## License @@ -35,27 +36,28 @@ EasyDiffraction is released under the The latest version of the EasyDiffraction Python library is [{{ vars.release_version }}](https://github.com/easyscience/diffraction-lib/releases/latest). -For a complete list of new features, bug fixes, and improvements, see the +For a complete list of new features, bug fixes, and improvements, see +the [GitHub Releases page](https://github.com/easyscience/diffraction-lib/releases). ## Citation -If you use EasyDiffraction in your work, please cite the specific version you -used. +If you use EasyDiffraction in your work, please cite the specific +version you used. All official releases of the EasyDiffraction library are archived on Zenodo, each with a version-specific Digital Object Identifier (DOI). -Citation details in various styles (e.g., APA, MLA) and formats (e.g., BibTeX, -JSON) +Citation details in various styles (e.g., APA, MLA) and formats (e.g., +BibTeX, JSON) are available on the [Zenodo archive page](https://doi.org/10.5281/zenodo.16806521). ## Contributing -We welcome contributions from the community! EasyDiffraction is intended to be a -community-driven, open-source project supported by a diverse group of -contributors. +We welcome contributions from the community! EasyDiffraction is intended +to be a community-driven, open-source project supported by a diverse +group of contributors. The project is maintained by the [European Spallation Source (ESS)](https://ess.eu). diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md index a7c2cc37..5e406e59 100644 --- a/docs/docs/tutorials/index.md +++ b/docs/docs/tutorials/index.md @@ -4,10 +4,10 @@ icon: material/school # :material-school: Tutorials -This section presents a collection of **Jupyter Notebook** tutorials that -demonstrate how to use EasyDiffraction for various tasks. These tutorials serve -as self-contained, step-by-step **guides** to help users grasp the workflow of -diffraction data analysis using EasyDiffraction. +This section presents a collection of **Jupyter Notebook** tutorials +that demonstrate how to use EasyDiffraction for various tasks. These +tutorials serve as self-contained, step-by-step **guides** to help users +grasp the workflow of diffraction data analysis using EasyDiffraction. Instructions on how to run the tutorials are provided in the [:material-cog-box: Installation & Setup](../installation-and-setup/index.md#how-to-run-tutorials) @@ -18,73 +18,78 @@ The tutorials are organized into the following categories. ## Getting Started - [LBCO `quick` CIF](ed-1.ipynb) – A minimal example intended as a quick - reference for users already familiar with the EasyDiffraction API or who want - to see how Rietveld refinement of the La0.5Ba0.5CoO3 crystal structure can be - performed when both the structure and experiment are loaded from CIF files. - Data collected from constant wavelength neutron powder diffraction at HRPT at - PSI. -- [LBCO `quick` `code`](ed-2.ipynb) – A minimal example intended as a quick - reference for users already familiar with the EasyDiffraction API or who want - to see an example refinement when both the structure and experiment are - defined directly in code. This tutorial covers a Rietveld refinement of the - La0.5Ba0.5CoO3 crystal structure using constant wavelength neutron powder - diffraction data from HRPT at PSI. -- [LBCO `complete`](ed-3.ipynb) – Demonstrates the use of the EasyDiffraction - API in a simplified, user-friendly manner that closely follows the GUI - workflow for a Rietveld refinement of the La0.5Ba0.5CoO3 crystal structure - using constant wavelength neutron powder diffraction data from HRPT at PSI. - This tutorial provides a full explanation of the workflow with detailed - comments and descriptions of every step, making it suitable for users who are - new to EasyDiffraction or those who prefer a more guided approach. + reference for users already familiar with the EasyDiffraction API or + who want to see how Rietveld refinement of the La0.5Ba0.5CoO3 crystal + structure can be performed when both the structure and experiment are + loaded from CIF files. Data collected from constant wavelength neutron + powder diffraction at HRPT at PSI. +- [LBCO `quick` `code`](ed-2.ipynb) – A minimal example intended as a + quick reference for users already familiar with the EasyDiffraction + API or who want to see an example refinement when both the structure + and experiment are defined directly in code. This tutorial covers a + Rietveld refinement of the La0.5Ba0.5CoO3 crystal structure using + constant wavelength neutron powder diffraction data from HRPT at PSI. +- [LBCO `complete`](ed-3.ipynb) – Demonstrates the use of the + EasyDiffraction API in a simplified, user-friendly manner that closely + follows the GUI workflow for a Rietveld refinement of the + La0.5Ba0.5CoO3 crystal structure using constant wavelength neutron + powder diffraction data from HRPT at PSI. This tutorial provides a + full explanation of the workflow with detailed comments and + descriptions of every step, making it suitable for users who are new + to EasyDiffraction or those who prefer a more guided approach. ## Powder Diffraction -- [Co2SiO4 `pd-neut-cwl`](ed-5.ipynb) – Demonstrates a Rietveld refinement of - the Co2SiO4 crystal structure using constant wavelength neutron powder - diffraction data from D20 at ILL. -- [HS `pd-neut-cwl`](ed-6.ipynb) – Demonstrates a Rietveld refinement of the HS - crystal structure using constant wavelength neutron powder diffraction data - from HRPT at PSI. -- [Si `pd-neut-tof`](ed-7.ipynb) – Demonstrates a Rietveld refinement of the Si - crystal structure using time-of-flight neutron powder diffraction data from - SEPD at Argonne. -- [NCAF `pd-neut-tof`](ed-8.ipynb) – Demonstrates a Rietveld refinement of the - Na2Ca3Al2F14 crystal structure using two time-of-flight neutron powder - diffraction datasets (from two detector banks) of the WISH instrument at ISIS. +- [Co2SiO4 `pd-neut-cwl`](ed-5.ipynb) – Demonstrates a Rietveld + refinement of the Co2SiO4 crystal structure using constant wavelength + neutron powder diffraction data from D20 at ILL. +- [HS `pd-neut-cwl`](ed-6.ipynb) – Demonstrates a Rietveld refinement of + the HS crystal structure using constant wavelength neutron powder + diffraction data from HRPT at PSI. +- [Si `pd-neut-tof`](ed-7.ipynb) – Demonstrates a Rietveld refinement of + the Si crystal structure using time-of-flight neutron powder + diffraction data from SEPD at Argonne. +- [NCAF `pd-neut-tof`](ed-8.ipynb) – Demonstrates a Rietveld refinement + of the Na2Ca3Al2F14 crystal structure using two time-of-flight neutron + powder diffraction datasets (from two detector banks) of the WISH + instrument at ISIS. ## Single Crystal Diffraction -- [Tb2TiO7 `sg-neut-cwl`](ed-14.ipynb) – Demonstrates structure refinement of - Tb2TiO7 using constant wavelength neutron single crystal diffraction data from - HEiDi at FRM II. -- [Taurine `sg-neut-tof`](ed-15.ipynb) – Demonstrates structure refinement of - Taurine using time-of-flight neutron single crystal diffraction data from - SENJU at J-PARC. +- [Tb2TiO7 `sg-neut-cwl`](ed-14.ipynb) – Demonstrates structure + refinement of Tb2TiO7 using constant wavelength neutron single crystal + diffraction data from HEiDi at FRM II. +- [Taurine `sg-neut-tof`](ed-15.ipynb) – Demonstrates structure + refinement of Taurine using time-of-flight neutron single crystal + diffraction data from SENJU at J-PARC. ## Pair Distribution Function (PDF) -- [Ni `pd-neut-cwl`](ed-10.ipynb) – Demonstrates a PDF analysis of Ni using data - collected from a constant wavelength neutron powder diffraction experiment. -- [Si `pd-neut-tof`](ed-11.ipynb) – Demonstrates a PDF analysis of Si using data - collected from a time-of-flight neutron powder diffraction experiment at NOMAD - at SNS. -- [NaCl `pd-xray`](ed-12.ipynb) – Demonstrates a PDF analysis of NaCl using data - collected from an X-ray powder diffraction experiment. +- [Ni `pd-neut-cwl`](ed-10.ipynb) – Demonstrates a PDF analysis of Ni + using data collected from a constant wavelength neutron powder + diffraction experiment. +- [Si `pd-neut-tof`](ed-11.ipynb) – Demonstrates a PDF analysis of Si + using data collected from a time-of-flight neutron powder diffraction + experiment at NOMAD at SNS. +- [NaCl `pd-xray`](ed-12.ipynb) – Demonstrates a PDF analysis of NaCl + using data collected from an X-ray powder diffraction experiment. ## Multi-Structure & Multi-Experiment Refinement -- [PbSO4 NPD+XRD](ed-4.ipynb) – Joint fit of PbSO4 using X-ray and neutron - constant wavelength powder diffraction data. +- [PbSO4 NPD+XRD](ed-4.ipynb) – Joint fit of PbSO4 using X-ray and + neutron constant wavelength powder diffraction data. - [LBCO+Si McStas](ed-9.ipynb) – Multi-phase Rietveld refinement of - La0.5Ba0.5CoO3 with Si impurity using time-of-flight neutron data simulated - with McStas. + La0.5Ba0.5CoO3 with Si impurity using time-of-flight neutron data + simulated with McStas. - [Si Bragg+PDF](ed-16.ipynb) – Joint refinement of Si combining Bragg - diffraction (SEPD) and pair distribution function (NOMAD) analysis. A single - shared structure is refined simultaneously against both datasets. + diffraction (SEPD) and pair distribution function (NOMAD) analysis. A + single shared structure is refined simultaneously against both + datasets. ## Workshops & Schools -- [DMSC Summer School](ed-13.ipynb) – A workshop tutorial that demonstrates a - Rietveld refinement of the La0.5Ba0.5CoO3 crystal structure using - time-of-flight neutron powder diffraction data simulated with McStas. This - tutorial is designed for the ESS DMSC Summer School. +- [DMSC Summer School](ed-13.ipynb) – A workshop tutorial that + demonstrates a Rietveld refinement of the La0.5Ba0.5CoO3 crystal + structure using time-of-flight neutron powder diffraction data + simulated with McStas. This tutorial is designed for the ESS DMSC + Summer School. diff --git a/docs/docs/user-guide/analysis-workflow/analysis.md b/docs/docs/user-guide/analysis-workflow/analysis.md index 73086b3b..f0341519 100644 --- a/docs/docs/user-guide/analysis-workflow/analysis.md +++ b/docs/docs/user-guide/analysis-workflow/analysis.md @@ -5,57 +5,62 @@ icon: material/calculator # :material-calculator: Analysis This section provides an overview of **diffraction data analysis** in -EasyDiffraction, focusing on model-dependent analysis, calculation engines, and -minimization techniques. +EasyDiffraction, focusing on model-dependent analysis, calculation +engines, and minimization techniques. -In EasyDiffraction, we focus on **model-dependent analysis**, where a model is -constructed based on prior knowledge of the studied system, and its parameters -are optimized to achieve the best agreement between experimental and calculated -diffraction data. Model-dependent analysis is widely used in neutron and X-ray -scattering data. +In EasyDiffraction, we focus on **model-dependent analysis**, where a +model is constructed based on prior knowledge of the studied system, and +its parameters are optimized to achieve the best agreement between +experimental and calculated diffraction data. Model-dependent analysis +is widely used in neutron and X-ray scattering data. ## Calculation -EasyDiffraction relies on third-party crystallographic libraries, referred to as -**calculation engines** or just **calculators**, to perform the calculations. +EasyDiffraction relies on third-party crystallographic libraries, +referred to as **calculation engines** or just **calculators**, to +perform the calculations. -The calculation engines are used to calculate the diffraction pattern for the -defined model of the studied structure using the instrumental and other required -experiment-related parameters, such as the wavelength, resolution, etc. +The calculation engines are used to calculate the diffraction pattern +for the defined model of the studied structure using the instrumental +and other required experiment-related parameters, such as the +wavelength, resolution, etc. -You do not necessarily need the measured data to perform the calculations, but -you need a structural model and some details about the type of experiment you -want to simulate. +You do not necessarily need the measured data to perform the +calculations, but you need a structural model and some details about the +type of experiment you want to simulate. -EasyDiffraction is designed as a flexible and extensible tool that supports -different **calculation engines** for diffraction pattern calculations. -Currently, we integrate CrysPy, CrysFML, and PDFfit2 libraries as calculation -engines. +EasyDiffraction is designed as a flexible and extensible tool that +supports different **calculation engines** for diffraction pattern +calculations. Currently, we integrate CrysPy, CrysFML, and PDFfit2 +libraries as calculation engines. ### CrysPy Calculator -[CrysPy](https://www.cryspy.fr) is a Python library originally developed for -analysing polarised neutron diffraction data. It is now evolving into a more -general purpose library and covers powders and single crystals, nuclear and -(commensurate) magnetic structures, unpolarised neutron and X-ray diffraction. +[CrysPy](https://www.cryspy.fr) is a Python library originally developed +for analysing polarised neutron diffraction data. It is now evolving +into a more general purpose library and covers powders and single +crystals, nuclear and (commensurate) magnetic structures, unpolarised +neutron and X-ray diffraction. ### CrysFML Calculator -[CrysFML](https://code.ill.fr/scientific-software/CrysFML2008) library is a -collection of Fortran modules for crystallographic computations. It is used in -the software package [FullProf](https://www.ill.eu/sites/fullprof/), and we are -currently working on its integration into EasyDiffraction. +[CrysFML](https://code.ill.fr/scientific-software/CrysFML2008) library +is a collection of Fortran modules for crystallographic computations. It +is used in the software package +[FullProf](https://www.ill.eu/sites/fullprof/), and we are currently +working on its integration into EasyDiffraction. ### PDFfit2 Calculator -[PDFfit2](https://github.com/diffpy/diffpy.pdffit2/) is a Python library for -calculating the pair distribution function (PDF) from crystallographic models. +[PDFfit2](https://github.com/diffpy/diffpy.pdffit2/) is a Python library +for calculating the pair distribution function (PDF) from +crystallographic models. ### Set Calculator -The calculator is automatically selected based on the experiment type (e.g., -`cryspy` for Bragg diffraction, `pdffit` for total scattering). To show the -supported calculation engines for a specific experiment: +The calculator is automatically selected based on the experiment type +(e.g., `cryspy` for Bragg diffraction, `pdffit` for total scattering). +To show the supported calculation engines for a specific experiment: ```python project.experiments['hrpt'].show_supported_calculator_types() @@ -77,9 +82,9 @@ project.experiments['hrpt'].calculator_type = 'cryspy' ## Minimization / Optimization -The process of refining model parameters involves iterating through multiple -steps until the calculated data sufficiently matches the experimental data. This -process is illustrated in the following diagram: +The process of refining model parameters involves iterating through +multiple steps until the calculated data sufficiently matches the +experimental data. This process is illustrated in the following diagram: ```mermaid flowchart LR @@ -95,31 +100,34 @@ flowchart LR d-- Threshold
reached -->e ``` -Like the calculation engines, EasyDiffraction is designed to utilize various -third-party libraries for model refinement and parameter optimization. These -libraries provide robust curve fitting and uncertainty estimation tools. +Like the calculation engines, EasyDiffraction is designed to utilize +various third-party libraries for model refinement and parameter +optimization. These libraries provide robust curve fitting and +uncertainty estimation tools. ### Lmfit Minimizer Most of the examples in this section will use the -[lmfit](https://lmfit.github.io/lmfit-py/) package, which provides a high-level -interface to non-linear optimisation and curve fitting problems for Python. It -is one of the tools that can be used to fit models to the experimental data. +[lmfit](https://lmfit.github.io/lmfit-py/) package, which provides a +high-level interface to non-linear optimisation and curve fitting +problems for Python. It is one of the tools that can be used to fit +models to the experimental data. ### Bumps Minimizer Another package that can be used for the same purpose is -[bumps](https://bumps.readthedocs.io/en/latest/). In addition to traditional -optimizers which search for the best minimum they can find in the search space, -bumps provides Bayesian uncertainty analysis which explores all viable minima -and finds confidence intervals on the parameters based on uncertainty in the -measured values. +[bumps](https://bumps.readthedocs.io/en/latest/). In addition to +traditional optimizers which search for the best minimum they can find +in the search space, bumps provides Bayesian uncertainty analysis which +explores all viable minima and finds confidence intervals on the +parameters based on uncertainty in the measured values. ### DFO-LS Minimizer -[DFO-LS](https://github.com/numericalalgorithmsgroup/dfols) (Derivative-Free -Optimizer for Least-Squares) is a Python library for solving nonlinear -least-squares minimization, without requiring derivatives of the objective. +[DFO-LS](https://github.com/numericalalgorithmsgroup/dfols) +(Derivative-Free Optimizer for Least-Squares) is a Python library for +solving nonlinear least-squares minimization, without requiring +derivatives of the objective. ### Set Minimizer @@ -148,9 +156,10 @@ project.analysis.current_minimizer = 'lmfit' ### Fit Mode -In EasyDiffraction, you can set the **fit mode** to control how the refinement -process is performed. The fit mode determines whether the refinement is -performed independently for each experiment or jointly across all experiments. +In EasyDiffraction, you can set the **fit mode** to control how the +refinement process is performed. The fit mode determines whether the +refinement is performed independently for each experiment or jointly +across all experiments. The supported fit modes are: @@ -173,14 +182,14 @@ print(project.analysis.fit_mode.mode.value) ### Perform Fit -Refining the structure and experiment parameters against measured data is -usually divided into several steps, where each step involves adding or removing -parameters to be refined, calculating the model data, and comparing it to the -experimental data as shown in the diagram above. +Refining the structure and experiment parameters against measured data +is usually divided into several steps, where each step involves adding +or removing parameters to be refined, calculating the model data, and +comparing it to the experimental data as shown in the diagram above. -To select the parameters to be refined, you can set the attribute `free` of the -parameters to `True`. This indicates that the parameter is free to be optimized -during the refinement process. +To select the parameters to be refined, you can set the attribute `free` +of the parameters to `True`. This indicates that the parameter is free +to be optimized during the refinement process. Here is an example of how to set parameters to be refined: @@ -195,15 +204,16 @@ project.experiments['hrpt'].background['10'].y.free = True project.experiments['hrpt'].background['165'].y.free = True ``` -After setting the parameters to be refined, you can perform the fit using the -`fit` method of the `analysis` object: +After setting the parameters to be refined, you can perform the fit +using the `fit` method of the `analysis` object: ```python project.analysis.fit() ``` -This method will iterate through the defined steps, adjusting the parameters -until the calculated data sufficiently matches the experimental data. +This method will iterate through the defined steps, adjusting the +parameters until the calculated data sufficiently matches the +experimental data. An example of the output after performing the fit is: @@ -233,9 +243,9 @@ Fit results 📈 Fitted parameters: ``` -Now, you can inspect the fitted parameters to see how they have changed during -the refinement process, select more parameters to be refined, and perform -additional fits as needed. +Now, you can inspect the fitted parameters to see how they have changed +during the refinement process, select more parameters to be refined, and +perform additional fits as needed. To plot the measured vs calculated data after the fit, you can use the `plot_meas_vs_calc` method of the `analysis` object: @@ -246,16 +256,16 @@ project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True) ## Constraints -In EasyDiffraction, you can define **constraints** on the model parameters to -ensure that they remain within a specific range or follow a certain relationship -during the refinement process. +In EasyDiffraction, you can define **constraints** on the model +parameters to ensure that they remain within a specific range or follow +a certain relationship during the refinement process. ### Setting Aliases -Before setting constraints, you need to set aliases for the parameters you want -to constrain. This can be done using the `add` method of the `aliases` object. -Aliases are used to reference parameters in a more readable way, making it -easier to manage constraints. +Before setting constraints, you need to set aliases for the parameters +you want to constrain. This can be done using the `add` method of the +`aliases` object. Aliases are used to reference parameters in a more +readable way, making it easier to manage constraints. An example of setting aliases for parameters in a structure: @@ -283,11 +293,11 @@ project.analysis.aliases.create( ### Setting Constraints -Now that you have set the aliases, you can define constraints using the `add` -method of the `constraints` object. Constraints are defined by specifying the -**left-hand side (lhs) alias** and the **right-hand side (rhs) expression**. The -rhs expression can be a simple alias or a more complex expression involving -other aliases. +Now that you have set the aliases, you can define constraints using the +`add` method of the `constraints` object. Constraints are defined by +specifying the **left-hand side (lhs) alias** and the **right-hand side +(rhs) expression**. The rhs expression can be a simple alias or a more +complex expression involving other aliases. An example of setting constraints for the aliases defined above: @@ -303,15 +313,16 @@ project.analysis.constraints.create( ) ``` -These constraints ensure that the `biso_Ba` parameter is equal to `biso_La`, and -the `occ_Ba` parameter is equal to `1 - occ_La`. This means that the occupancy -of the Ba atom will always be adjusted based on the occupancy of the La atom, -and the isotropic displacement parameter for Ba will be equal to that of La -during the refinement process. +These constraints ensure that the `biso_Ba` parameter is equal to +`biso_La`, and the `occ_Ba` parameter is equal to `1 - occ_La`. This +means that the occupancy of the Ba atom will always be adjusted based on +the occupancy of the La atom, and the isotropic displacement parameter +for Ba will be equal to that of La during the refinement process. ### Viewing Constraints -To view the defined constraints, you can use the `show_constraints` method: +To view the defined constraints, you can use the `show_constraints` +method: ```python project.analysis.show_constraints() @@ -361,8 +372,9 @@ Example output: ## Saving an Analysis -Saving the project, as described in the [Project](project.md) section, will also -save the analysis settings to the `analysis.cif` inside the project directory. +Saving the project, as described in the [Project](project.md) section, +will also save the analysis settings to the `analysis.cif` inside the +project directory.
diff --git a/docs/docs/user-guide/analysis-workflow/experiment.md b/docs/docs/user-guide/analysis-workflow/experiment.md index a62d9822..120653af 100644 --- a/docs/docs/user-guide/analysis-workflow/experiment.md +++ b/docs/docs/user-guide/analysis-workflow/experiment.md @@ -4,10 +4,10 @@ icon: material/microscope # :material-microscope: Experiment -An **Experiment** in EasyDiffraction includes the measured diffraction data -along with all relevant parameters that describe the experimental setup and -associated conditions. This can include information about the instrumental -resolution, peak shape, background, etc. +An **Experiment** in EasyDiffraction includes the measured diffraction +data along with all relevant parameters that describe the experimental +setup and associated conditions. This can include information about the +instrumental resolution, peak shape, background, etc. ## Defining an Experiment @@ -15,23 +15,26 @@ EasyDiffraction allows you to: - **Load an existing experiment** from a file (**CIF** format). Both the metadata and measured data are expected to be in CIF format. -- **Manually define** a new experiment by specifying its type, other necessary - experimental parameters, as well as load measured data. This is useful when - you want to create an experiment from scratch or when you have a measured data - file in a non-CIF format (e.g., `.xye`, `.xy`). - -Below, you will find instructions on how to define and manage experiments in -EasyDiffraction. It is assumed that you have already created a `project` object, -as described in the [Project](project.md) section as well as defined its -`structures`, as described in the [Structure](model.md) section. +- **Manually define** a new experiment by specifying its type, other + necessary experimental parameters, as well as load measured data. This + is useful when you want to create an experiment from scratch or when + you have a measured data file in a non-CIF format (e.g., `.xye`, + `.xy`). + +Below, you will find instructions on how to define and manage +experiments in EasyDiffraction. It is assumed that you have already +created a `project` object, as described in the [Project](project.md) +section as well as defined its `structures`, as described in the +[Structure](model.md) section. ### Adding from CIF -This is the most straightforward way to define an experiment in EasyDiffraction. -If you have a crystallographic information file (CIF) for your experiment, that -contains both the necessary information (metadata) about the experiment as well -as the measured data, you can add it to your `project.experiments` collection -using the `add_from_cif_path` method. In this case, the name of the experiment +This is the most straightforward way to define an experiment in +EasyDiffraction. If you have a crystallographic information file (CIF) +for your experiment, that contains both the necessary information +(metadata) about the experiment as well as the measured data, you can +add it to your `project.experiments` collection using the +`add_from_cif_path` method. In this case, the name of the experiment will be taken from CIF. ```python @@ -51,9 +54,9 @@ project.experiments.add_from_cif_str(cif_string) ``` Accessing the experiment after adding it will also be done through the -`experiments` object of the `project` instance. The name of the experiment will -be the same as the data block id in the CIF file. For example, if the CIF file -contains a data block with the id `hrpt`, +`experiments` object of the `project` instance. The name of the +experiment will be the same as the data block id in the CIF file. For +example, if the CIF file contains a data block with the id `hrpt`, @@ -77,20 +80,22 @@ project.experiments['hrpt'] ### Defining Manually -If you do not have a CIF file or prefer to define the experiment manually, you -can use the `add_from_data_path` method of the `experiments` object of the -`project` instance. In this case, you will need to specify the **name** of the -experiment, which will be used to reference it later, as well as **data_path** -to the measured data file (e.g., `.xye`, `.xy`). Supported formats are described -in the [Measured Data Category](#5-measured-data-category) section. +If you do not have a CIF file or prefer to define the experiment +manually, you can use the `add_from_data_path` method of the +`experiments` object of the `project` instance. In this case, you will +need to specify the **name** of the experiment, which will be used to +reference it later, as well as **data_path** to the measured data file +(e.g., `.xye`, `.xy`). Supported formats are described in the +[Measured Data Category](#5-measured-data-category) section. -Optionally, you can also specify the additional parameters that define the -**type of experiment** you want to create. If you do not specify any of these -parameters, the default values will be used, which are the first in the list of -supported options for each parameter: +Optionally, you can also specify the additional parameters that define +the **type of experiment** you want to create. If you do not specify any +of these parameters, the default values will be used, which are the +first in the list of supported options for each parameter: - **sample_form**: The form of the sample (powder, single crystal). -- **beam_mode**: The mode of the beam (constant wavelength, time-of-flight). +- **beam_mode**: The mode of the beam (constant wavelength, + time-of-flight). - **radiation_probe**: The type of radiation used (neutron, X-ray). - **scattering_type**: The type of scattering (bragg, total). @@ -100,8 +105,8 @@ supported options for each parameter: these parameters. If you need to change them, you must create a new experiment or redefine the existing one. -Here is an example of how to add an experiment with all relevant components -explicitly defined: +Here is an example of how to add an experiment with all relevant +components explicitly defined: ```python # Add an experiment with default parameters, based on the specified type. @@ -125,9 +130,9 @@ project.experiments.add_from_data_path( ) ``` -If you do not have measured data for fitting and only want to view the simulated -pattern, you can define an experiment without measured data using the `create` -method: +If you do not have measured data for fitting and only want to view the +simulated pattern, you can define an experiment without measured data +using the `create` method: ```python # Add an experiment without measured data @@ -159,23 +164,24 @@ project.experiments.add(experiment) ## Modifying Parameters -When an experiment is added, it is created with a set of default parameters that -you can modify to match your specific experimental setup. All parameters are -grouped into categories based on their function, making it easier to manage and -understand the different aspects of the experiment: - -1. **Instrument Category**: Defines the instrument configuration, including - wavelength, two-theta offset, and resolution parameters. -2. **Peak Category**: Specifies the peak profile type and its parameters, such - as broadening and asymmetry. -3. **Background Category**: Defines the background type and allows you to add - background points. -4. **Linked Phases Category**: Links the structure defined in the previous step - to the experiment, allowing you to specify the scale factor for the linked - phase. -5. **Measured Data Category**: Contains the measured data. The expected format - depends on the experiment type, but generally includes columns for 2θ angle - or TOF and intensity. +When an experiment is added, it is created with a set of default +parameters that you can modify to match your specific experimental +setup. All parameters are grouped into categories based on their +function, making it easier to manage and understand the different +aspects of the experiment: + +1. **Instrument Category**: Defines the instrument configuration, + including wavelength, two-theta offset, and resolution parameters. +2. **Peak Category**: Specifies the peak profile type and its + parameters, such as broadening and asymmetry. +3. **Background Category**: Defines the background type and allows you + to add background points. +4. **Linked Phases Category**: Links the structure defined in the + previous step to the experiment, allowing you to specify the scale + factor for the linked phase. +5. **Measured Data Category**: Contains the measured data. The expected + format depends on the experiment type, but generally includes columns + for 2θ angle or TOF and intensity. ### 1. Instrument Category { #instrument-category } @@ -230,10 +236,10 @@ project.experiments['hrpt'].linked_phases.create(id='lbco', scale=10.0) ### 6. Measured Data Category { #measured-data-category } -If you do not have a CIF file for your experiment, you can load measured data -from a file in a supported format. The measured data will be automatically -converted into CIF format and added to the experiment. The expected format -depends on the experiment type. +If you do not have a CIF file for your experiment, you can load measured +data from a file in a supported format. The measured data will be +automatically converted into CIF format and added to the experiment. The +expected format depends on the experiment type. #### Supported data file formats: @@ -245,8 +251,8 @@ depends on the experiment type. - [\_pd_meas.2theta_scan](../parameters/pd_meas.md) - [\_pd_meas.intensity_total](../parameters/pd_meas.md) -If no **standard deviations** are provided, they are automatically calculated as -the **square root** of measured intensities. +If no **standard deviations** are provided, they are automatically +calculated as the **square root** of measured intensities. Optional comments with `#` are possible in data file headers. @@ -602,5 +608,5 @@ loop_ --- -Now that the experiment has been defined, you can proceed to the next step: -[Analysis](analysis.md). +Now that the experiment has been defined, you can proceed to the next +step: [Analysis](analysis.md). diff --git a/docs/docs/user-guide/analysis-workflow/index.md b/docs/docs/user-guide/analysis-workflow/index.md index 51c1f623..84598210 100644 --- a/docs/docs/user-guide/analysis-workflow/index.md +++ b/docs/docs/user-guide/analysis-workflow/index.md @@ -16,19 +16,22 @@ flowchart LR d --> e ``` -- [:material-archive: Project](project.md) – Establish a **project** as a - container for structure and experiment parameters, measured and calculated - data, analysis settings and results. -- [:material-puzzle: Structure](model.md) – Load an existing **crystallographic - model** in CIF format or define a new one from scratch. -- [:material-microscope: Experiment](experiment.md) – Import **experimental - diffraction data** and configure **instrumental** and other relevant +- [:material-archive: Project](project.md) – Establish a **project** as + a container for structure and experiment parameters, measured and + calculated data, analysis settings and results. +- [:material-puzzle: Structure](model.md) – Load an existing + **crystallographic model** in CIF format or define a new one from + scratch. +- [:material-microscope: Experiment](experiment.md) – Import + **experimental diffraction data** and configure **instrumental** and + other relevant parameters. +- [:material-calculator: Analysis](analysis.md) – **Calculate the + diffraction pattern** and **optimize the structural model** by + refining its parameters to match experimental measurements. +- [:material-clipboard-text: Summary](summary.md) – Generate a + **report** summarizing the results of the analysis, including refined parameters. -- [:material-calculator: Analysis](analysis.md) – **Calculate the diffraction - pattern** and **optimize the structural model** by refining its parameters to - match experimental measurements. -- [:material-clipboard-text: Summary](summary.md) – Generate a **report** - summarizing the results of the analysis, including refined parameters. -Each step is described in detail in its respective section, guiding users -through the **entire diffraction data analysis workflow** in EasyDiffraction. +Each step is described in detail in its respective section, guiding +users through the **entire diffraction data analysis workflow** in +EasyDiffraction. diff --git a/docs/docs/user-guide/analysis-workflow/model.md b/docs/docs/user-guide/analysis-workflow/model.md index c437ea62..95e309cd 100644 --- a/docs/docs/user-guide/analysis-workflow/model.md +++ b/docs/docs/user-guide/analysis-workflow/model.md @@ -5,35 +5,38 @@ icon: material/puzzle # :material-puzzle: Structure The **Structure** in EasyDiffraction represents the **crystallographic -structure** used to calculate the diffraction pattern, which is then fitted to -the **experimentally measured data** to refine the structural parameters. +structure** used to calculate the diffraction pattern, which is then +fitted to the **experimentally measured data** to refine the structural +parameters. EasyDiffraction allows you to: - **Load an existing model** from a file (**CIF** format). -- **Manually define** a new structure by specifying crystallographic parameters. +- **Manually define** a new structure by specifying crystallographic + parameters. -Below, you will find instructions on how to define and manage crystallographic -models in EasyDiffraction. It is assumed that you have already created a -`project` object, as described in the [Project](project.md) section. +Below, you will find instructions on how to define and manage +crystallographic models in EasyDiffraction. It is assumed that you have +already created a `project` object, as described in the +[Project](project.md) section. ## Adding a Model from CIF -This is the most straightforward way to define a structure in EasyDiffraction. -If you have a crystallographic information file (CIF) for your structure, you -can add it to your project using the `add_from_cif_path` method of the -`project.structures` collection. In this case, the name of the model will be -taken from CIF. +This is the most straightforward way to define a structure in +EasyDiffraction. If you have a crystallographic information file (CIF) +for your structure, you can add it to your project using the +`add_from_cif_path` method of the `project.structures` collection. In +this case, the name of the model will be taken from CIF. ```python # Load a phase from a CIF file project.structures.add_from_cif_path('data/lbco.cif') ``` -Accessing the model after loading it will be done through the `structures` -collection of the `project` instance. The name of the model will be the same as -the data block id in the CIF file. For example, if the CIF file contains a data -block with the id `lbco`, +Accessing the model after loading it will be done through the +`structures` collection of the `project` instance. The name of the model +will be the same as the data block id in the CIF file. For example, if +the CIF file contains a data block with the id `lbco`, @@ -57,10 +60,10 @@ project.structures['lbco'] ## Defining a Model Manually -If you do not have a CIF file or prefer to define the model manually, you can -use the `create` method of the `structures` object of the `project` instance. In -this case, you will need to specify the name of the model, which will be used to -reference it later. +If you do not have a CIF file or prefer to define the model manually, +you can use the `create` method of the `structures` object of the +`project` instance. In this case, you will need to specify the name of +the model, which will be used to reference it later. ```python # Add a structure with default parameters @@ -68,15 +71,17 @@ reference it later. project.structures.create(name='nacl') ``` -The `add` method creates a new structure with default parameters. You can then -modify its parameters to match your specific crystallographic structure. All -parameters are grouped into the following categories, which makes it easier to -manage the model: +The `add` method creates a new structure with default parameters. You +can then modify its parameters to match your specific crystallographic +structure. All parameters are grouped into the following categories, +which makes it easier to manage the model: -1. **Space Group Category**: Defines the symmetry of the crystal structure. -2. **Cell Category**: Specifies the dimensions and angles of the unit cell. -3. **Atom Sites Category**: Describes the positions and properties of atoms - within the unit cell. +1. **Space Group Category**: Defines the symmetry of the crystal + structure. +2. **Cell Category**: Specifies the dimensions and angles of the unit + cell. +3. **Atom Sites Category**: Describes the positions and properties of + atoms within the unit cell. ### 1. Space Group Category { #space-group-category } @@ -177,10 +182,10 @@ Structure 🧩 'lbco' as cif ## Saving a Model -Saving the project, as described in the [Project](project.md) section, will also -save the model. Each model is saved as a separate CIF file in the `structures` -subdirectory of the project directory. The project file contains references to -these files. +Saving the project, as described in the [Project](project.md) section, +will also save the model. Each model is saved as a separate CIF file in +the `structures` subdirectory of the project directory. The project file +contains references to these files. Below is an example of the saved CIF file for the `lbco` model: @@ -223,5 +228,5 @@ O O 0 0.5 0.5 c 1 Biso 1.4041 --- -Now that the crystallographic model has been defined and added to the project, -you can proceed to the next step: [Experiment](experiment.md). +Now that the crystallographic model has been defined and added to the +project, you can proceed to the next step: [Experiment](experiment.md). diff --git a/docs/docs/user-guide/analysis-workflow/project.md b/docs/docs/user-guide/analysis-workflow/project.md index 45e1d9c5..ad41f852 100644 --- a/docs/docs/user-guide/analysis-workflow/project.md +++ b/docs/docs/user-guide/analysis-workflow/project.md @@ -4,25 +4,26 @@ icon: material/archive # :material-archive: Project -The **Project** serves as a container for all data and metadata associated with -a particular data analysis task. It acts as the top-level entity in -EasyDiffraction, ensuring structured organization and easy access to relevant -information. Each project can contain multiple **experimental datasets**, with -each dataset containing contribution from multiple **structures**. +The **Project** serves as a container for all data and metadata +associated with a particular data analysis task. It acts as the +top-level entity in EasyDiffraction, ensuring structured organization +and easy access to relevant information. Each project can contain +multiple **experimental datasets**, with each dataset containing +contribution from multiple **structures**. EasyDiffraction allows you to: - **Manually create** a new project by specifying its metadata. - **Load an existing project** from a file (**CIF** format). -Below are instructions on how to set up a project in EasyDiffraction. It is -assumed that you have already imported the `easydiffraction` package, as -described in the [First Steps](../first-steps.md) section. +Below are instructions on how to set up a project in EasyDiffraction. It +is assumed that you have already imported the `easydiffraction` package, +as described in the [First Steps](../first-steps.md) section. ## Creating a Project Manually -You can manually create a new project and specify its short **name**, **title** -and **description**. All these parameters are optional. +You can manually create a new project and specify its short **name**, +**title** and **description**. All these parameters are optional. ```py # Create a new project @@ -44,10 +45,11 @@ Saving the initial project requires specifying the directory path: project.save_as(dir_path='lbco_hrpt') ``` -If working in the interactive mode in a Jupyter notebook or similar environment, -you can also save the project after every significant change. This is useful for -keeping track of changes and ensuring that your work is not lost. If you already -saved the project with `save_as`, you can just call the `save`: +If working in the interactive mode in a Jupyter notebook or similar +environment, you can also save the project after every significant +change. This is useful for keeping track of changes and ensuring that +your work is not lost. If you already saved the project with `save_as`, +you can just call the `save`: ```python project.save() @@ -55,8 +57,9 @@ project.save() ## Loading a Project from CIF -If you have an existing project, you can load it directly from a CIF file. This -is useful for reusing previously defined projects or sharing them with others. +If you have an existing project, you can load it directly from a CIF +file. This is useful for reusing previously defined projects or sharing +them with others. ```python project.load('data/lbco_hrpt.cif') @@ -89,8 +92,8 @@ The example below illustrates a typical **project structure** for a ## Project Files -Below is a complete project example stored in the `La0.5Ba0.5CoO3` directory, -showing the contents of all files in the project. +Below is a complete project example stored in the `La0.5Ba0.5CoO3` +directory, showing the contents of all files in the project. !!! warning "Important" @@ -101,8 +104,8 @@ showing the contents of all files in the project. ### 1. project.cif -This file provides an overview of the project, including file names of the -**structures** and **experiments** associated with the project. +This file provides an overview of the project, including file names of +the **structures** and **experiments** associated with the project. @@ -127,9 +130,9 @@ hrpt.cif ### 2. structures / lbco.cif -This file contains crystallographic information associated with the structure -model, including **space group**, **unit cell parameters**, and **atomic -positions**. +This file contains crystallographic information associated with the +structure model, including **space group**, **unit cell parameters**, +and **atomic positions**. @@ -168,9 +171,9 @@ O O 0 0.5 0.5 c 1 Biso 1.4041 ### 3. experiments / hrpt.cif -This file contains the **experiment type**, **instrumental parameters**, **peak -parameters**, **associated phases**, **background parameters** and **measured -diffraction data**. +This file contains the **experiment type**, **instrumental parameters**, +**peak parameters**, **associated phases**, **background parameters** +and **measured diffraction data**. @@ -236,8 +239,8 @@ loop_ ### 4. analysis.cif -This file contains settings used for data analysis, including the choice of -**calculation** and **fitting** engines, as well as user defined +This file contains settings used for data analysis, including the choice +of **calculation** and **fitting** engines, as well as user defined **constraints**. diff --git a/docs/docs/user-guide/analysis-workflow/summary.md b/docs/docs/user-guide/analysis-workflow/summary.md index 4790f857..34a89b61 100644 --- a/docs/docs/user-guide/analysis-workflow/summary.md +++ b/docs/docs/user-guide/analysis-workflow/summary.md @@ -5,20 +5,20 @@ icon: material/clipboard-text # :material-clipboard-text: Summary The **Summary** section represents the final step in the data processing -workflow. It involves generating a **summary report** that consolidates the -results of the diffraction data analysis, providing a comprehensive overview of -the model refinement process and its outcomes. +workflow. It involves generating a **summary report** that consolidates +the results of the diffraction data analysis, providing a comprehensive +overview of the model refinement process and its outcomes. ## Contents of the Summary Report The summary report includes key details such as: -- Final refined model parameters – Optimized crystallographic and instrumental - parameters. -- Goodness-of-fit indicators – Metrics such as R-factors, chi-square (χ²), and - residuals. -- Graphical representation – Visualization of experimental vs. calculated - diffraction patterns. +- Final refined model parameters – Optimized crystallographic and + instrumental parameters. +- Goodness-of-fit indicators – Metrics such as R-factors, chi-square + (χ²), and residuals. +- Graphical representation – Visualization of experimental vs. + calculated diffraction patterns. ## Viewing the Summary Report @@ -36,8 +36,9 @@ including model parameters, fit statistics, and data visualizations. ## Saving a Summary -Saving the project, as described in the [Project](project.md) section, will also -save the summary report to the `summary.cif` inside the project directory. +Saving the project, as described in the [Project](project.md) section, +will also save the summary report to the `summary.cif` inside the +project directory. ![](../assets/images/user-guide/data-acquisition_instrument.png){ width="450", loading=lazy } @@ -42,10 +43,10 @@ Credits: DOI 10.1126/science.1238932 ## Data Reduction -Data reduction involves processing the raw data to remove background noise, -correct for instrumental effects, and convert the data into a more usable -format. The goal is to produce a clean and reliable dataset suitable for -analysis. +Data reduction involves processing the raw data to remove background +noise, correct for instrumental effects, and convert the data into a +more usable format. The goal is to produce a clean and reliable dataset +suitable for analysis. ![](../assets/images/user-guide/data-reduction_1d-pattern.png){ width="450", loading=lazy } @@ -57,28 +58,32 @@ Credits: DOI 10.1126/science.1238932 ## Data Analysis -Data analysis uses the reduced data to extract meaningful information about the -crystallographic structure. This may include determining the crystal or magnetic -structure, identifying phases, performing quantitative analysis, etc. - -Analysis often involves comparing experimental data with data calculated from a -crystallographic model to validate and interpret the results. For powder -diffraction, techniques such as Rietveld or Le Bail refinement may be used. - -In EasyDiffraction, we focus on this **model-dependent analysis**. A model is -built using prior knowledge of the system, and its parameters are optimized to -achieve the best agreement between experimental and calculated diffraction data. - -By "model", we usually refer to a **crystallographic model** of the sample. This -includes unit cell parameters, space group, atomic positions, thermal -parameters, and more. However, the term "model" also encompasses experimental -aspects such as instrumental resolution, background, peak shape, etc. Therefore, -EasyDiffraction separates the model into two parts: the **structure** and the -**experiment**. - -The aim of data analysis is to refine the structural parameters of the sample by -minimizing the difference (or **residual**) between the experimental and -calculated data — and this is exactly where EasyDiffraction comes into play. +Data analysis uses the reduced data to extract meaningful information +about the crystallographic structure. This may include determining the +crystal or magnetic structure, identifying phases, performing +quantitative analysis, etc. + +Analysis often involves comparing experimental data with data calculated +from a crystallographic model to validate and interpret the results. For +powder diffraction, techniques such as Rietveld or Le Bail refinement +may be used. + +In EasyDiffraction, we focus on this **model-dependent analysis**. A +model is built using prior knowledge of the system, and its parameters +are optimized to achieve the best agreement between experimental and +calculated diffraction data. + +By "model", we usually refer to a **crystallographic model** of the +sample. This includes unit cell parameters, space group, atomic +positions, thermal parameters, and more. However, the term "model" also +encompasses experimental aspects such as instrumental resolution, +background, peak shape, etc. Therefore, EasyDiffraction separates the +model into two parts: the **structure** and the **experiment**. + +The aim of data analysis is to refine the structural parameters of the +sample by minimizing the difference (or **residual**) between the +experimental and calculated data — and this is exactly where +EasyDiffraction comes into play. ![](../assets/images/user-guide/data-analysis_refinement.png){ width="450", loading=lazy } diff --git a/docs/docs/user-guide/data-format.md b/docs/docs/user-guide/data-format.md index 63e2e52e..da7c92b1 100644 --- a/docs/docs/user-guide/data-format.md +++ b/docs/docs/user-guide/data-format.md @@ -1,46 +1,48 @@ # Data Format -Before starting the data analysis workflow, it is important to define the **data -formats** used in EasyDiffraction. +Before starting the data analysis workflow, it is important to define +the **data formats** used in EasyDiffraction. ## Crystallographic Information File -Each software package typically uses its own **data format** and **parameter -names** for storing and sharing data. In EasyDiffraction, we use the -**Crystallographic Information File (CIF)** format, which is widely used in -crystallography and materials science. It provides both a human-readable syntax -and a set of dictionaries that define the meaning of each parameter. +Each software package typically uses its own **data format** and +**parameter names** for storing and sharing data. In EasyDiffraction, we +use the **Crystallographic Information File (CIF)** format, which is +widely used in crystallography and materials science. It provides both a +human-readable syntax and a set of dictionaries that define the meaning +of each parameter. These dictionaries are maintained by the [International Union of Crystallography (IUCr)](https://www.iucr.org). The base dictionary, **coreCIF**, contains the most common parameters in -crystallography. The **pdCIF** dictionary covers parameters specific to powder -diffraction, **magCIF** is used for magnetic structure analysis. +crystallography. The **pdCIF** dictionary covers parameters specific to +powder diffraction, **magCIF** is used for magnetic structure analysis. -As most parameters needed for diffraction data analysis are already covered by -IUCr dictionaries, EasyDiffraction uses the strict **CIF format** and follows -these dictionaries as closely as possible — for both input and output — -throughout the workflow described in the +As most parameters needed for diffraction data analysis are already +covered by IUCr dictionaries, EasyDiffraction uses the strict **CIF +format** and follows these dictionaries as closely as possible — for +both input and output — throughout the workflow described in the [Analysis Workflow](analysis-workflow/index.md) section. The key advantage of CIF is the standardized naming of parameters and -categories, which promotes interoperability and familiarity among researchers. +categories, which promotes interoperability and familiarity among +researchers. If a required parameter is not defined in the standard dictionaries, EasyDiffraction introduces **custom CIF keywords**, documented in the -[Parameters](parameters.md) section under the **CIF name for serialization** -columns. +[Parameters](parameters.md) section under the **CIF name for +serialization** columns. ## Format Comparison -Below, we compare **CIF** with another common data format in programming: -**JSON**. +Below, we compare **CIF** with another common data format in +programming: **JSON**. ### Scientific Journals Let's assume the following structural data for La₀.₅Ba₀.₅CoO₃ (LBCO), as -reported in a scientific publication. These parameters are to be refined during -diffraction data analysis: +reported in a scientific publication. These parameters are to be refined +during diffraction data analysis: Table 1. Crystallographic data. Space group: _Pm3̅m_. @@ -53,8 +55,8 @@ Table 1. Crystallographic data. Space group: _Pm3̅m_. | beta | 90.0 | | gamma | 90.0 | -Table 2. Atomic coordinates (_x_, _y_, _z_), occupancies (occ) and isotropic -displacement parameters (_Biso_) +Table 2. Atomic coordinates (_x_, _y_, _z_), occupancies (occ) and +isotropic displacement parameters (_Biso_) | Label | Type | x | y | z | occ | Biso | | ----- | ---- | --- | --- | --- | --- | ------ | @@ -102,17 +104,17 @@ O O 0 0.5 0.5 c 1 Biso 1.4041 -Here, unit cell parameters are grouped under the `_cell` category, and atomic -positions under the `_atom_site` category. The `loop_` keyword indicates that -multiple rows follow for the listed parameters. Each atom is identified using -`_atom_site.label`. +Here, unit cell parameters are grouped under the `_cell` category, and +atomic positions under the `_atom_site` category. The `loop_` keyword +indicates that multiple rows follow for the listed parameters. Each atom +is identified using `_atom_site.label`. ### JSON -Representing the same data in **JSON** results in a format that is more verbose -and less human-readable, especially for large datasets. JSON is ideal for -structured data in programming environments, whereas CIF is better suited for -human-readable crystallographic data. +Representing the same data in **JSON** results in a format that is more +verbose and less human-readable, especially for large datasets. JSON is +ideal for structured data in programming environments, whereas CIF is +better suited for human-readable crystallographic data. ```json { @@ -173,11 +175,11 @@ human-readable crystallographic data. ## Experiment Definition -The previous example described the **structure** (crystallographic model), but -how is the **experiment** itself represented? +The previous example described the **structure** (crystallographic +model), but how is the **experiment** itself represented? -The experiment is also saved as a CIF file. For example, background intensity in -a powder diffraction experiment might be represented as: +The experiment is also saved as a CIF file. For example, background +intensity in a powder diffraction experiment might be represented as: @@ -197,13 +199,13 @@ loop_ -More details on how to define the experiment in CIF format are provided in the -[Experiment](analysis-workflow/experiment.md) section. +More details on how to define the experiment in CIF format are provided +in the [Experiment](analysis-workflow/experiment.md) section. ## Other Input/Output Blocks -EasyDiffraction uses CIF consistently throughout its workflow, including in the -following blocks: +EasyDiffraction uses CIF consistently throughout its workflow, including +in the following blocks: - **project**: contains the project information - **structure**: defines the structure @@ -217,11 +219,13 @@ Example CIF files for each block are provided in the ## Other Data Formats -While CIF is the primary format in EasyDiffraction, we also support other -formats for importing measured data. These include plain text files with -multiple columns. The meaning of the columns depends on the experiment type. +While CIF is the primary format in EasyDiffraction, we also support +other formats for importing measured data. These include plain text +files with multiple columns. The meaning of the columns depends on the +experiment type. -For example, in a standard constant-wavelength powder diffraction experiment: +For example, in a standard constant-wavelength powder diffraction +experiment: - Column 1: 2θ angle - Column 2: intensity diff --git a/docs/docs/user-guide/first-steps.md b/docs/docs/user-guide/first-steps.md index acb50bec..2de8fb5c 100644 --- a/docs/docs/user-guide/first-steps.md +++ b/docs/docs/user-guide/first-steps.md @@ -1,31 +1,31 @@ # First Steps -This section introduces the basic usage of the EasyDiffraction Python API. -You'll learn how to import the package, use core classes and utility functions, -and access built-in helper methods to streamline diffraction data analysis -workflows. +This section introduces the basic usage of the EasyDiffraction Python +API. You'll learn how to import the package, use core classes and +utility functions, and access built-in helper methods to streamline +diffraction data analysis workflows. ## Importing EasyDiffraction ### Importing the entire package -To start using EasyDiffraction, first import the package in your Python script -or Jupyter Notebook. This can be done with the following command: +To start using EasyDiffraction, first import the package in your Python +script or Jupyter Notebook. This can be done with the following command: ```python import easydiffraction ``` -Alternatively, you can import it with an alias to avoid naming conflicts and for -convenience: +Alternatively, you can import it with an alias to avoid naming conflicts +and for convenience: ```python import easydiffraction as ed ``` -The latter syntax allows you to access all the modules and classes within the -package using the `ed` prefix. For example, you can create a project instance -like this: +The latter syntax allows you to access all the modules and classes +within the package using the `ed` prefix. For example, you can create a +project instance like this: ```python project = ed.Project() @@ -36,9 +36,9 @@ A complete tutorial using the `import` syntax can be found ### Importing specific parts -Alternatively, you can import specific classes or methods from the package. For -example, you can import the `Project`, `Structure`, `Experiment` classes and -`download_from_repository` method like this: +Alternatively, you can import specific classes or methods from the +package. For example, you can import the `Project`, `Structure`, +`Experiment` classes and `download_from_repository` method like this: ```python from easydiffraction import Project @@ -47,10 +47,10 @@ from easydiffraction import Experiment from easydiffraction import download_from_repository ``` -This enables you to use these classes and methods directly without the package -prefix. This is especially useful when you're using only a few components and -want to keep your code clean and concise. In this case, you can create a project -instance like this: +This enables you to use these classes and methods directly without the +package prefix. This is especially useful when you're using only a few +components and want to keep your code clean and concise. In this case, +you can create a project instance like this: ```python project = Project() @@ -61,10 +61,11 @@ A complete tutorial using the `from` syntax can be found ## Utility functions -EasyDiffraction also provides several utility functions that can simplify your -workflow. One of them is the `download_from_repository` function, which allows -you to download data files from our remote repository, making it easy to access -and use them while experimenting with EasyDiffraction. +EasyDiffraction also provides several utility functions that can +simplify your workflow. One of them is the `download_from_repository` +function, which allows you to download data files from our remote +repository, making it easy to access and use them while experimenting +with EasyDiffraction. For example, you can download a data file like this: @@ -78,29 +79,31 @@ ed.download_from_repository( ) ``` -This command will download the `hrpt_lbco.xye` file from the `docs` branch of -the EasyDiffraction repository and save it in the `data` directory of your -current working directory. This is particularly useful for quickly accessing -example datasets without having to manually download them. +This command will download the `hrpt_lbco.xye` file from the `docs` +branch of the EasyDiffraction repository and save it in the `data` +directory of your current working directory. This is particularly useful +for quickly accessing example datasets without having to manually +download them. ## Help methods -EasyDiffraction provides several helper methods to display supported engines for -calculation, minimization, and plotting. These methods can be called on the -`Project` instance to display the available options for different categories. +EasyDiffraction provides several helper methods to display supported +engines for calculation, minimization, and plotting. These methods can +be called on the `Project` instance to display the available options for +different categories. ### Supported calculators -The calculator is automatically selected based on the experiment type. You can -use the `show_supported_calculator_types()` method on an experiment to see which -calculation engines are compatible: +The calculator is automatically selected based on the experiment type. +You can use the `show_supported_calculator_types()` method on an +experiment to see which calculation engines are compatible: ```python project.experiments['hrpt'].show_supported_calculator_types() ``` -This will display a list of supported calculators along with their descriptions, -allowing you to choose the one that best fits your needs. +This will display a list of supported calculators along with their +descriptions, allowing you to choose the one that best fits your needs. An example of the output for a Bragg diffraction experiment: @@ -119,24 +122,25 @@ project.show_available_minimizers() ### Available parameters -EasyDiffraction provides several methods for showing the available parameters -grouped in different categories. For example, you can use: +EasyDiffraction provides several methods for showing the available +parameters grouped in different categories. For example, you can use: -- `project.analysis.show_all_params()` – to display all available parameters for - the analysis step. -- `project.analysis.show_fittable_params()` – to display only the parameters - that can be fitted during the analysis. -- `project.analysis.show_free_params()` – to display the parameters that are - currently free to be adjusted during the fitting process. +- `project.analysis.show_all_params()` – to display all available + parameters for the analysis step. +- `project.analysis.show_fittable_params()` – to display only the + parameters that can be fitted during the analysis. +- `project.analysis.show_free_params()` – to display the parameters that + are currently free to be adjusted during the fitting process. -Finally, you can use the `project.analysis.how_to_access_parameters()` method to -get a brief overview of how to access and modify parameters in the analysis -step, along with their unique identifiers in the CIF format. This can be -particularly useful for users who are new to the EasyDiffraction API or those -who want to quickly understand how to work with parameters in their projects. +Finally, you can use the `project.analysis.how_to_access_parameters()` +method to get a brief overview of how to access and modify parameters in +the analysis step, along with their unique identifiers in the CIF +format. This can be particularly useful for users who are new to the +EasyDiffraction API or those who want to quickly understand how to work +with parameters in their projects. -An example of the output for the `project.analysis.how_to_access_parameters()` -method is: +An example of the output for the +`project.analysis.how_to_access_parameters()` method is: | | Code variable | Unique ID for CIF | | --- | --------------------------------------------------- | -------------------------------- | @@ -151,8 +155,9 @@ method is: ### Supported plotters -To see the available plotters, you can use the `show_available_plotters()` -method on the `plotter` attribute of the `Project` instance: +To see the available plotters, you can use the +`show_available_plotters()` method on the `plotter` attribute of the +`Project` instance: ```python project.plotter.show_supported_engines() @@ -167,16 +172,19 @@ An example of the output is: ## Data analysis workflow -Once the EasyDiffraction package is imported, you can proceed with the **data -analysis**. This step can be split into several sub-steps, such as creating a -project, defining structures, adding experimental data, etc. - -EasyDiffraction provides a **Python API** that allows you to perform these steps -programmatically in a certain linear order. This is especially useful for users -who prefer to work in a script or Jupyter Notebook environment. The API is -designed to be intuitive and easy to use, allowing you to focus on the analysis -rather than low-level implementation details. - -Because this workflow is an important part of the EasyDiffraction package, it is -described in detail in the separate -[Analysis Workflow](analysis-workflow/index.md) section of the documentation. +Once the EasyDiffraction package is imported, you can proceed with the +**data analysis**. This step can be split into several sub-steps, such +as creating a project, defining structures, adding experimental data, +etc. + +EasyDiffraction provides a **Python API** that allows you to perform +these steps programmatically in a certain linear order. This is +especially useful for users who prefer to work in a script or Jupyter +Notebook environment. The API is designed to be intuitive and easy to +use, allowing you to focus on the analysis rather than low-level +implementation details. + +Because this workflow is an important part of the EasyDiffraction +package, it is described in detail in the separate +[Analysis Workflow](analysis-workflow/index.md) section of the +documentation. diff --git a/docs/docs/user-guide/glossary.md b/docs/docs/user-guide/glossary.md index 827cfdf5..75f0300c 100644 --- a/docs/docs/user-guide/glossary.md +++ b/docs/docs/user-guide/glossary.md @@ -1,25 +1,29 @@ # Glossary -Before guiding you through the use of EasyDiffraction, we define some common -terms and abbreviations used throughout the documentation and tutorials. +Before guiding you through the use of EasyDiffraction, we define some +common terms and abbreviations used throughout the documentation and +tutorials. ## Dictionary Type Labels -The following labels are used to identify different types of CIF dictionaries: +The following labels are used to identify different types of CIF +dictionaries: - [coreCIF][1]{:.label-cif} – Core CIF dictionary by the [IUCr](https://www.iucr.org). - [pdCIF][2]{:.label-cif} – Powder CIF dictionary by the [IUCr](https://www.iucr.org). -- [easydiffractionCIF][0]{:.label-cif} – Custom CIF dictionary developed for - EasyDiffraction. +- [easydiffractionCIF][0]{:.label-cif} – Custom CIF dictionary developed + for EasyDiffraction. -For more information about CIF, see the [Data Format](data-format.md) section. +For more information about CIF, see the [Data Format](data-format.md) +section. ## Experiment Type Labels -EasyDiffraction supports a variety of experiment types, each with its own set of -parameters. The following labels identify the supported experiment types: +EasyDiffraction supports a variety of experiment types, each with its +own set of parameters. The following labels identify the supported +experiment types: ### Neutron Diffraction @@ -27,8 +31,8 @@ parameters. The following labels identify the supported experiment types: constant wavelength. - [pd-neut-tof][0]{:.label-experiment} – Powder neutron diffraction with time-of-flight. -- [sc-neut-cwl][0]{:.label-experiment} – Single-crystal neutron diffraction with - constant wavelength. +- [sc-neut-cwl][0]{:.label-experiment} – Single-crystal neutron + diffraction with constant wavelength. ### X-ray Diffraction diff --git a/docs/docs/user-guide/index.md b/docs/docs/user-guide/index.md index a6671f3d..2ddc28b9 100644 --- a/docs/docs/user-guide/index.md +++ b/docs/docs/user-guide/index.md @@ -4,20 +4,21 @@ icon: material/book-open-variant # :material-book-open-variant: User Guide -This section provides an overview of the **core concepts**, **key parameters** -and **workflow steps** required for using EasyDiffraction effectively. +This section provides an overview of the **core concepts**, **key +parameters** and **workflow steps** required for using EasyDiffraction +effectively. Here is a brief overview of the User Guide sections: -- [Glossary](glossary.md) – Defines common terms and labels used throughout the - documentation. -- [Concept](concept.md) – Introduces the overall idea behind diffraction data - processing and where EasyDiffraction fits. -- [Data Format](data-format.md) – Explains the Crystallographic Information File - (CIF) and how it's used in EasyDiffraction. -- [Parameters](parameters.md) – Describes how parameters are structured, named, - and accessed within the EasyDiffraction library. -- [First Steps](first-steps.md) – Shows how to begin using EasyDiffraction in - Python or Jupyter notebooks. +- [Glossary](glossary.md) – Defines common terms and labels used + throughout the documentation. +- [Concept](concept.md) – Introduces the overall idea behind diffraction + data processing and where EasyDiffraction fits. +- [Data Format](data-format.md) – Explains the Crystallographic + Information File (CIF) and how it's used in EasyDiffraction. +- [Parameters](parameters.md) – Describes how parameters are structured, + named, and accessed within the EasyDiffraction library. +- [First Steps](first-steps.md) – Shows how to begin using + EasyDiffraction in Python or Jupyter notebooks. - [Analysis Workflow](analysis-workflow/index.md) – Breaks down the data analysis pipeline into practical, sequential steps. diff --git a/docs/docs/user-guide/parameters.md b/docs/docs/user-guide/parameters.md index 794f65aa..622bf6f1 100644 --- a/docs/docs/user-guide/parameters.md +++ b/docs/docs/user-guide/parameters.md @@ -1,52 +1,58 @@ # Parameters -The data analysis process, introduced in the [Concept](concept.md) section, -assumes that you mainly work with different parameters. The parameters are used -to describe the structure and the experiment and are required to set up the -analysis. +The data analysis process, introduced in the [Concept](concept.md) +section, assumes that you mainly work with different parameters. The +parameters are used to describe the structure and the experiment and are +required to set up the analysis. -Each parameter in EasyDiffraction has a specific name used for code reference, -and it belongs to a specific category. +Each parameter in EasyDiffraction has a specific name used for code +reference, and it belongs to a specific category. - In many cases, the EasyDiffraction name is the same as the CIF name. -- In some cases, the EasyDiffraction name is a slightly modified version of the - CIF name to comply with Python naming conventions. For example, `name_H-M_alt` - becomes `name_h_m`, replacing hyphens with underscores and using lowercase - letters. -- In rare cases, the EasyDiffraction name is a bit shorter, like `b_iso` instead - of CIF `B_iso_or_equiv`, to make the code a bit more user-friendly. -- When there is no defined CIF name for a parameter, EasyDiffraction introduces - its own name, which is used in the code as well as an equivalent CIF name to - be placed in the custom CIF dictionary `easydiffractionCIF`. - -EasyDiffraction names are used in code, while CIF names are used to store and -retrieve the full state of a data analysis project in CIF format. You can find -more about the project in the [Project](analysis-workflow/project.md) section. +- In some cases, the EasyDiffraction name is a slightly modified version + of the CIF name to comply with Python naming conventions. For example, + `name_H-M_alt` becomes `name_h_m`, replacing hyphens with underscores + and using lowercase letters. +- In rare cases, the EasyDiffraction name is a bit shorter, like `b_iso` + instead of CIF `B_iso_or_equiv`, to make the code a bit more + user-friendly. +- When there is no defined CIF name for a parameter, EasyDiffraction + introduces its own name, which is used in the code as well as an + equivalent CIF name to be placed in the custom CIF dictionary + `easydiffractionCIF`. + +EasyDiffraction names are used in code, while CIF names are used to +store and retrieve the full state of a data analysis project in CIF +format. You can find more about the project in the +[Project](analysis-workflow/project.md) section. ## Parameter Attributes -Parameters in EasyDiffraction are more than just variables. They are objects -that, in addition to the name and value, also include attributes such as the -description, unit, uncertainty, minimum and maximum values, etc. All these -attributes are described in the [API Reference](../api-reference/index.md) -section. Examples of how to use these parameters in code are provided in the +Parameters in EasyDiffraction are more than just variables. They are +objects that, in addition to the name and value, also include attributes +such as the description, unit, uncertainty, minimum and maximum values, +etc. All these attributes are described in the +[API Reference](../api-reference/index.md) section. Examples of how to +use these parameters in code are provided in the [Analysis Workflow](analysis-workflow/index.md) and [Tutorials](../tutorials/index.md) sections. -The most important attribute, besides `name` and `value`, is `free`, which is -used to define whether the parameter is free or fixed for optimization during -the fitting process. The `free` attribute is set to `False` by default, which -means the parameter is fixed. To optimize a parameter, set `free` to `True`. +The most important attribute, besides `name` and `value`, is `free`, +which is used to define whether the parameter is free or fixed for +optimization during the fitting process. The `free` attribute is set to +`False` by default, which means the parameter is fixed. To optimize a +parameter, set `free` to `True`. -Although parameters are central, EasyDiffraction hides their creation and -attribute handling from the user. The user only accesses the required parameters -through the top-level objects, such as `project`, `structures`, `experiments`, -etc. The parameters are created and initialized automatically when a new project -is created or an existing one is loaded. +Although parameters are central, EasyDiffraction hides their creation +and attribute handling from the user. The user only accesses the +required parameters through the top-level objects, such as `project`, +`structures`, `experiments`, etc. The parameters are created and +initialized automatically when a new project is created or an existing +one is loaded. In the following sections, you can see a list of the parameters used in -EasyDiffraction. Use the tabs to switch between how to access a parameter in -code and its CIF name for serialization. +EasyDiffraction. Use the tabs to switch between how to access a +parameter in code and its CIF name for serialization. !!! warning "Important" @@ -59,23 +65,27 @@ code and its CIF name for serialization. project.structures['nacl'].space_group.name_h_m ``` -In the example above, `space_group` is a structure category, and `name_h_m` is -the parameter. For simplicity, only the last part (`category.parameter`) of the -full access name will be shown in the tables below. +In the example above, `space_group` is a structure category, and +`name_h_m` is the parameter. For simplicity, only the last part +(`category.parameter`) of the full access name will be shown in the +tables below. -In addition, the CIF names are also provided for each parameter, which are used -to serialize the parameters in the CIF format. +In addition, the CIF names are also provided for each parameter, which +are used to serialize the parameters in the CIF format. -Tags defining the corresponding experiment type are also given before the table. +Tags defining the corresponding experiment type are also given before +the table. ## Structure parameters -Below is a list of parameters used to describe the structure in EasyDiffraction. +Below is a list of parameters used to describe the structure in +EasyDiffraction. ### Crystall structure parameters -[pd-neut-cwl][3]{:.label-experiment} [pd-neut-tof][3]{:.label-experiment} -[pd-xray][3]{:.label-experiment} [sc-neut-cwl][3]{:.label-experiment} +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} === "How to access in the code" @@ -130,8 +140,9 @@ EasyDiffraction. ### Common parameters -[pd-neut-cwl][3]{:.label-experiment} [pd-neut-tof][3]{:.label-experiment} -[pd-xray][3]{:.label-experiment} [sc-neut-cwl][3]{:.label-experiment} +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} === "How to access in the code" @@ -153,8 +164,8 @@ EasyDiffraction. ### Standard powder diffraction -[pd-neut-cwl][3]{:.label-experiment} [pd-neut-tof][3]{:.label-experiment} -[pd-xray][3]{:.label-experiment} +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} === "How to access in the code" @@ -240,7 +251,8 @@ EasyDiffraction. ### Total scattering -[pd-neut-total][3]{:.label-experiment} [pd-xray-total][3]{:.label-experiment} +[pd-neut-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} === "How to access in the code" diff --git a/docs/docs/user-guide/parameters/_diffrn_radiation.md b/docs/docs/user-guide/parameters/_diffrn_radiation.md index e3c0a7e9..6e2f0a06 100644 --- a/docs/docs/user-guide/parameters/_diffrn_radiation.md +++ b/docs/docs/user-guide/parameters/_diffrn_radiation.md @@ -4,13 +4,13 @@ Data items in this category describe the radiation used in measuring the diffraction intensities. Please see the -[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) for -further details. +[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) +for further details. ## [\_diffrn_radiation.probe](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) -The nature of the radiation used (i.e. the name of the subatomic particle or the -region of the electromagnetic spectrum). +The nature of the radiation used (i.e. the name of the subatomic +particle or the region of the electromagnetic spectrum). Supported values: `neutron` and `x-ray` diff --git a/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md b/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md index 4d205788..3b750b22 100644 --- a/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md +++ b/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md @@ -4,8 +4,8 @@ Data items in this category describe the wavelength of radiation used in diffraction measurements. Please see the -[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) for -further details. +[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) +for further details. ## [\_diffrn_radiation_wavelength.wavelength](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) diff --git a/docs/docs/user-guide/parameters/_pd_calib.md b/docs/docs/user-guide/parameters/_pd_calib.md index 2b96a4c8..407c5f73 100644 --- a/docs/docs/user-guide/parameters/_pd_calib.md +++ b/docs/docs/user-guide/parameters/_pd_calib.md @@ -2,8 +2,8 @@ # \_pd_calib -This section defines the parameters used for the calibration of the instrument, -similar to this +This section defines the parameters used for the calibration of the +instrument, similar to this [IUCr section](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd). ## [\_pd_calib.2theta_offset](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) diff --git a/docs/docs/user-guide/parameters/atom_site.md b/docs/docs/user-guide/parameters/atom_site.md index 44325b8a..7acdb650 100644 --- a/docs/docs/user-guide/parameters/atom_site.md +++ b/docs/docs/user-guide/parameters/atom_site.md @@ -2,16 +2,16 @@ # \_atom_site -Data items in this category record details about the atom sites in a crystal -structure, such as the positional coordinates and atomic displacement -parameters. Please see the +Data items in this category record details about the atom sites in a +crystal structure, such as the positional coordinates and atomic +displacement parameters. Please see the [IUCr page](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/CATOM_SITE.html) for further details. ## [\_atom_site.label](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.label.html) -This is a unique identifier for a particular site in the asymmetric unit of the -crystal unit cell. +This is a unique identifier for a particular site in the asymmetric unit +of the crystal unit cell. ## [\_atom_site.type_symbol](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.type_symbol.html) @@ -19,7 +19,8 @@ A code to identify the atom specie(s) occupying this site. ## \_atom_site.fract -Atom-site coordinates as fractions of the [\_cell_length](cell.md) values. +Atom-site coordinates as fractions of the [\_cell_length](cell.md) +values. - [\_atom_site.fract_x](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.fract_x.html) - [\_atom_site.fract_y](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.fract_y.html) @@ -31,8 +32,8 @@ The fraction of the atom type present at this site. ## [\_atom_site.ADP_type](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.adp_type.html) -Code for type of atomic displacement parameters used for the site. Currently -only `Biso` (isotropic B) is supported. +Code for type of atomic displacement parameters used for the site. +Currently only `Biso` (isotropic B) is supported. ## [\_atom_site.B_iso_or_equiv](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.B_iso_or_equiv.html) @@ -43,17 +44,17 @@ displacement parameter, in angstroms squared. `optional parameter` -The number of different sites that are generated by the application of the -space-group symmetry to the coordinates given for this site. It is equal to the -multiplicity given for this Wyckoff site in International Tables for -Crystallography Vol. A (2002). +The number of different sites that are generated by the application of +the space-group symmetry to the coordinates given for this site. It is +equal to the multiplicity given for this Wyckoff site in International +Tables for Crystallography Vol. A (2002). ## [\_atom_site.Wyckoff_symbol](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.Wyckoff_symbol.html) `optional parameter` -The Wyckoff symbol (letter) as listed in the space-group tables of International -Tables for Crystallography Vol. A. +The Wyckoff symbol (letter) as listed in the space-group tables of +International Tables for Crystallography Vol. A. [0]: # diff --git a/docs/docs/user-guide/parameters/background.md b/docs/docs/user-guide/parameters/background.md index 89985470..e307c981 100644 --- a/docs/docs/user-guide/parameters/background.md +++ b/docs/docs/user-guide/parameters/background.md @@ -2,26 +2,27 @@ # \_pd_background -This category defines various background functions that could be used when -calculating diffractograms. Please see the -[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) for -further details. +This category defines various background functions that could be used +when calculating diffractograms. Please see the +[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) +for further details. ## [\_pd_background.line_segment_X](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) -List of X-coordinates used to create many straight-line segments representing -the background in a calculated diffractogram. +List of X-coordinates used to create many straight-line segments +representing the background in a calculated diffractogram. Supported values: `2theta` and `time-of-flight` ## [\_pd_background.line_segment_intensity](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) -List of intensities used to create many straight-line segments representing the -background in a calculated diffractogram. +List of intensities used to create many straight-line segments +representing the background in a calculated diffractogram. ## [\_pd_background.X_coordinate](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) -The type of X-coordinate against which the pd_background values were calculated. +The type of X-coordinate against which the pd_background values were +calculated. [0]: # diff --git a/docs/docs/user-guide/parameters/cell.md b/docs/docs/user-guide/parameters/cell.md index 5aeb12ae..806df516 100644 --- a/docs/docs/user-guide/parameters/cell.md +++ b/docs/docs/user-guide/parameters/cell.md @@ -2,8 +2,8 @@ # \_cell -Data items in this category record details about the crystallographic cell -parameters and their measurement. Please see the +Data items in this category record details about the crystallographic +cell parameters and their measurement. Please see the [IUCr page](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/CCELL.html) for further details. diff --git a/docs/docs/user-guide/parameters/expt_type.md b/docs/docs/user-guide/parameters/expt_type.md index 5aeb12ae..806df516 100644 --- a/docs/docs/user-guide/parameters/expt_type.md +++ b/docs/docs/user-guide/parameters/expt_type.md @@ -2,8 +2,8 @@ # \_cell -Data items in this category record details about the crystallographic cell -parameters and their measurement. Please see the +Data items in this category record details about the crystallographic +cell parameters and their measurement. Please see the [IUCr page](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/CCELL.html) for further details. diff --git a/docs/docs/user-guide/parameters/instrument.md b/docs/docs/user-guide/parameters/instrument.md index 3975161c..fbeca6c9 100644 --- a/docs/docs/user-guide/parameters/instrument.md +++ b/docs/docs/user-guide/parameters/instrument.md @@ -2,21 +2,22 @@ # \_pd_instr -This section contains information relevant to the instrument used for the -diffraction measurement, similar to this +This section contains information relevant to the instrument used for +the diffraction measurement, similar to this [IUCr section](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd). ## [\_pd_instr.resolution](#) -In general, the profile of a Bragg reflection centred at the peak position can -be approximated by mathematical convolution of contributions from the -instrument, called the instrumental resolution function, and from the -microstructure of the sample. Because many contributions to powder diffraction -peaks have a nearly Gaussian or Lorentzian shape, the pseudo-Voigt function, is -widely used to describe peak profiles in powder diffraction. +In general, the profile of a Bragg reflection centred at the peak +position can be approximated by mathematical convolution of +contributions from the instrument, called the instrumental resolution +function, and from the microstructure of the sample. Because many +contributions to powder diffraction peaks have a nearly Gaussian or +Lorentzian shape, the pseudo-Voigt function, is widely used to describe +peak profiles in powder diffraction. -Half-width parameters (normally characterising the instrumental resolution -function) as implemented in [CrysPy](https://cryspy.fr): +Half-width parameters (normally characterising the instrumental +resolution function) as implemented in [CrysPy](https://cryspy.fr): - \_pd_instr.resolution_u - \_pd_instr.resolution_v @@ -34,7 +35,8 @@ Lorentzian isotropic particle size parameteras implemented in ## [\_pd_instr.reflex_asymmetry](#) -Peak profile asymmetry parameters as implemented in [CrysPy](https://cryspy.fr). +Peak profile asymmetry parameters as implemented in +[CrysPy](https://cryspy.fr). - \_pd_instr.reflex_asymmetry_p1 - \_pd_instr.reflex_asymmetry_p2 diff --git a/docs/docs/user-guide/parameters/linked_phases.md b/docs/docs/user-guide/parameters/linked_phases.md index df3f6d29..1d00f1d2 100644 --- a/docs/docs/user-guide/parameters/linked_phases.md +++ b/docs/docs/user-guide/parameters/linked_phases.md @@ -2,10 +2,10 @@ # \_pd_phase_block -A table of phases relevant to the current data block. Each phase is identified -by its data block identifier. Please see the -[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) for -further details. +A table of phases relevant to the current data block. Each phase is +identified by its data block identifier. Please see the +[IUCr page](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) +for further details. ## [\_pd_phase_block.id](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) diff --git a/docs/docs/user-guide/parameters/pd_meas.md b/docs/docs/user-guide/parameters/pd_meas.md index b294a96a..fcb4db17 100644 --- a/docs/docs/user-guide/parameters/pd_meas.md +++ b/docs/docs/user-guide/parameters/pd_meas.md @@ -7,8 +7,8 @@ This section contains the measured diffractogram, similar to this ## [\_pd_meas.2theta_scan](https://raw.githubusercontent.com/COMCIFS/Powder_Dictionary/master/cif_pow.dic) -2θ diffraction angle (in degrees) for intensity points measured in a scanning -method. +2θ diffraction angle (in degrees) for intensity points measured in a +scanning method. ## [\_pd_meas.time-of-flight](https://raw.githubusercontent.com/COMCIFS/Powder_Dictionary/master/cif_pow.dic) diff --git a/docs/docs/user-guide/parameters/peak.md b/docs/docs/user-guide/parameters/peak.md index 5aeb12ae..806df516 100644 --- a/docs/docs/user-guide/parameters/peak.md +++ b/docs/docs/user-guide/parameters/peak.md @@ -2,8 +2,8 @@ # \_cell -Data items in this category record details about the crystallographic cell -parameters and their measurement. Please see the +Data items in this category record details about the crystallographic +cell parameters and their measurement. Please see the [IUCr page](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/CCELL.html) for further details. diff --git a/docs/docs/user-guide/parameters/space_group.md b/docs/docs/user-guide/parameters/space_group.md index 883be103..ae5bce6b 100644 --- a/docs/docs/user-guide/parameters/space_group.md +++ b/docs/docs/user-guide/parameters/space_group.md @@ -2,16 +2,16 @@ # \_space_group -Contains all the data items that refer to the space group as a whole. Please see -the +Contains all the data items that refer to the space group as a whole. +Please see the [IUCr page](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/CSPACE_GROUP.html) for further details. ## [\_space_group.name_H-M_alt](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Ispace_group.name_H-M_alt.html) -The international Hermann-Mauguin space-group symbol as defined in International -Tables for Crystallography Volume A. It allows any Hermann-Mauguin symbol to be -given. +The international Hermann-Mauguin space-group symbol as defined in +International Tables for Crystallography Volume A. It allows any +Hermann-Mauguin symbol to be given. ## [\_space_group.IT_coordinate_system_code](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Ispace_group.IT_coordinate_system_code.html) diff --git a/pixi.lock b/pixi.lock index 4cd98911..def2f9e4 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4883,8 +4883,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+dev4 - sha256: c8c56839d471863bde26398ee4ae4a9316dae45a3e250437f8bfc947c30d2cb9 + version: 0.10.2+devdirty4 + sha256: 76d657c42f711c02fa25bafdfe3f68d98e38a82734abd7d83e904d61d31e51c8 requires_dist: - asciichartpy - asteval diff --git a/pixi.toml b/pixi.toml index f94cc0ce..859b89e7 100644 --- a/pixi.toml +++ b/pixi.toml @@ -54,7 +54,7 @@ macos = '14.0' [dependencies] nodejs = '*' # Required for Prettier (non-Python formatting) -gsl = '*' # GNU Scientific Library; required for pdffit2 +gsl = '*' # GNU Scientific Library; required for pdffit2 [pypi-dependencies] # == [feature.default.pypi-dependencies] #pip = '*' # Native package installer @@ -62,14 +62,11 @@ easydiffraction = { path = ".", editable = true, extras = ['dev'] } # Specific features: Set specific Python versions - [feature.py-min.dependencies] python = '3.11.*' [feature.py-max.dependencies] python = '3.13.*' - - ############## # ENVIRONMENTS ############## @@ -272,4 +269,4 @@ post-install = { depends-on = [ ########################## # 🔗 Main Package Shortcut ########################## -easydiffraction = 'python -m easydiffraction' \ No newline at end of file +easydiffraction = 'python -m easydiffraction' diff --git a/pycrysfml.md b/pycrysfml.md index af681478..de9bc4d0 100644 --- a/pycrysfml.md +++ b/pycrysfml.md @@ -12,7 +12,8 @@ ```bash otool -L .venv/lib/python3.12/site-packages/pycrysfml/crysfml08lib.so ``` -- If the library is linked to the wrong Python version, you can fix it with: +- If the library is linked to the wrong Python version, you can fix it + with: ```bash install_name_tool -change `python3-config --prefix`/Python `python3-config --prefix`/lib/libpython3.12.dylib .venv/lib/python3.12/site-packages/pycrysfml/crysfml08lib.so ``` diff --git a/pyproject.toml b/pyproject.toml index 9c4af972..e723f609 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,12 +44,12 @@ dependencies = [ 'diffpy.utils', # Utilities for PDF calculations 'uncertainties', # Propagation of uncertainties 'typeguard', # Runtime type checking - 'darkdetect', # Detecting dark mode (system-level) - 'pandas', # Displaying tables in Jupyter notebooks - 'plotly', # Interactive plots - 'py3Dmol', # Visualisation of crystal structures - 'jupyterlab', # Jupyter notebooks - 'pixi-kernel', # Pixi Jupyter kernel + 'darkdetect', # Detecting dark mode (system-level) + 'pandas', # Displaying tables in Jupyter notebooks + 'plotly', # Interactive plots + 'py3Dmol', # Visualisation of crystal structures + 'jupyterlab', # Jupyter notebooks + 'pixi-kernel', # Pixi Jupyter kernel ] [project.optional-dependencies] @@ -169,7 +169,7 @@ source = ['src'] # Limit coverage to the source code directory [tool.coverage.report] show_missing = true # Show missing lines skip_covered = false # Skip files with 100% coverage in the report -fail_under = 60 # Minimum coverage percentage to pass +fail_under = 60 # Minimum coverage percentage to pass ########################## # Configuration for pytest @@ -212,12 +212,12 @@ select = [ # Various rules #'C90', # https://docs.astral.sh/ruff/rules/#mccabe-c90 'F', # https://docs.astral.sh/ruff/rules/#pyflakes-f - 'FLY', # https://docs.astral.sh/ruff/rules/#flynt-fly + 'FLY', # https://docs.astral.sh/ruff/rules/#flynt-fly #'FURB', # https://docs.astral.sh/ruff/rules/#refurb-furb - 'I', # https://docs.astral.sh/ruff/rules/#isort-i - 'N', # https://docs.astral.sh/ruff/rules/#pep8-naming-n - 'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy - 'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh + 'I', # https://docs.astral.sh/ruff/rules/#isort-i + 'N', # https://docs.astral.sh/ruff/rules/#pep8-naming-n + 'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy + 'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh #'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf #'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf #'TRY', # https://docs.astral.sh/ruff/rules/#tryceratops-try @@ -233,30 +233,30 @@ select = [ # flake8 rules #'A', # https://docs.astral.sh/ruff/rules/#flake8-builtins-a #'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann - 'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg + 'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg #'ASYNC', # https://docs.astral.sh/ruff/rules/#flake8-async-async - 'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b + 'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b #'BLE', # https://docs.astral.sh/ruff/rules/#flake8-blind-except-ble #'C4', # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 #'COM', # https://docs.astral.sh/ruff/rules/#flake8-commas-com - 'DTZ', # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz + 'DTZ', # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz #'EM', # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em #'FA', # https://docs.astral.sh/ruff/rules/#flake8-future-annotations-fa #'FBT', # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt #'FIX', # https://docs.astral.sh/ruff/rules/#flake8-fixme-fix - 'G', # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g - 'ICN', # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn + 'G', # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g + 'ICN', # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn #'INP', # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp #'ISC', # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc #'LOG', # https://docs.astral.sh/ruff/rules/#flake8-logging-log #'PIE', # https://docs.astral.sh/ruff/rules/#flake8-pie-pie #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt - 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth #'PYI', # https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi #'RET', # https://docs.astral.sh/ruff/rules/#flake8-return-ret #'RSE', # https://docs.astral.sh/ruff/rules/#flake8-raise-rse - 'S', # https://docs.astral.sh/ruff/rules/#flake8-bandit-s - 'SIM', # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + 'S', # https://docs.astral.sh/ruff/rules/#flake8-bandit-s + 'SIM', # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim #'SLF', # https://docs.astral.sh/ruff/rules/#flake8-self-slf #'SLOT', # https://docs.astral.sh/ruff/rules/#flake8-slots-slot #'T20', # https://docs.astral.sh/ruff/rules/#flake8-print-t20 @@ -277,6 +277,9 @@ ignore = [ # Ignore specific rules in certain files or directories [tool.ruff.lint.per-file-ignores] +'*/__init__.py' = [ + 'F401', # re-exports are intentional in __init__.py +] 'tests/**' = [ 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc From 7f095eacc6bfe28ff019f8b902ae95e7c5fdd5ef Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 25 Mar 2026 22:00:26 +0100 Subject: [PATCH 03/48] pixi run py-lint-fix --- pixi.lock | 4 +- pyproject.toml | 18 +-- .../analysis/categories/test_aliases.py | 4 +- .../analysis/categories/test_constraints.py | 4 +- .../categories/test_joint_fit_experiments.py | 4 +- .../easydiffraction/analysis/test_analysis.py | 1 - .../analysis/test_analysis_access_params.py | 4 +- .../easydiffraction/core/test_category.py | 4 +- .../easydiffraction/core/test_collection.py | 5 +- .../easydiffraction/core/test_datablock.py | 5 +- tests/unit/easydiffraction/core/test_guard.py | 2 - .../easydiffraction/core/test_parameters.py | 10 +- .../categories/background/test_base.py | 2 +- .../categories/background/test_enums.py | 6 +- .../categories/background/test_factory.py | 4 +- .../categories/data/test_bragg_pd.py | 1 - .../categories/data/test_bragg_sc.py | 1 - .../categories/data/test_factory.py | 16 +-- .../categories/data/test_total_pd.py | 1 - .../categories/instrument/test_factory.py | 4 +- .../categories/peak/test_tof_mixins.py | 4 +- .../categories/test_excluded_regions.py | 4 +- .../experiment/categories/test_extinction.py | 2 - .../categories/test_linked_crystal.py | 2 - .../experiment/item/test_bragg_pd.py | 2 - .../experiment/item/test_bragg_sc.py | 1 - .../experiment/item/test_factory.py | 1 - .../datablocks/experiment/test_collection.py | 2 +- .../datablocks/structure/test_collection.py | 4 - .../display/plotters/test_base.py | 3 +- .../io/cif/test_serialize_more.py | 3 - .../easydiffraction/project/test_project.py | 1 - .../easydiffraction/summary/test_summary.py | 5 - .../utils/test_theme_detect.py | 131 ++++++++---------- .../unit/easydiffraction/utils/test_utils.py | 3 - 35 files changed, 110 insertions(+), 158 deletions(-) diff --git a/pixi.lock b/pixi.lock index def2f9e4..ff6a7a6c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4883,8 +4883,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty4 - sha256: 76d657c42f711c02fa25bafdfe3f68d98e38a82734abd7d83e904d61d31e51c8 + version: 0.10.2+devdirty5 + sha256: 8256b9114218814d2c4984beb6b63023cd5be686f8e9ef7f07307f5d5716c03d requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index e723f609..3d40179f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -191,10 +191,15 @@ testpaths = ['tests'] # https://docs.astral.sh/ruff/rules/ [tool.ruff] -exclude = ['tmp'] +exclude = [ + 'tmp', + # Vendored jupyter_dark_detect: keep as-is from upstream for easy updates + # https://github.com/OpenMined/jupyter-dark-detect/tree/main/jupyter_dark_detect + 'src/easydiffraction/utils/_vendored/jupyter_dark_detect', +] indent-width = 4 line-length = 99 -preview = true # Enable new rules that are not yet stable, like DOC +preview = true # Enable new rules that are not yet stable, like DOC # Formatting options for Ruff @@ -290,15 +295,6 @@ ignore = [ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'T201', # https://docs.astral.sh/ruff/rules/print/ ] -# Vendored jupyter_dark_detect: keep as-is from upstream for easy updates -# https://github.com/OpenMined/jupyter-dark-detect/tree/main/jupyter_dark_detect -'src/easydiffraction/utils/_vendored/jupyter_dark_detect/*' = [ - 'S', - 'TID', - 'W', - 'S', - 'E', -] # Specific options for certain rules diff --git a/tests/unit/easydiffraction/analysis/categories/test_aliases.py b/tests/unit/easydiffraction/analysis/categories/test_aliases.py index 4860961d..f44609ee 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_aliases.py +++ b/tests/unit/easydiffraction/analysis/categories/test_aliases.py @@ -7,8 +7,8 @@ def test_alias_creation_and_collection(): a = Alias() - a.label='x' - a.param_uid='p1' + a.label = 'x' + a.param_uid = 'p1' assert a.label.value == 'x' coll = Aliases() coll.create(label='x', param_uid='p1') diff --git a/tests/unit/easydiffraction/analysis/categories/test_constraints.py b/tests/unit/easydiffraction/analysis/categories/test_constraints.py index 7d4acb0a..2ed29866 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_constraints.py +++ b/tests/unit/easydiffraction/analysis/categories/test_constraints.py @@ -7,8 +7,8 @@ def test_constraint_creation_and_collection(): c = Constraint() - c.lhs_alias='a' - c.rhs_expr='b + c' + c.lhs_alias = 'a' + c.rhs_expr = 'b + c' assert c.lhs_alias.value == 'a' coll = Constraints() coll.create(lhs_alias='a', rhs_expr='b + c') diff --git a/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py b/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py index c4194c35..9e8e8d1f 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py +++ b/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py @@ -7,8 +7,8 @@ def test_joint_fit_experiment_and_collection(): j = JointFitExperiment() - j.id='ex1' - j.weight=0.5 + j.id = 'ex1' + j.weight = 0.5 assert j.id.value == 'ex1' assert j.weight.value == 0.5 coll = JointFitExperiments() diff --git a/tests/unit/easydiffraction/analysis/test_analysis.py b/tests/unit/easydiffraction/analysis/test_analysis.py index 56d493aa..8323c8af 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis.py +++ b/tests/unit/easydiffraction/analysis/test_analysis.py @@ -36,7 +36,6 @@ def test_show_current_minimizer_prints(capsys): assert 'lmfit' in out - def test_fit_mode_category_and_joint_fit_experiments(monkeypatch, capsys): from easydiffraction.analysis.analysis import Analysis diff --git a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py index 96bf6ca1..1edcb978 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py +++ b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py @@ -2,11 +2,11 @@ # SPDX-License-Identifier: BSD-3-Clause def test_how_to_access_parameters_prints_paths_and_uids(capsys, monkeypatch): + import easydiffraction.analysis.analysis as analysis_mod from easydiffraction.analysis.analysis import Analysis - from easydiffraction.core.variable import Parameter from easydiffraction.core.validation import AttributeSpec + from easydiffraction.core.variable import Parameter from easydiffraction.io.cif.handler import CifHandler - import easydiffraction.analysis.analysis as analysis_mod # Build two parameters with identity metadata set directly def make_param(db, cat, entry, name, val): diff --git a/tests/unit/easydiffraction/core/test_category.py b/tests/unit/easydiffraction/core/test_category.py index c53fd12c..50c66e4e 100644 --- a/tests/unit/easydiffraction/core/test_category.py +++ b/tests/unit/easydiffraction/core/test_category.py @@ -3,8 +3,8 @@ from easydiffraction.core.category import CategoryCollection from easydiffraction.core.category import CategoryItem -from easydiffraction.core.variable import StringDescriptor from easydiffraction.core.validation import AttributeSpec +from easydiffraction.core.variable import StringDescriptor from easydiffraction.io.cif.handler import CifHandler @@ -97,5 +97,3 @@ def test_category_collection_help(capsys): assert 'Items (2)' in out assert 'n1' in out assert 'n2' in out - - diff --git a/tests/unit/easydiffraction/core/test_collection.py b/tests/unit/easydiffraction/core/test_collection.py index 616b232f..c940547c 100644 --- a/tests/unit/easydiffraction/core/test_collection.py +++ b/tests/unit/easydiffraction/core/test_collection.py @@ -55,11 +55,11 @@ def as_cif(self) -> str: def test_collection_remove(): + import pytest + from easydiffraction.core.collection import CollectionBase from easydiffraction.core.identity import Identity - import pytest - class Item: def __init__(self, name): self._identity = Identity(owner=self, category_entry=lambda: name) @@ -119,4 +119,3 @@ def as_cif(self) -> str: del c['beta'] assert 'beta' not in c assert len(c) == 1 - diff --git a/tests/unit/easydiffraction/core/test_datablock.py b/tests/unit/easydiffraction/core/test_datablock.py index 6565bbaa..4011197b 100644 --- a/tests/unit/easydiffraction/core/test_datablock.py +++ b/tests/unit/easydiffraction/core/test_datablock.py @@ -5,8 +5,8 @@ def test_datablock_collection_add_and_filters_with_real_parameters(): from easydiffraction.core.category import CategoryItem from easydiffraction.core.datablock import DatablockCollection from easydiffraction.core.datablock import DatablockItem - from easydiffraction.core.variable import Parameter from easydiffraction.core.validation import AttributeSpec + from easydiffraction.core.variable import Parameter from easydiffraction.io.cif.handler import CifHandler class Cat(CategoryItem): @@ -78,8 +78,8 @@ def cat(self): def test_datablock_item_help(capsys): from easydiffraction.core.category import CategoryItem from easydiffraction.core.datablock import DatablockItem - from easydiffraction.core.variable import Parameter from easydiffraction.core.validation import AttributeSpec + from easydiffraction.core.variable import Parameter from easydiffraction.io.cif.handler import CifHandler class Cat(CategoryItem): @@ -133,4 +133,3 @@ def __init__(self, name): out = capsys.readouterr().out assert 'Items (1)' in out assert 'A' in out - diff --git a/tests/unit/easydiffraction/core/test_guard.py b/tests/unit/easydiffraction/core/test_guard.py index 34407914..20fc8a4e 100644 --- a/tests/unit/easydiffraction/core/test_guard.py +++ b/tests/unit/easydiffraction/core/test_guard.py @@ -99,5 +99,3 @@ def test_first_sentence_extracts_first_paragraph(): assert GuardedBase._first_sentence('One liner.') == 'One liner.' assert GuardedBase._first_sentence('First.\n\nSecond.') == 'First.' assert GuardedBase._first_sentence('Line one\ncontinued.') == 'Line one continued.' - - diff --git a/tests/unit/easydiffraction/core/test_parameters.py b/tests/unit/easydiffraction/core/test_parameters.py index 35c65558..57341af4 100644 --- a/tests/unit/easydiffraction/core/test_parameters.py +++ b/tests/unit/easydiffraction/core/test_parameters.py @@ -13,9 +13,9 @@ def test_module_import(): def test_string_descriptor_type_override_raises_type_error(): # Creating a StringDescriptor with a NUMERIC spec should raise via Diagnostics - from easydiffraction.core.variable import StringDescriptor from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.validation import DataTypes + from easydiffraction.core.variable import StringDescriptor from easydiffraction.io.cif.handler import CifHandler with pytest.raises(TypeError): @@ -28,8 +28,8 @@ def test_string_descriptor_type_override_raises_type_error(): def test_numeric_descriptor_str_includes_units(): - from easydiffraction.core.variable import NumericDescriptor from easydiffraction.core.validation import AttributeSpec + from easydiffraction.core.variable import NumericDescriptor from easydiffraction.io.cif.handler import CifHandler d = NumericDescriptor( @@ -43,8 +43,8 @@ def test_numeric_descriptor_str_includes_units(): def test_parameter_string_repr_and_as_cif_and_flags(): - from easydiffraction.core.variable import Parameter from easydiffraction.core.validation import AttributeSpec + from easydiffraction.core.variable import Parameter from easydiffraction.io.cif.handler import CifHandler p = Parameter( @@ -69,8 +69,8 @@ def test_parameter_string_repr_and_as_cif_and_flags(): def test_parameter_uncertainty_must_be_non_negative(): - from easydiffraction.core.variable import Parameter from easydiffraction.core.validation import AttributeSpec + from easydiffraction.core.variable import Parameter from easydiffraction.io.cif.handler import CifHandler p = Parameter( @@ -83,8 +83,8 @@ def test_parameter_uncertainty_must_be_non_negative(): def test_parameter_fit_bounds_assign_and_read(): - from easydiffraction.core.variable import Parameter from easydiffraction.core.validation import AttributeSpec + from easydiffraction.core.variable import Parameter from easydiffraction.io.cif.handler import CifHandler p = Parameter( diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py index 31c7fd0b..985c2d5b 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py @@ -7,9 +7,9 @@ def test_background_base_minimal_impl_and_collection_cif(): from easydiffraction.core.category import CategoryItem from easydiffraction.core.collection import CollectionBase - from easydiffraction.core.variable import Parameter from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.validation import DataTypes + from easydiffraction.core.variable import Parameter from easydiffraction.datablocks.experiment.categories.background.base import BackgroundBase from easydiffraction.io.cif.handler import CifHandler diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py index 47e0f5f3..f8ea00af 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py @@ -3,12 +3,12 @@ def test_background_type_info(): - from easydiffraction.datablocks.experiment.categories.background.line_segment import ( - LineSegmentBackground, - ) from easydiffraction.datablocks.experiment.categories.background.chebyshev import ( ChebyshevPolynomialBackground, ) + from easydiffraction.datablocks.experiment.categories.background.line_segment import ( + LineSegmentBackground, + ) assert LineSegmentBackground.type_info.tag == 'line-segment' assert LineSegmentBackground.type_info.description == 'Linear interpolation between points' diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py index 15c40a19..285f3b81 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py @@ -5,7 +5,9 @@ def test_background_factory_default_and_errors(): - from easydiffraction.datablocks.experiment.categories.background.factory import BackgroundFactory + from easydiffraction.datablocks.experiment.categories.background.factory import ( + BackgroundFactory, + ) # Default via default_tag() obj = BackgroundFactory.create(BackgroundFactory.default_tag()) diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py index eaa48808..10514fe0 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py @@ -142,4 +142,3 @@ def test_pd_data_intensity_meas_su_zero_replacement(): assert su[0] == 1.0 # replaced assert su[1] == 1.0 # replaced assert su[2] == 5.0 # kept - diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py index 06dd634d..0b5c0cfe 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py @@ -90,4 +90,3 @@ def test_refln_data_type_info(): assert ReflnData.type_info.tag == 'bragg-sc' assert ReflnData.type_info.description == 'Bragg single-crystal reflection data' - diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py index 18ecd5d0..bb4c7ffb 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py @@ -5,12 +5,11 @@ def test_data_factory_default_and_errors(): - from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory - # Ensure concrete classes are registered from easydiffraction.datablocks.experiment.categories.data import bragg_pd # noqa: F401 from easydiffraction.datablocks.experiment.categories.data import bragg_sc # noqa: F401 from easydiffraction.datablocks.experiment.categories.data import total_pd # noqa: F401 + from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory # Explicit type by tag obj = DataFactory.create('bragg-pd') @@ -32,15 +31,14 @@ def test_data_factory_default_and_errors(): def test_data_factory_default_tag_resolution(): - from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory - from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum - from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum - from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum - # Ensure concrete classes are registered from easydiffraction.datablocks.experiment.categories.data import bragg_pd # noqa: F401 from easydiffraction.datablocks.experiment.categories.data import bragg_sc # noqa: F401 from easydiffraction.datablocks.experiment.categories.data import total_pd # noqa: F401 + from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory + from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum + from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum + from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum # Context-dependent default: Bragg powder CWL tag = DataFactory.default_tag( @@ -74,16 +72,14 @@ def test_data_factory_default_tag_resolution(): def test_data_factory_supported_tags(): - from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory - # Ensure concrete classes are registered from easydiffraction.datablocks.experiment.categories.data import bragg_pd # noqa: F401 from easydiffraction.datablocks.experiment.categories.data import bragg_sc # noqa: F401 from easydiffraction.datablocks.experiment.categories.data import total_pd # noqa: F401 + from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory tags = DataFactory.supported_tags() assert 'bragg-pd' in tags assert 'bragg-pd-tof' in tags assert 'bragg-sc' in tags assert 'total-pd' in tags - diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py index 90c85e3e..ae520bfc 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py @@ -89,4 +89,3 @@ def test_total_data_type_info(): assert TotalData.type_info.tag == 'total-pd' assert TotalData.type_info.description == 'Total scattering (PDF) data' - diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py index f122f9be..5b399346 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py @@ -6,7 +6,9 @@ def test_instrument_factory_default_and_errors(): try: - from easydiffraction.datablocks.experiment.categories.instrument.factory import InstrumentFactory + from easydiffraction.datablocks.experiment.categories.instrument.factory import ( + InstrumentFactory, + ) from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum except ImportError as e: # pragma: no cover - environment-specific circular import diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py index ae39c99c..e8932153 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py @@ -6,7 +6,9 @@ def test_tof_broadening_and_asymmetry_mixins(): from easydiffraction.datablocks.experiment.categories.peak.base import PeakBase - from easydiffraction.datablocks.experiment.categories.peak.tof_mixins import IkedaCarpenterAsymmetryMixin + from easydiffraction.datablocks.experiment.categories.peak.tof_mixins import ( + IkedaCarpenterAsymmetryMixin, + ) from easydiffraction.datablocks.experiment.categories.peak.tof_mixins import TofBroadeningMixin class TofPeak(PeakBase, TofBroadeningMixin, IkedaCarpenterAsymmetryMixin,): diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py index 8026740b..dc290dcf 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py @@ -23,7 +23,7 @@ def test_excluded_regions_add_updates_datastore_and_cif(): meas=full_meas.copy(), meas_su=full_meas_su.copy(), ) - + def set_calc_status(status): # _set_calc_status sets excluded to the inverse ds.excluded = ~status @@ -31,7 +31,7 @@ def set_calc_status(status): ds.x = ds.full_x[status] ds.meas = ds.full_meas[status] ds.meas_su = ds.full_meas_su[status] - + ds._set_calc_status = set_calc_status coll = ExcludedRegions() diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py index 5287b488..b4621cf2 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py @@ -74,5 +74,3 @@ def test_extinction_factory_default_tag(): ) assert ExtinctionFactory.default_tag() == 'shelx' - - diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py index 06035598..06b55151 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py @@ -86,5 +86,3 @@ def test_linked_crystal_factory_default_tag(): ) assert LinkedCrystalFactory.default_tag() == 'default' - - diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py index 8b164ed0..8ae3bcf2 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py @@ -22,8 +22,6 @@ def _mk_type_powder_cwl_bragg(): return et - - def test_background_defaults_and_change(): expt = BraggPdExperiment(name='e1', type=_mk_type_powder_cwl_bragg()) # default background type diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py index c01a5ee2..5fedd3d5 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py @@ -21,7 +21,6 @@ def _mk_type_sc_bragg(): return et - class _ConcreteCwlSc(CwlScExperiment): def _load_ascii_data_to_experiment(self, data_path: str) -> None: # Not used in this test diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py index 8dac91b6..fbce5523 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py @@ -25,4 +25,3 @@ def test_experiment_factory_from_scratch(): ) # Instance should be created (BraggPdExperiment) assert hasattr(ex, 'type') and ex.type.sample_form.value == SampleFormEnum.POWDER.value - diff --git a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py index 5165f141..589af777 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py +++ b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py @@ -10,8 +10,8 @@ def test_module_import(): def test_experiments_show_and_remove(monkeypatch, capsys): - from easydiffraction.datablocks.experiment.item.base import ExperimentBase from easydiffraction.datablocks.experiment.collection import Experiments + from easydiffraction.datablocks.experiment.item.base import ExperimentBase class DummyType: def __init__(self): diff --git a/tests/unit/easydiffraction/datablocks/structure/test_collection.py b/tests/unit/easydiffraction/datablocks/structure/test_collection.py index 9955a1e3..429f2648 100644 --- a/tests/unit/easydiffraction/datablocks/structure/test_collection.py +++ b/tests/unit/easydiffraction/datablocks/structure/test_collection.py @@ -1,6 +1,2 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause - -import pytest - -from easydiffraction.datablocks.structure.collection import Structures diff --git a/tests/unit/easydiffraction/display/plotters/test_base.py b/tests/unit/easydiffraction/display/plotters/test_base.py index d1c2d561..06e80057 100644 --- a/tests/unit/easydiffraction/display/plotters/test_base.py +++ b/tests/unit/easydiffraction/display/plotters/test_base.py @@ -1,9 +1,8 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause -import importlib -import types import sys +import types def test_module_import(): diff --git a/tests/unit/easydiffraction/io/cif/test_serialize_more.py b/tests/unit/easydiffraction/io/cif/test_serialize_more.py index 54f345ee..441ba9fe 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize_more.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize_more.py @@ -1,9 +1,6 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause -import numpy as np -import pytest - def test_datablock_item_to_cif_includes_item_and_collection(): import easydiffraction.io.cif.serialize as MUT diff --git a/tests/unit/easydiffraction/project/test_project.py b/tests/unit/easydiffraction/project/test_project.py index 1a949fc5..078d59bc 100644 --- a/tests/unit/easydiffraction/project/test_project.py +++ b/tests/unit/easydiffraction/project/test_project.py @@ -19,4 +19,3 @@ def test_project_help(capsys): assert 'experiments' in out assert 'analysis' in out assert 'summary' in out - diff --git a/tests/unit/easydiffraction/summary/test_summary.py b/tests/unit/easydiffraction/summary/test_summary.py index 29da4e28..c8c25f9e 100644 --- a/tests/unit/easydiffraction/summary/test_summary.py +++ b/tests/unit/easydiffraction/summary/test_summary.py @@ -46,11 +46,6 @@ class R: assert 'FITTING' in out - - - - - def test_module_import(): import easydiffraction.summary.summary as MUT diff --git a/tests/unit/easydiffraction/utils/test_theme_detect.py b/tests/unit/easydiffraction/utils/test_theme_detect.py index e9899fb9..0ff2a4ad 100644 --- a/tests/unit/easydiffraction/utils/test_theme_detect.py +++ b/tests/unit/easydiffraction/utils/test_theme_detect.py @@ -199,23 +199,20 @@ def test_default_to_false(self) -> None: 'easydiffraction.utils._vendored.theme_detect.' '_check_jupyterlab_settings', return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_vscode_settings', + return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_javascript_detection', + return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_system_preferences', + return_value=None, ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=None, - ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_javascript_detection', - return_value=None, - ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_system_preferences', - return_value=None, - ): - assert is_dark() is False + assert is_dark() is False def test_jupyterlab_priority(self) -> None: """Test that JupyterLab settings take priority.""" @@ -225,13 +222,12 @@ def test_jupyterlab_priority(self) -> None: 'easydiffraction.utils._vendored.theme_detect.' '_check_jupyterlab_settings', return_value=True, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_vscode_settings', + return_value=False, ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=False, - ): - assert is_dark() is True + assert is_dark() is True def test_vscode_second_priority(self) -> None: """Test that VS Code settings are checked after JupyterLab.""" @@ -241,13 +237,12 @@ def test_vscode_second_priority(self) -> None: 'easydiffraction.utils._vendored.theme_detect.' '_check_jupyterlab_settings', return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_vscode_settings', + return_value=True, ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=True, - ): - assert is_dark() is True + assert is_dark() is True def test_javascript_before_system(self) -> None: """Test that JS detection comes before system preferences.""" @@ -257,24 +252,21 @@ def test_javascript_before_system(self) -> None: 'easydiffraction.utils._vendored.theme_detect.' '_check_jupyterlab_settings', return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_vscode_settings', + return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_javascript_detection', + return_value=True, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_system_preferences', + return_value=False, ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=None, - ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_javascript_detection', - return_value=True, - ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_system_preferences', - return_value=False, - ): - # JS detection should win over system prefs - assert is_dark() is True + # JS detection should win over system prefs + assert is_dark() is True class TestGetDetectionResult: @@ -288,33 +280,30 @@ def test_returns_dict_with_all_methods(self) -> None: 'easydiffraction.utils._vendored.theme_detect.' '_check_jupyterlab_settings', return_value=True, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_vscode_settings', + return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_javascript_detection', + return_value=None, + ), mock.patch( + 'easydiffraction.utils._vendored.theme_detect.' + '_check_system_preferences', + return_value=False, ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=None, - ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_javascript_detection', - return_value=None, - ): - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_system_preferences', - return_value=False, - ): - result = get_detection_result() - - assert 'jupyterlab_settings' in result - assert 'vscode_settings' in result - assert 'javascript_dom' in result - assert 'system_preferences' in result - - assert result['jupyterlab_settings'] is True - assert result['vscode_settings'] is None - assert result['javascript_dom'] is None - assert result['system_preferences'] is False + result = get_detection_result() + + assert 'jupyterlab_settings' in result + assert 'vscode_settings' in result + assert 'javascript_dom' in result + assert 'system_preferences' in result + + assert result['jupyterlab_settings'] is True + assert result['vscode_settings'] is None + assert result['javascript_dom'] is None + assert result['system_preferences'] is False class TestImports: diff --git a/tests/unit/easydiffraction/utils/test_utils.py b/tests/unit/easydiffraction/utils/test_utils.py index 183178bd..f1691c7a 100644 --- a/tests/unit/easydiffraction/utils/test_utils.py +++ b/tests/unit/easydiffraction/utils/test_utils.py @@ -5,7 +5,6 @@ import pytest - def test_module_import(): import easydiffraction.utils.utils as MUT @@ -354,7 +353,6 @@ def __exit__(self, *args): def test_resolve_tutorial_url(): - import easydiffraction.utils.utils as MUT # Test with a specific version url_template = 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb' @@ -362,4 +360,3 @@ def test_resolve_tutorial_url(): # So we just test that the function exists and replaces {version} result = url_template.replace('{version}', '0.8.0') assert result == 'https://example.com/0.8.0/tutorials/ed-1/ed-1.ipynb' - From ba1536de0374833b011d5cf57627f41fd23fd468 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 25 Mar 2026 22:07:33 +0100 Subject: [PATCH 04/48] Disable some rules temporary --- pixi.lock | 4 ++-- pyproject.toml | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pixi.lock b/pixi.lock index ff6a7a6c..ff21006c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4883,8 +4883,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty5 - sha256: 8256b9114218814d2c4984beb6b63023cd5be686f8e9ef7f07307f5d5716c03d + version: 0.10.2+devdirty6 + sha256: 377f0443a16b4d4edf0c288797da06462da5f22eae7dbe0148832cac2610eb01 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index 3d40179f..e5107fea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -278,6 +278,9 @@ ignore = [ # The following covered by [tool.pydoclint] section below 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc + # Temporary: + 'DTZ005', + ] # Ignore specific rules in certain files or directories @@ -290,6 +293,21 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'S101', # https://docs.astral.sh/ruff/rules/assert/ + # Temporary: + 'ARG001', + 'ARG002', + 'ARG004', + 'ARG005', + 'B011', + 'B017', + 'B018', + 'E501', + 'E741', + 'F841', + 'N801', + 'N805', + 'N812', + 'SIM117', ] 'docs/**' = [ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ From 00fde44b2f48e94cc662bf2555d1d0ef94afe7b9 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 25 Mar 2026 22:08:07 +0100 Subject: [PATCH 05/48] pixi run py-format-fix --- .../analysis/calculators/test_base.py | 1 + .../analysis/calculators/test_cryspy.py | 1 + .../analysis/fit_helpers/test_reporting.py | 1 + .../analysis/minimizers/test_factory.py | 1 + .../easydiffraction/analysis/test_analysis.py | 1 + .../analysis/test_analysis_access_params.py | 1 + .../analysis/test_analysis_show_empty.py | 1 + .../easydiffraction/analysis/test_fitting.py | 1 + .../easydiffraction/core/test_collection.py | 1 + .../easydiffraction/core/test_datablock.py | 1 + .../easydiffraction/core/test_identity.py | 1 + .../easydiffraction/core/test_validation.py | 1 + .../crystallography/test_crystallography.py | 1 + .../crystallography/test_space_groups.py | 1 + .../categories/instrument/test_base.py | 1 + .../experiment/categories/peak/test_cwl.py | 1 + .../categories/peak/test_tof_mixins.py | 6 +- .../categories/test_experiment_type.py | 1 + .../categories/test_linked_phases.py | 1 + .../datablocks/experiment/item/test_base.py | 1 + .../datablocks/experiment/item/test_enums.py | 1 + .../experiment/item/test_factory.py | 1 + .../datablocks/experiment/test_collection.py | 1 + .../display/plotters/test_base.py | 36 +++- .../display/plotters/test_plotly.py | 1 + .../easydiffraction/display/test_plotting.py | 17 +- .../easydiffraction/io/cif/test_handler.py | 1 + .../easydiffraction/io/cif/test_serialize.py | 1 + .../easydiffraction/project/test_project.py | 1 + .../project/test_project_info.py | 1 + .../test_project_load_and_summary_wrap.py | 1 + .../project/test_project_save.py | 1 + .../easydiffraction/summary/test_summary.py | 1 + .../summary/test_summary_details.py | 1 + .../easydiffraction/utils/test_logging.py | 1 + .../utils/test_theme_detect.py | 170 ++++++++---------- .../unit/easydiffraction/utils/test_utils.py | 1 + 37 files changed, 157 insertions(+), 105 deletions(-) diff --git a/tests/unit/easydiffraction/analysis/calculators/test_base.py b/tests/unit/easydiffraction/analysis/calculators/test_base.py index 6483af6e..004a266b 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_base.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_base.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.analysis.calculators.base as MUT diff --git a/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py b/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py index bccd63ec..d42514e4 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.analysis.calculators.cryspy as MUT diff --git a/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py b/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py index 458519ce..50a50cd6 100644 --- a/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py +++ b/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.analysis.fit_helpers.reporting as MUT diff --git a/tests/unit/easydiffraction/analysis/minimizers/test_factory.py b/tests/unit/easydiffraction/analysis/minimizers/test_factory.py index 236d1656..33c18236 100644 --- a/tests/unit/easydiffraction/analysis/minimizers/test_factory.py +++ b/tests/unit/easydiffraction/analysis/minimizers/test_factory.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_minimizer_factory_list_and_show(capsys): from easydiffraction.analysis.minimizers.factory import MinimizerFactory diff --git a/tests/unit/easydiffraction/analysis/test_analysis.py b/tests/unit/easydiffraction/analysis/test_analysis.py index 8323c8af..5ef51324 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis.py +++ b/tests/unit/easydiffraction/analysis/test_analysis.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.analysis.analysis as MUT diff --git a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py index 1edcb978..df8e490e 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py +++ b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_how_to_access_parameters_prints_paths_and_uids(capsys, monkeypatch): import easydiffraction.analysis.analysis as analysis_mod from easydiffraction.analysis.analysis import Analysis diff --git a/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py b/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py index 921127ba..e341e56e 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py +++ b/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_show_params_empty_branches(capsys): from easydiffraction.analysis.analysis import Analysis diff --git a/tests/unit/easydiffraction/analysis/test_fitting.py b/tests/unit/easydiffraction/analysis/test_fitting.py index d3a1b1fb..dc006bfa 100644 --- a/tests/unit/easydiffraction/analysis/test_fitting.py +++ b/tests/unit/easydiffraction/analysis/test_fitting.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.analysis.fitting as MUT diff --git a/tests/unit/easydiffraction/core/test_collection.py b/tests/unit/easydiffraction/core/test_collection.py index c940547c..b1831008 100644 --- a/tests/unit/easydiffraction/core/test_collection.py +++ b/tests/unit/easydiffraction/core/test_collection.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_collection_add_get_delete_and_names(): from easydiffraction.core.collection import CollectionBase from easydiffraction.core.identity import Identity diff --git a/tests/unit/easydiffraction/core/test_datablock.py b/tests/unit/easydiffraction/core/test_datablock.py index 4011197b..133ae596 100644 --- a/tests/unit/easydiffraction/core/test_datablock.py +++ b/tests/unit/easydiffraction/core/test_datablock.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_datablock_collection_add_and_filters_with_real_parameters(): from easydiffraction.core.category import CategoryItem from easydiffraction.core.datablock import DatablockCollection diff --git a/tests/unit/easydiffraction/core/test_identity.py b/tests/unit/easydiffraction/core/test_identity.py index 584135d7..69c5e73b 100644 --- a/tests/unit/easydiffraction/core/test_identity.py +++ b/tests/unit/easydiffraction/core/test_identity.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_identity_direct_and_parent_resolution(): from easydiffraction.core.identity import Identity diff --git a/tests/unit/easydiffraction/core/test_validation.py b/tests/unit/easydiffraction/core/test_validation.py index 70a92bb5..a0ff2e54 100644 --- a/tests/unit/easydiffraction/core/test_validation.py +++ b/tests/unit/easydiffraction/core/test_validation.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.core.validation as MUT diff --git a/tests/unit/easydiffraction/crystallography/test_crystallography.py b/tests/unit/easydiffraction/crystallography/test_crystallography.py index 3c2bf7df..f2968180 100644 --- a/tests/unit/easydiffraction/crystallography/test_crystallography.py +++ b/tests/unit/easydiffraction/crystallography/test_crystallography.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.crystallography.crystallography as MUT diff --git a/tests/unit/easydiffraction/crystallography/test_space_groups.py b/tests/unit/easydiffraction/crystallography/test_space_groups.py index 5629f633..0b663257 100644 --- a/tests/unit/easydiffraction/crystallography/test_space_groups.py +++ b/tests/unit/easydiffraction/crystallography/test_space_groups.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.crystallography.space_groups as MUT diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py index 70d36b8a..fff741db 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_instrument_base_sets_category_code(): from easydiffraction.datablocks.experiment.categories.instrument.base import InstrumentBase diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py index f4505287..854bc3cc 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_cwl_peak_classes_expose_expected_parameters_and_category(): from easydiffraction.datablocks.experiment.categories.peak.cwl import CwlPseudoVoigt from easydiffraction.datablocks.experiment.categories.peak.cwl import CwlSplitPseudoVoigt diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py index e8932153..f68e6fbf 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py @@ -11,7 +11,11 @@ def test_tof_broadening_and_asymmetry_mixins(): ) from easydiffraction.datablocks.experiment.categories.peak.tof_mixins import TofBroadeningMixin - class TofPeak(PeakBase, TofBroadeningMixin, IkedaCarpenterAsymmetryMixin,): + class TofPeak( + PeakBase, + TofBroadeningMixin, + IkedaCarpenterAsymmetryMixin, + ): def __init__(self): super().__init__() diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py index 169510ab..3fcf2650 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.datablocks.experiment.categories.experiment_type as MUT diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py index 263586f8..86d73aa5 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_linked_phases_add_and_cif_headers(): from easydiffraction.datablocks.experiment.categories.linked_phases import LinkedPhase from easydiffraction.datablocks.experiment.categories.linked_phases import LinkedPhases diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py index 6a6766ed..ac159b34 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.datablocks.experiment.item.base as MUT diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py index dff44c0f..8796876b 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.datablocks.experiment.item.enums as MUT diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py index fbce5523..deb9dd6b 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.datablocks.experiment.item.factory as MUT diff --git a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py index 589af777..87db1e56 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py +++ b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.datablocks.experiment.collection as MUT diff --git a/tests/unit/easydiffraction/display/plotters/test_base.py b/tests/unit/easydiffraction/display/plotters/test_base.py index 06e80057..af8d402f 100644 --- a/tests/unit/easydiffraction/display/plotters/test_base.py +++ b/tests/unit/easydiffraction/display/plotters/test_base.py @@ -35,10 +35,34 @@ def test_default_axes_labels_keys_present(): from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum # Powder Bragg - assert (SampleFormEnum.POWDER, ScatteringTypeEnum.BRAGG, pb.XAxisType.TWO_THETA) in pb.DEFAULT_AXES_LABELS - assert (SampleFormEnum.POWDER, ScatteringTypeEnum.BRAGG, pb.XAxisType.TIME_OF_FLIGHT) in pb.DEFAULT_AXES_LABELS - assert (SampleFormEnum.POWDER, ScatteringTypeEnum.BRAGG, pb.XAxisType.D_SPACING) in pb.DEFAULT_AXES_LABELS + assert ( + SampleFormEnum.POWDER, + ScatteringTypeEnum.BRAGG, + pb.XAxisType.TWO_THETA, + ) in pb.DEFAULT_AXES_LABELS + assert ( + SampleFormEnum.POWDER, + ScatteringTypeEnum.BRAGG, + pb.XAxisType.TIME_OF_FLIGHT, + ) in pb.DEFAULT_AXES_LABELS + assert ( + SampleFormEnum.POWDER, + ScatteringTypeEnum.BRAGG, + pb.XAxisType.D_SPACING, + ) in pb.DEFAULT_AXES_LABELS # Single crystal Bragg - assert (SampleFormEnum.SINGLE_CRYSTAL, ScatteringTypeEnum.BRAGG, pb.XAxisType.INTENSITY_CALC) in pb.DEFAULT_AXES_LABELS - assert (SampleFormEnum.SINGLE_CRYSTAL, ScatteringTypeEnum.BRAGG, pb.XAxisType.D_SPACING) in pb.DEFAULT_AXES_LABELS - assert (SampleFormEnum.SINGLE_CRYSTAL, ScatteringTypeEnum.BRAGG, pb.XAxisType.SIN_THETA_OVER_LAMBDA) in pb.DEFAULT_AXES_LABELS + assert ( + SampleFormEnum.SINGLE_CRYSTAL, + ScatteringTypeEnum.BRAGG, + pb.XAxisType.INTENSITY_CALC, + ) in pb.DEFAULT_AXES_LABELS + assert ( + SampleFormEnum.SINGLE_CRYSTAL, + ScatteringTypeEnum.BRAGG, + pb.XAxisType.D_SPACING, + ) in pb.DEFAULT_AXES_LABELS + assert ( + SampleFormEnum.SINGLE_CRYSTAL, + ScatteringTypeEnum.BRAGG, + pb.XAxisType.SIN_THETA_OVER_LAMBDA, + ) in pb.DEFAULT_AXES_LABELS diff --git a/tests/unit/easydiffraction/display/plotters/test_plotly.py b/tests/unit/easydiffraction/display/plotters/test_plotly.py index 06539f2b..3933d0ab 100644 --- a/tests/unit/easydiffraction/display/plotters/test_plotly.py +++ b/tests/unit/easydiffraction/display/plotters/test_plotly.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.display.plotters.plotly as MUT diff --git a/tests/unit/easydiffraction/display/test_plotting.py b/tests/unit/easydiffraction/display/test_plotting.py index 15caf344..4eb069ca 100644 --- a/tests/unit/easydiffraction/display/test_plotting.py +++ b/tests/unit/easydiffraction/display/test_plotting.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.display.plotting as MUT @@ -59,7 +60,9 @@ def test_plotter_error_paths_and_filtering(capsys): from easydiffraction.display.plotting import Plotter class Ptn: - def __init__(self, two_theta=None, intensity_meas=None, intensity_calc=None, d_spacing=None): + def __init__( + self, two_theta=None, intensity_meas=None, intensity_calc=None, d_spacing=None + ): self.two_theta = two_theta self.intensity_meas = intensity_meas self.intensity_calc = intensity_calc @@ -90,13 +93,19 @@ def __init__(self): out = capsys.readouterr().out assert 'No calculated data available for experiment E' in out - p.plot_meas_vs_calc(Ptn(two_theta=None, intensity_meas=None, intensity_calc=None), 'E', ExptType()) + p.plot_meas_vs_calc( + Ptn(two_theta=None, intensity_meas=None, intensity_calc=None), 'E', ExptType() + ) out = capsys.readouterr().out assert 'No measured data available for experiment E' in out - p.plot_meas_vs_calc(Ptn(two_theta=[1], intensity_meas=None, intensity_calc=[1]), 'E', ExptType()) + p.plot_meas_vs_calc( + Ptn(two_theta=[1], intensity_meas=None, intensity_calc=[1]), 'E', ExptType() + ) out = capsys.readouterr().out assert 'No measured data available for experiment E' in out - p.plot_meas_vs_calc(Ptn(two_theta=[1], intensity_meas=[1], intensity_calc=None), 'E', ExptType()) + p.plot_meas_vs_calc( + Ptn(two_theta=[1], intensity_meas=[1], intensity_calc=None), 'E', ExptType() + ) out = capsys.readouterr().out assert 'No calculated data available for experiment E' in out diff --git a/tests/unit/easydiffraction/io/cif/test_handler.py b/tests/unit/easydiffraction/io/cif/test_handler.py index ea31b833..42bbe3ca 100644 --- a/tests/unit/easydiffraction/io/cif/test_handler.py +++ b/tests/unit/easydiffraction/io/cif/test_handler.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_cif_handler_names_and_uid(): import easydiffraction.io.cif.handler as H diff --git a/tests/unit/easydiffraction/io/cif/test_serialize.py b/tests/unit/easydiffraction/io/cif/test_serialize.py index 48f044a1..910d2f49 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.io.cif.serialize as MUT diff --git a/tests/unit/easydiffraction/project/test_project.py b/tests/unit/easydiffraction/project/test_project.py index 078d59bc..fcd7f429 100644 --- a/tests/unit/easydiffraction/project/test_project.py +++ b/tests/unit/easydiffraction/project/test_project.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.project.project as MUT diff --git a/tests/unit/easydiffraction/project/test_project_info.py b/tests/unit/easydiffraction/project/test_project_info.py index ef8b061f..1e513c2f 100644 --- a/tests/unit/easydiffraction/project/test_project_info.py +++ b/tests/unit/easydiffraction/project/test_project_info.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.project.project_info as MUT diff --git a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py index 5cb103b8..d52956eb 100644 --- a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py +++ b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_project_load_prints_and_sets_path(tmp_path, capsys): import pytest diff --git a/tests/unit/easydiffraction/project/test_project_save.py b/tests/unit/easydiffraction/project/test_project_save.py index eb662dfc..e319155a 100644 --- a/tests/unit/easydiffraction/project/test_project_save.py +++ b/tests/unit/easydiffraction/project/test_project_save.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_project_save_uses_cwd_when_no_explicit_path(monkeypatch, tmp_path, capsys): # Default ProjectInfo.path is cwd; ensure save writes into a temp cwd, not repo root from easydiffraction.project.project import Project diff --git a/tests/unit/easydiffraction/summary/test_summary.py b/tests/unit/easydiffraction/summary/test_summary.py index c8c25f9e..e98471a6 100644 --- a/tests/unit/easydiffraction/summary/test_summary.py +++ b/tests/unit/easydiffraction/summary/test_summary.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_summary_as_cif_returns_placeholder_string(): from easydiffraction.summary.summary import Summary diff --git a/tests/unit/easydiffraction/summary/test_summary_details.py b/tests/unit/easydiffraction/summary/test_summary_details.py index 0e9fcf04..41f3aabd 100644 --- a/tests/unit/easydiffraction/summary/test_summary_details.py +++ b/tests/unit/easydiffraction/summary/test_summary_details.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_summary_crystallographic_and_experimental_sections(capsys): from easydiffraction.summary.summary import Summary diff --git a/tests/unit/easydiffraction/utils/test_logging.py b/tests/unit/easydiffraction/utils/test_logging.py index 8503e178..79da58c4 100644 --- a/tests/unit/easydiffraction/utils/test_logging.py +++ b/tests/unit/easydiffraction/utils/test_logging.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors # SPDX-License-Identifier: BSD-3-Clause + def test_module_import(): import easydiffraction.utils.logging as MUT diff --git a/tests/unit/easydiffraction/utils/test_theme_detect.py b/tests/unit/easydiffraction/utils/test_theme_detect.py index 0ff2a4ad..f52750e7 100644 --- a/tests/unit/easydiffraction/utils/test_theme_detect.py +++ b/tests/unit/easydiffraction/utils/test_theme_detect.py @@ -23,12 +23,7 @@ def test_dark_theme_detection(self, tmp_path: Path) -> None: ) settings_dir = ( - tmp_path - / '.jupyter' - / 'lab' - / 'user-settings' - / '@jupyterlab' - / 'apputils-extension' + tmp_path / '.jupyter' / 'lab' / 'user-settings' / '@jupyterlab' / 'apputils-extension' ) settings_dir.mkdir(parents=True) @@ -45,12 +40,7 @@ def test_light_theme_detection(self, tmp_path: Path) -> None: ) settings_dir = ( - tmp_path - / '.jupyter' - / 'lab' - / 'user-settings' - / '@jupyterlab' - / 'apputils-extension' + tmp_path / '.jupyter' / 'lab' / 'user-settings' / '@jupyterlab' / 'apputils-extension' ) settings_dir.mkdir(parents=True) @@ -76,12 +66,7 @@ def test_comments_in_settings(self, tmp_path: Path) -> None: ) settings_dir = ( - tmp_path - / '.jupyter' - / 'lab' - / 'user-settings' - / '@jupyterlab' - / 'apputils-extension' + tmp_path / '.jupyter' / 'lab' / 'user-settings' / '@jupyterlab' / 'apputils-extension' ) settings_dir.mkdir(parents=True) @@ -119,9 +104,7 @@ def test_vscode_dark_theme(self, tmp_path: Path) -> None: vscode_dir.mkdir() settings_file = vscode_dir / 'settings.json' - settings_file.write_text( - json.dumps({'workbench.colorTheme': 'One Dark Pro'}) - ) + settings_file.write_text(json.dumps({'workbench.colorTheme': 'One Dark Pro'})) with mock.patch.dict(os.environ, {'VSCODE_PID': '12345'}): with mock.patch.object(Path, 'cwd', return_value=tmp_path): @@ -137,9 +120,7 @@ def test_vscode_light_theme(self, tmp_path: Path) -> None: vscode_dir.mkdir() settings_file = vscode_dir / 'settings.json' - settings_file.write_text( - json.dumps({'workbench.colorTheme': 'Light+ (default light)'}) - ) + settings_file.write_text(json.dumps({'workbench.colorTheme': 'Light+ (default light)'})) with mock.patch.dict(os.environ, {'VSCODE_PID': '12345'}): with mock.patch.object(Path, 'cwd', return_value=tmp_path): @@ -160,9 +141,7 @@ def test_vscode_nls_config_dark(self) -> None: class TestCheckSystemPreferences: """Tests for _check_system_preferences function.""" - @pytest.mark.skipif( - not os.sys.platform.startswith('darwin'), reason='macOS only test' - ) + @pytest.mark.skipif(not os.sys.platform.startswith('darwin'), reason='macOS only test') def test_macos_dark_mode(self) -> None: """Test macOS dark mode detection.""" from easydiffraction.utils._vendored.jupyter_dark_detect.detector import ( @@ -174,9 +153,7 @@ def test_macos_dark_mode(self) -> None: mock_run.return_value.stdout = 'Dark' assert _check_system_preferences() is True - @pytest.mark.skipif( - not os.sys.platform.startswith('darwin'), reason='macOS only test' - ) + @pytest.mark.skipif(not os.sys.platform.startswith('darwin'), reason='macOS only test') def test_macos_light_mode(self) -> None: """Test macOS light mode detection.""" from easydiffraction.utils._vendored.jupyter_dark_detect.detector import ( @@ -195,22 +172,23 @@ def test_default_to_false(self) -> None: """Test that is_dark defaults to False when no detection works.""" from easydiffraction.utils._vendored.theme_detect import is_dark - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_jupyterlab_settings', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_javascript_detection', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_system_preferences', - return_value=None, + with ( + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_jupyterlab_settings', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_vscode_settings', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_javascript_detection', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_system_preferences', + return_value=None, + ), ): assert is_dark() is False @@ -218,14 +196,15 @@ def test_jupyterlab_priority(self) -> None: """Test that JupyterLab settings take priority.""" from easydiffraction.utils._vendored.theme_detect import is_dark - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_jupyterlab_settings', - return_value=True, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=False, + with ( + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_jupyterlab_settings', + return_value=True, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_vscode_settings', + return_value=False, + ), ): assert is_dark() is True @@ -233,14 +212,15 @@ def test_vscode_second_priority(self) -> None: """Test that VS Code settings are checked after JupyterLab.""" from easydiffraction.utils._vendored.theme_detect import is_dark - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_jupyterlab_settings', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=True, + with ( + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_jupyterlab_settings', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_vscode_settings', + return_value=True, + ), ): assert is_dark() is True @@ -248,22 +228,23 @@ def test_javascript_before_system(self) -> None: """Test that JS detection comes before system preferences.""" from easydiffraction.utils._vendored.theme_detect import is_dark - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_jupyterlab_settings', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_javascript_detection', - return_value=True, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_system_preferences', - return_value=False, + with ( + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_jupyterlab_settings', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_vscode_settings', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_javascript_detection', + return_value=True, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_system_preferences', + return_value=False, + ), ): # JS detection should win over system prefs assert is_dark() is True @@ -276,22 +257,23 @@ def test_returns_dict_with_all_methods(self) -> None: """Test that get_detection_result returns all detection methods.""" from easydiffraction.utils._vendored.theme_detect import get_detection_result - with mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_jupyterlab_settings', - return_value=True, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_vscode_settings', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_javascript_detection', - return_value=None, - ), mock.patch( - 'easydiffraction.utils._vendored.theme_detect.' - '_check_system_preferences', - return_value=False, + with ( + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_jupyterlab_settings', + return_value=True, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_vscode_settings', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_javascript_detection', + return_value=None, + ), + mock.patch( + 'easydiffraction.utils._vendored.theme_detect._check_system_preferences', + return_value=False, + ), ): result = get_detection_result() diff --git a/tests/unit/easydiffraction/utils/test_utils.py b/tests/unit/easydiffraction/utils/test_utils.py index f1691c7a..472584c1 100644 --- a/tests/unit/easydiffraction/utils/test_utils.py +++ b/tests/unit/easydiffraction/utils/test_utils.py @@ -128,6 +128,7 @@ def test_is_pycharm_and_is_colab(monkeypatch): def test_render_table_terminal_branch(capsys, monkeypatch): import easydiffraction.utils.utils as MUT + # Ensure non-notebook rendering; on CI/default env it's terminal anyway. MUT.render_table( columns_data=[[1, 2], [3, 4]], From 0dc2e9bc3f10a1b606981daddf66759ae709c332 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 25 Mar 2026 22:23:23 +0100 Subject: [PATCH 06/48] Temporarily disable pydoclint until we are ready --- pixi.lock | 4 ++-- pyproject.toml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pixi.lock b/pixi.lock index ff21006c..f5900782 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4883,8 +4883,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty6 - sha256: 377f0443a16b4d4edf0c288797da06462da5f22eae7dbe0148832cac2610eb01 + version: 0.10.2+devdirty8 + sha256: 91dc01dcc37a9ea9fbbb3aeb46f055f2caf4a4b0ad81f0b2d320988a0a9a55c9 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index e5107fea..473eeece 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -279,7 +279,7 @@ ignore = [ 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc # Temporary: - 'DTZ005', + 'DTZ005', ] @@ -359,6 +359,7 @@ max-doc-length = 99 # the parameter declarations in the code (in function's signature). [tool.pydoclint] +exclude = '\.' # Temporarily disable pydoclint until we are ready style = "google" check-style-mismatch = true check-arg-defaults = true From 48f8ca9e62f58891d99d426659a4cb3f16487b5c Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 00:00:51 +0100 Subject: [PATCH 07/48] Update license headers --- pixi.lock | 2 +- pixi.toml | 6 +- src/easydiffraction/__init__.py | 2 +- src/easydiffraction/__main__.py | 2 +- src/easydiffraction/analysis/__init__.py | 2 +- src/easydiffraction/analysis/analysis.py | 2 +- .../analysis/calculators/__init__.py | 2 +- .../analysis/calculators/base.py | 2 +- .../analysis/calculators/crysfml.py | 2 +- .../analysis/calculators/cryspy.py | 2 +- .../analysis/calculators/factory.py | 2 +- .../analysis/calculators/pdffit.py | 2 +- .../analysis/categories/__init__.py | 2 +- .../analysis/categories/aliases/__init__.py | 2 +- .../analysis/categories/aliases/default.py | 2 +- .../analysis/categories/aliases/factory.py | 2 +- .../categories/constraints/__init__.py | 2 +- .../categories/constraints/default.py | 2 +- .../categories/constraints/factory.py | 2 +- .../analysis/categories/fit_mode/__init__.py | 2 +- .../analysis/categories/fit_mode/enums.py | 2 +- .../analysis/categories/fit_mode/factory.py | 2 +- .../analysis/categories/fit_mode/fit_mode.py | 2 +- .../joint_fit_experiments/__init__.py | 2 +- .../joint_fit_experiments/default.py | 2 +- .../joint_fit_experiments/factory.py | 2 +- .../analysis/fit_helpers/__init__.py | 2 +- .../analysis/fit_helpers/metrics.py | 2 +- .../analysis/fit_helpers/reporting.py | 2 +- .../analysis/fit_helpers/tracking.py | 2 +- src/easydiffraction/analysis/fitting.py | 2 +- .../analysis/minimizers/__init__.py | 2 +- .../analysis/minimizers/base.py | 2 +- .../analysis/minimizers/dfols.py | 2 +- .../analysis/minimizers/factory.py | 2 +- .../analysis/minimizers/lmfit.py | 2 +- src/easydiffraction/core/__init__.py | 2 +- src/easydiffraction/core/category.py | 2 +- src/easydiffraction/core/collection.py | 2 +- src/easydiffraction/core/datablock.py | 2 +- src/easydiffraction/core/diagnostic.py | 2 +- src/easydiffraction/core/factory.py | 2 +- src/easydiffraction/core/guard.py | 2 +- src/easydiffraction/core/identity.py | 2 +- src/easydiffraction/core/metadata.py | 2 +- src/easydiffraction/core/singleton.py | 2 +- src/easydiffraction/core/validation.py | 2 +- src/easydiffraction/core/variable.py | 2 +- .../crystallography/__init__.py | 2 +- .../crystallography/crystallography.py | 2 +- .../crystallography/space_groups.py | 2 +- src/easydiffraction/datablocks/__init__.py | 2 +- .../datablocks/experiment/__init__.py | 2 +- .../experiment/categories/__init__.py | 2 +- .../categories/background/__init__.py | 2 +- .../experiment/categories/background/base.py | 2 +- .../categories/background/chebyshev.py | 2 +- .../experiment/categories/background/enums.py | 2 +- .../categories/background/factory.py | 2 +- .../categories/background/line_segment.py | 2 +- .../experiment/categories/data/__init__.py | 2 +- .../experiment/categories/data/bragg_pd.py | 2 +- .../experiment/categories/data/bragg_sc.py | 2 +- .../experiment/categories/data/factory.py | 2 +- .../experiment/categories/data/total_pd.py | 2 +- .../categories/excluded_regions/__init__.py | 2 +- .../categories/excluded_regions/default.py | 2 +- .../categories/excluded_regions/factory.py | 2 +- .../categories/experiment_type/__init__.py | 2 +- .../categories/experiment_type/default.py | 2 +- .../categories/experiment_type/factory.py | 2 +- .../categories/extinction/__init__.py | 2 +- .../categories/extinction/factory.py | 2 +- .../experiment/categories/extinction/shelx.py | 2 +- .../categories/instrument/__init__.py | 2 +- .../experiment/categories/instrument/base.py | 2 +- .../experiment/categories/instrument/cwl.py | 2 +- .../categories/instrument/factory.py | 2 +- .../experiment/categories/instrument/tof.py | 2 +- .../categories/linked_crystal/__init__.py | 2 +- .../categories/linked_crystal/default.py | 2 +- .../categories/linked_crystal/factory.py | 2 +- .../categories/linked_phases/__init__.py | 2 +- .../categories/linked_phases/default.py | 2 +- .../categories/linked_phases/factory.py | 2 +- .../experiment/categories/peak/__init__.py | 2 +- .../experiment/categories/peak/base.py | 2 +- .../experiment/categories/peak/cwl.py | 2 +- .../experiment/categories/peak/cwl_mixins.py | 2 +- .../experiment/categories/peak/factory.py | 2 +- .../experiment/categories/peak/tof.py | 2 +- .../experiment/categories/peak/tof_mixins.py | 2 +- .../experiment/categories/peak/total.py | 2 +- .../categories/peak/total_mixins.py | 2 +- .../datablocks/experiment/collection.py | 2 +- .../datablocks/experiment/item/__init__.py | 2 +- .../datablocks/experiment/item/base.py | 2 +- .../datablocks/experiment/item/bragg_pd.py | 2 +- .../datablocks/experiment/item/bragg_sc.py | 2 +- .../datablocks/experiment/item/enums.py | 2 +- .../datablocks/experiment/item/factory.py | 2 +- .../datablocks/experiment/item/total_pd.py | 2 +- .../datablocks/structure/__init__.py | 2 +- .../structure/categories/__init__.py | 2 +- .../categories/atom_sites/__init__.py | 2 +- .../categories/atom_sites/default.py | 2 +- .../categories/atom_sites/factory.py | 2 +- .../structure/categories/cell/__init__.py | 2 +- .../structure/categories/cell/default.py | 2 +- .../structure/categories/cell/factory.py | 2 +- .../categories/space_group/__init__.py | 2 +- .../categories/space_group/default.py | 2 +- .../categories/space_group/factory.py | 2 +- .../datablocks/structure/collection.py | 2 +- .../datablocks/structure/item/__init__.py | 2 +- .../datablocks/structure/item/base.py | 2 +- .../datablocks/structure/item/factory.py | 2 +- src/easydiffraction/display/__init__.py | 2 +- src/easydiffraction/display/base.py | 2 +- .../display/plotters/__init__.py | 2 +- src/easydiffraction/display/plotters/ascii.py | 2 +- src/easydiffraction/display/plotters/base.py | 2 +- .../display/plotters/plotly.py | 2 +- src/easydiffraction/display/plotting.py | 2 +- .../display/tablers/__init__.py | 2 +- src/easydiffraction/display/tablers/base.py | 2 +- src/easydiffraction/display/tablers/pandas.py | 2 +- src/easydiffraction/display/tablers/rich.py | 2 +- src/easydiffraction/display/tables.py | 2 +- src/easydiffraction/display/utils.py | 2 +- src/easydiffraction/io/__init__.py | 2 +- src/easydiffraction/io/cif/__init__.py | 2 +- src/easydiffraction/io/cif/handler.py | 2 +- src/easydiffraction/io/cif/parse.py | 2 +- src/easydiffraction/io/cif/serialize.py | 2 +- src/easydiffraction/project/__init__.py | 2 +- src/easydiffraction/project/project.py | 2 +- src/easydiffraction/project/project_info.py | 2 +- src/easydiffraction/summary/__init__.py | 2 +- src/easydiffraction/summary/summary.py | 2 +- src/easydiffraction/utils/__init__.py | 2 +- .../utils/_vendored/__init__.py | 2 +- .../utils/_vendored/theme_detect.py | 2 +- src/easydiffraction/utils/environment.py | 2 +- src/easydiffraction/utils/logging.py | 2 +- src/easydiffraction/utils/utils.py | 2 +- tests/integration/fitting/test_multi.py | 2 +- .../test_pair-distribution-function.py | 2 +- ..._powder-diffraction_constant-wavelength.py | 2 +- .../test_powder-diffraction_joint-fit.py | 2 +- .../test_powder-diffraction_time-of-flight.py | 2 +- .../test_single-crystal-diffraction.py | 2 +- .../scipp-analysis/dream/conftest.py | 3 +- .../dream/test_analyze_reduced_data.py | 3 +- .../dream/test_package_import.py | 3 +- .../dream/test_read_reduced_data.py | 3 +- .../dream/test_validate_meta_data.py | 3 +- .../dream/test_validate_physical_data.py | 3 +- .../analysis/calculators/test_base.py | 2 +- .../analysis/calculators/test_crysfml.py | 2 +- .../analysis/calculators/test_cryspy.py | 2 +- .../analysis/calculators/test_factory.py | 2 +- .../analysis/calculators/test_pdffit.py | 2 +- .../analysis/categories/test_aliases.py | 2 +- .../analysis/categories/test_constraints.py | 2 +- .../categories/test_joint_fit_experiments.py | 2 +- .../analysis/fit_helpers/test_metrics.py | 2 +- .../analysis/fit_helpers/test_reporting.py | 2 +- .../analysis/fit_helpers/test_tracking.py | 2 +- .../analysis/minimizers/test_base.py | 2 +- .../analysis/minimizers/test_dfols.py | 2 +- .../analysis/minimizers/test_factory.py | 2 +- .../analysis/minimizers/test_lmfit.py | 2 +- .../easydiffraction/analysis/test_analysis.py | 2 +- .../analysis/test_analysis_access_params.py | 2 +- .../analysis/test_analysis_show_empty.py | 2 +- .../easydiffraction/analysis/test_fitting.py | 2 +- .../easydiffraction/core/test_category.py | 2 +- .../easydiffraction/core/test_collection.py | 2 +- .../easydiffraction/core/test_datablock.py | 2 +- .../easydiffraction/core/test_diagnostic.py | 2 +- .../unit/easydiffraction/core/test_factory.py | 5 +- tests/unit/easydiffraction/core/test_guard.py | 2 +- .../easydiffraction/core/test_identity.py | 2 +- .../easydiffraction/core/test_parameters.py | 2 +- .../easydiffraction/core/test_singletons.py | 2 +- .../easydiffraction/core/test_validation.py | 2 +- .../crystallography/test_crystallography.py | 2 +- .../crystallography/test_space_groups.py | 2 +- .../categories/background/test_base.py | 2 +- .../categories/background/test_chebyshev.py | 2 +- .../categories/background/test_enums.py | 2 +- .../categories/background/test_factory.py | 2 +- .../background/test_line_segment.py | 2 +- .../categories/data/test_bragg_pd.py | 2 +- .../categories/data/test_bragg_sc.py | 2 +- .../categories/data/test_factory.py | 2 +- .../categories/data/test_total_pd.py | 2 +- .../categories/instrument/test_base.py | 2 +- .../categories/instrument/test_cwl.py | 2 +- .../categories/instrument/test_factory.py | 2 +- .../categories/instrument/test_tof.py | 2 +- .../experiment/categories/peak/test_base.py | 2 +- .../experiment/categories/peak/test_cwl.py | 2 +- .../categories/peak/test_cwl_mixins.py | 2 +- .../categories/peak/test_factory.py | 2 +- .../experiment/categories/peak/test_tof.py | 2 +- .../categories/peak/test_tof_mixins.py | 2 +- .../experiment/categories/peak/test_total.py | 2 +- .../categories/peak/test_total_mixins.py | 2 +- .../categories/test_excluded_regions.py | 2 +- .../categories/test_experiment_type.py | 2 +- .../experiment/categories/test_extinction.py | 2 +- .../categories/test_linked_crystal.py | 2 +- .../categories/test_linked_phases.py | 2 +- .../datablocks/experiment/item/test_base.py | 2 +- .../experiment/item/test_bragg_pd.py | 2 +- .../experiment/item/test_bragg_sc.py | 2 +- .../datablocks/experiment/item/test_enums.py | 2 +- .../experiment/item/test_factory.py | 2 +- .../experiment/item/test_total_pd.py | 2 +- .../datablocks/experiment/test_collection.py | 2 +- .../structure/categories/test_space_group.py | 2 +- .../datablocks/structure/item/test_base.py | 2 +- .../datablocks/structure/item/test_factory.py | 2 +- .../datablocks/structure/test_collection.py | 2 +- .../display/plotters/test_ascii.py | 2 +- .../display/plotters/test_base.py | 2 +- .../display/plotters/test_plotly.py | 2 +- .../easydiffraction/display/test_plotting.py | 2 +- .../easydiffraction/io/cif/test_handler.py | 2 +- .../easydiffraction/io/cif/test_serialize.py | 2 +- .../io/cif/test_serialize_more.py | 2 +- .../easydiffraction/project/test_project.py | 2 +- .../project/test_project_info.py | 2 +- .../test_project_load_and_summary_wrap.py | 2 +- .../project/test_project_save.py | 2 +- .../easydiffraction/summary/test_summary.py | 2 +- .../summary/test_summary_details.py | 2 +- tests/unit/easydiffraction/test___init__.py | 3 +- tests/unit/easydiffraction/test___main__.py | 2 +- .../easydiffraction/utils/test_logging.py | 2 +- .../utils/test_theme_detect.py | 2 +- .../unit/easydiffraction/utils/test_utils.py | 2 +- tools/add_license_headers.py | 151 --------- tools/check_license_headers.py | 45 --- tools/license_headers.py | 315 ++++++++++++++++++ tools/remove_license_headers.py | 41 --- tools/update_spdx.py | 109 ------ 249 files changed, 567 insertions(+), 596 deletions(-) delete mode 100644 tools/add_license_headers.py delete mode 100644 tools/check_license_headers.py create mode 100644 tools/license_headers.py delete mode 100644 tools/remove_license_headers.py delete mode 100644 tools/update_spdx.py diff --git a/pixi.lock b/pixi.lock index f5900782..6afb88dc 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4883,7 +4883,7 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty8 + version: 0.10.2+devdirty9 sha256: 91dc01dcc37a9ea9fbbb3aeb46f055f2caf4a4b0ad81f0b2d320988a0a9a55c9 requires_dist: - asciichartpy diff --git a/pixi.toml b/pixi.toml index 859b89e7..58a61809 100644 --- a/pixi.toml +++ b/pixi.toml @@ -244,9 +244,9 @@ github-labels = 'python tools/update_github_labels.py' # ⚖️ SPDX License Headers ######################### -license-remove = 'python tools/remove_license_headers.py src/ tests/' -license-add = 'python tools/add_license_headers.py src/ tests/' -license-check = 'python tools/check_license_headers.py src/ tests/' +license-remove = 'python tools/license_headers.py remove src/ tests/ --exclude-from-pyproject-toml tool.ruff.exclude' +license-add = 'python tools/license_headers.py add src/ tests/ --exclude-from-pyproject-toml tool.ruff.exclude' +license-check = 'python tools/license_headers.py check src/ tests/ --exclude-from-pyproject-toml tool.ruff.exclude' #################################### # 🚀 Other Development & Build Tasks diff --git a/src/easydiffraction/__init__.py b/src/easydiffraction/__init__.py index d15d6682..e2fb3c4e 100644 --- a/src/easydiffraction/__init__.py +++ b/src/easydiffraction/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory diff --git a/src/easydiffraction/__main__.py b/src/easydiffraction/__main__.py index c850b1fa..33fb4dcd 100644 --- a/src/easydiffraction/__main__.py +++ b/src/easydiffraction/__main__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import sys diff --git a/src/easydiffraction/analysis/__init__.py b/src/easydiffraction/analysis/__init__.py index 429f2648..78150ea5 100644 --- a/src/easydiffraction/analysis/__init__.py +++ b/src/easydiffraction/analysis/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index bf403247..4ddde133 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import List diff --git a/src/easydiffraction/analysis/calculators/__init__.py b/src/easydiffraction/analysis/calculators/__init__.py index 5874b1a4..38bd1aa0 100644 --- a/src/easydiffraction/analysis/calculators/__init__.py +++ b/src/easydiffraction/analysis/calculators/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.calculators.crysfml import CrysfmlCalculator diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index 5ebd3086..d566f735 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from abc import ABC diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 5d47300e..92ac3d9d 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import Any diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index ad09dc2a..967189a1 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import contextlib diff --git a/src/easydiffraction/analysis/calculators/factory.py b/src/easydiffraction/analysis/calculators/factory.py index fa3812da..fac3b570 100644 --- a/src/easydiffraction/analysis/calculators/factory.py +++ b/src/easydiffraction/analysis/calculators/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Calculator factory — delegates to ``FactoryBase``. diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py index debd90de..ecb4ee8f 100644 --- a/src/easydiffraction/analysis/calculators/pdffit.py +++ b/src/easydiffraction/analysis/calculators/pdffit.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """PDF calculation backend using diffpy.pdffit2 if available. diff --git a/src/easydiffraction/analysis/categories/__init__.py b/src/easydiffraction/analysis/categories/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/analysis/categories/__init__.py +++ b/src/easydiffraction/analysis/categories/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/analysis/categories/aliases/__init__.py b/src/easydiffraction/analysis/categories/aliases/__init__.py index aa72de6b..6ca3a859 100644 --- a/src/easydiffraction/analysis/categories/aliases/__init__.py +++ b/src/easydiffraction/analysis/categories/aliases/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.categories.aliases.default import Alias diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 49b263f4..1b1cb77f 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Alias category for mapping friendly names to parameter UIDs. diff --git a/src/easydiffraction/analysis/categories/aliases/factory.py b/src/easydiffraction/analysis/categories/aliases/factory.py index 07e1fe38..f2bebe43 100644 --- a/src/easydiffraction/analysis/categories/aliases/factory.py +++ b/src/easydiffraction/analysis/categories/aliases/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Aliases factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/analysis/categories/constraints/__init__.py b/src/easydiffraction/analysis/categories/constraints/__init__.py index ded70ca6..97d8c03c 100644 --- a/src/easydiffraction/analysis/categories/constraints/__init__.py +++ b/src/easydiffraction/analysis/categories/constraints/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.categories.constraints.default import Constraint diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 647dd273..b7ca9139 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Simple symbolic constraint between parameters. diff --git a/src/easydiffraction/analysis/categories/constraints/factory.py b/src/easydiffraction/analysis/categories/constraints/factory.py index 829260f4..682c9684 100644 --- a/src/easydiffraction/analysis/categories/constraints/factory.py +++ b/src/easydiffraction/analysis/categories/constraints/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Constraints factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/analysis/categories/fit_mode/__init__.py b/src/easydiffraction/analysis/categories/fit_mode/__init__.py index 45267810..058d054c 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/__init__.py +++ b/src/easydiffraction/analysis/categories/fit_mode/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.categories.fit_mode.enums import FitModeEnum diff --git a/src/easydiffraction/analysis/categories/fit_mode/enums.py b/src/easydiffraction/analysis/categories/fit_mode/enums.py index 156e9c30..7f11b33b 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/enums.py +++ b/src/easydiffraction/analysis/categories/fit_mode/enums.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Enumeration for fit-mode values.""" diff --git a/src/easydiffraction/analysis/categories/fit_mode/factory.py b/src/easydiffraction/analysis/categories/fit_mode/factory.py index 48edef66..f10485f8 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/factory.py +++ b/src/easydiffraction/analysis/categories/fit_mode/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Fit-mode factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index 8a5bd24b..7e473738 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Fit-mode category item. diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/__init__.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/__init__.py index 2857b28d..1aa8f0ae 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/__init__.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.categories.joint_fit_experiments.default import JointFitExperiment diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index 6acf4f44..6c5b1db5 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Joint-fit experiment weighting configuration. diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py index 2919c741..897a3746 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Joint-fit-experiments factory — delegates entirely to ``FactoryBase``. diff --git a/src/easydiffraction/analysis/fit_helpers/__init__.py b/src/easydiffraction/analysis/fit_helpers/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/analysis/fit_helpers/__init__.py +++ b/src/easydiffraction/analysis/fit_helpers/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index 97c715d8..cc267102 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import Optional diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index d9dbc40e..895cdeb6 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import Any diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index 438fd46d..5bf99e60 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import time diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index 453aaf1a..d33c0ed2 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import TYPE_CHECKING diff --git a/src/easydiffraction/analysis/minimizers/__init__.py b/src/easydiffraction/analysis/minimizers/__init__.py index 8a41a6f2..01fbc136 100644 --- a/src/easydiffraction/analysis/minimizers/__init__.py +++ b/src/easydiffraction/analysis/minimizers/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.minimizers.dfols import DfolsMinimizer diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index 069601ed..eda5b649 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from abc import ABC diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index b68a034a..9ceefe5b 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import Any diff --git a/src/easydiffraction/analysis/minimizers/factory.py b/src/easydiffraction/analysis/minimizers/factory.py index e12a9533..18f67cc6 100644 --- a/src/easydiffraction/analysis/minimizers/factory.py +++ b/src/easydiffraction/analysis/minimizers/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Minimizer factory — delegates to ``FactoryBase``.""" diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index cb0a59fd..0d7aa83b 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import Any diff --git a/src/easydiffraction/core/__init__.py b/src/easydiffraction/core/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/core/__init__.py +++ b/src/easydiffraction/core/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index d4b57dc3..883aec56 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index 164f3d77..aac21cf8 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Lightweight container for guarded items with name-based indexing. diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 221845b6..0fdb452b 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/core/diagnostic.py b/src/easydiffraction/core/diagnostic.py index 74c2ab3b..4a1bd9a2 100644 --- a/src/easydiffraction/core/diagnostic.py +++ b/src/easydiffraction/core/diagnostic.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Diagnostics helpers for logging validation messages. diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index 8e699085..6aed086a 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Base factory with registration, lookup, and context-dependent defaults. diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index a0033d14..a6f1242c 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/core/identity.py b/src/easydiffraction/core/identity.py index 5848bf18..2f57c163 100644 --- a/src/easydiffraction/core/identity.py +++ b/src/easydiffraction/core/identity.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Identity helpers to build CIF-like hierarchical names. diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index 4a820515..a1ad457c 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Metadata dataclasses for factory-created classes. diff --git a/src/easydiffraction/core/singleton.py b/src/easydiffraction/core/singleton.py index 10af4667..7644bc39 100644 --- a/src/easydiffraction/core/singleton.py +++ b/src/easydiffraction/core/singleton.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import Any diff --git a/src/easydiffraction/core/validation.py b/src/easydiffraction/core/validation.py index 1fd268d9..1adf6a12 100644 --- a/src/easydiffraction/core/validation.py +++ b/src/easydiffraction/core/validation.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Lightweight runtime validation utilities. diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 6b33a39e..fa0c26ec 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/crystallography/__init__.py b/src/easydiffraction/crystallography/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/crystallography/__init__.py +++ b/src/easydiffraction/crystallography/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/crystallography/crystallography.py b/src/easydiffraction/crystallography/crystallography.py index c7ff6203..529a3221 100644 --- a/src/easydiffraction/crystallography/crystallography.py +++ b/src/easydiffraction/crystallography/crystallography.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typing import Any diff --git a/src/easydiffraction/crystallography/space_groups.py b/src/easydiffraction/crystallography/space_groups.py index 114b3467..12e35984 100644 --- a/src/easydiffraction/crystallography/space_groups.py +++ b/src/easydiffraction/crystallography/space_groups.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Space group reference data. diff --git a/src/easydiffraction/datablocks/__init__.py b/src/easydiffraction/datablocks/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/datablocks/__init__.py +++ b/src/easydiffraction/datablocks/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/datablocks/experiment/__init__.py b/src/easydiffraction/datablocks/experiment/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/datablocks/experiment/__init__.py +++ b/src/easydiffraction/datablocks/experiment/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/datablocks/experiment/categories/__init__.py b/src/easydiffraction/datablocks/experiment/categories/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/datablocks/experiment/categories/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/datablocks/experiment/categories/background/__init__.py b/src/easydiffraction/datablocks/experiment/categories/background/__init__.py index b7b3b47d..7ffe8f22 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.background.chebyshev import ( diff --git a/src/easydiffraction/datablocks/experiment/categories/background/base.py b/src/easydiffraction/datablocks/experiment/categories/background/base.py index 78cc5ef1..5f392532 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 4a6a714d..6173eba5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Chebyshev polynomial background model. diff --git a/src/easydiffraction/datablocks/experiment/categories/background/enums.py b/src/easydiffraction/datablocks/experiment/categories/background/enums.py index 2356702a..9e78effc 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/enums.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/enums.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Enumerations for background model types.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/background/factory.py b/src/easydiffraction/datablocks/experiment/categories/background/factory.py index c52b7dd7..c4d300c8 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Background factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index 822f6e0d..23113b26 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Line-segment background model. diff --git a/src/easydiffraction/datablocks/experiment/categories/data/__init__.py b/src/easydiffraction/datablocks/experiment/categories/data/__init__.py index c228ecd8..3599f3b5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.data.bragg_pd import PdCwlData diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 73402d30..8ecf2bb9 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 39552b63..497c13f5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/categories/data/factory.py b/src/easydiffraction/datablocks/experiment/categories/data/factory.py index 1ef25c0b..d8cdcf12 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Data collection factory — delegates to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index 0aa63be5..e819bea7 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Data categories for total scattering (PDF) experiments.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/__init__.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/__init__.py index 3356f4cf..8d232629 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.excluded_regions.default import ( diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 2696f25c..0a45a8dd 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Exclude ranges of x from fitting/plotting (masked regions).""" diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py index 789e25e7..7fa9bf08 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Excluded-regions factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/__init__.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/__init__.py index 63e6bb0b..197b5510 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.experiment_type.default import ExperimentType diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 5221e444..88693e14 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Experiment type descriptor (form, beam, probe, scattering). diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/factory.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/factory.py index bf78fb53..05f0d2d9 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Experiment-type factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/__init__.py b/src/easydiffraction/datablocks/experiment/categories/extinction/__init__.py index f3d62fad..3e6fa39a 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.extinction.shelx import ShelxExtinction diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/factory.py b/src/easydiffraction/datablocks/experiment/categories/extinction/factory.py index fbeb32e7..4e4bd9ed 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Extinction factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index 67dfaf94..b4a81a80 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Shelx-style isotropic extinction correction.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/__init__.py b/src/easydiffraction/datablocks/experiment/categories/instrument/__init__.py index e4a03696..d12b40c3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.instrument.cwl import CwlPdInstrument diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/base.py b/src/easydiffraction/datablocks/experiment/categories/instrument/base.py index 0d1c04d5..d58dd059 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Instrument category base definitions for CWL/TOF instruments. diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 924158a0..0c78f435 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.core.metadata import CalculatorSupport diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/factory.py b/src/easydiffraction/datablocks/experiment/categories/instrument/factory.py index 7d4286af..fce8ad5c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Instrument factory — delegates to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index efbff40d..33fccab3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.core.metadata import CalculatorSupport diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py index 1a6b0b67..4b93121b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.linked_crystal.default import LinkedCrystal diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index e1fa8b6a..763b6481 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Default linked-crystal reference (id + scale).""" diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py index 49ac1a64..b34b8073 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Linked-crystal factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py index 6dd96b94..dda7d445 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.linked_phases.default import LinkedPhase diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index 067683a9..c95638d1 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Linked phases allow combining phases with scale factors.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py index 74f16616..56970ee8 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Linked-phases factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/__init__.py b/src/easydiffraction/datablocks/experiment/categories/peak/__init__.py index b335b9d3..667bafb4 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/__init__.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.peak.cwl import CwlPseudoVoigt diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/base.py b/src/easydiffraction/datablocks/experiment/categories/peak/base.py index 5f2654a7..050411b1 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Base class for peak profile categories.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py index aa03c80c..4bb036eb 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Constant-wavelength peak profile classes.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 2bc9c178..29f629b7 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Constant-wavelength (CWL) peak-profile component classes. diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/factory.py b/src/easydiffraction/datablocks/experiment/categories/peak/factory.py index 1992b633..ca196748 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Peak profile factory — delegates to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py index 1c70b65b..2ee2a579 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Time-of-flight peak profile classes.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index 01a10b26..be942a97 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Time-of-flight (TOF) peak-profile component classes. diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total.py b/src/easydiffraction/datablocks/experiment/categories/peak/total.py index a1166c1a..61d5e6fd 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Total-scattering (PDF) peak profile classes.""" diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 139e37dd..2101ec5e 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Total scattering / pair distribution function (PDF) peak-profile component classes. diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index 854b77ad..4f690399 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Collection of experiment data blocks.""" diff --git a/src/easydiffraction/datablocks/experiment/item/__init__.py b/src/easydiffraction/datablocks/experiment/item/__init__.py index ffe5775d..35a64fb1 100644 --- a/src/easydiffraction/datablocks/experiment/item/__init__.py +++ b/src/easydiffraction/datablocks/experiment/item/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.item.base import ExperimentBase diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 24c6ee6e..8672f673 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Base classes for experiment datablock items.""" diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index 258085c5..5434a531 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py index e8142e0e..e4db951f 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/item/enums.py b/src/easydiffraction/datablocks/experiment/item/enums.py index 8d0f153e..1813ac50 100644 --- a/src/easydiffraction/datablocks/experiment/item/enums.py +++ b/src/easydiffraction/datablocks/experiment/item/enums.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Enumerations for experiment configuration (forms, modes, types).""" diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 3e5aebb5..91fe88be 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Factory for creating experiment instances from various inputs. diff --git a/src/easydiffraction/datablocks/experiment/item/total_pd.py b/src/easydiffraction/datablocks/experiment/item/total_pd.py index 881dd0ce..7a2c55f3 100644 --- a/src/easydiffraction/datablocks/experiment/item/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/total_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/datablocks/structure/__init__.py b/src/easydiffraction/datablocks/structure/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/datablocks/structure/__init__.py +++ b/src/easydiffraction/datablocks/structure/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/datablocks/structure/categories/__init__.py b/src/easydiffraction/datablocks/structure/categories/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/datablocks/structure/categories/__init__.py +++ b/src/easydiffraction/datablocks/structure/categories/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/__init__.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/__init__.py index cb4c1750..7bd7b9ad 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/__init__.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.structure.categories.atom_sites.default import AtomSite diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index 76d890d5..fe688f15 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Atom site category. diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py index e233d0bd..c91b3dda 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Atom-sites factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/structure/categories/cell/__init__.py b/src/easydiffraction/datablocks/structure/categories/cell/__init__.py index 08773b3e..16f6cab2 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/__init__.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.structure.categories.cell.default import Cell diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index e07e20e0..0a7c4de6 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Unit cell parameters category for structures.""" diff --git a/src/easydiffraction/datablocks/structure/categories/cell/factory.py b/src/easydiffraction/datablocks/structure/categories/cell/factory.py index c5fde941..6817b2d7 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/factory.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Cell factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/__init__.py b/src/easydiffraction/datablocks/structure/categories/space_group/__init__.py index daf02947..a8b33e62 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/__init__.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.structure.categories.space_group.default import SpaceGroup diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 7076d272..8a0f38a7 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Space group category for crystallographic structures.""" diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/factory.py b/src/easydiffraction/datablocks/structure/categories/space_group/factory.py index 87807cef..9ef8611d 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/factory.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Space-group factory — delegates entirely to ``FactoryBase``.""" diff --git a/src/easydiffraction/datablocks/structure/collection.py b/src/easydiffraction/datablocks/structure/collection.py index ecc8f26e..4df632f9 100644 --- a/src/easydiffraction/datablocks/structure/collection.py +++ b/src/easydiffraction/datablocks/structure/collection.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Collection of structure data blocks.""" diff --git a/src/easydiffraction/datablocks/structure/item/__init__.py b/src/easydiffraction/datablocks/structure/item/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/datablocks/structure/item/__init__.py +++ b/src/easydiffraction/datablocks/structure/item/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index cd517785..ef1f67d6 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Structure datablock item.""" diff --git a/src/easydiffraction/datablocks/structure/item/factory.py b/src/easydiffraction/datablocks/structure/item/factory.py index a2067dff..74bc21c7 100644 --- a/src/easydiffraction/datablocks/structure/item/factory.py +++ b/src/easydiffraction/datablocks/structure/item/factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Factory for creating structure instances from various inputs. diff --git a/src/easydiffraction/display/__init__.py b/src/easydiffraction/display/__init__.py index 265560b2..5f4f18b3 100644 --- a/src/easydiffraction/display/__init__.py +++ b/src/easydiffraction/display/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Display subsystem for tables and plots. diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index d97f7be9..bce08c7e 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Common base classes for display components and their factories.""" diff --git a/src/easydiffraction/display/plotters/__init__.py b/src/easydiffraction/display/plotters/__init__.py index 14dae26a..31bd07bd 100644 --- a/src/easydiffraction/display/plotters/__init__.py +++ b/src/easydiffraction/display/plotters/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Plotting backends. diff --git a/src/easydiffraction/display/plotters/ascii.py b/src/easydiffraction/display/plotters/ascii.py index 7b5ff6a8..da11d1d2 100644 --- a/src/easydiffraction/display/plotters/ascii.py +++ b/src/easydiffraction/display/plotters/ascii.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """ASCII plotting backend. diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index 7220dfeb..0c2e044c 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Abstract base and shared constants for plotting backends.""" diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index bf2b4907..62f658ec 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Plotly plotting backend. diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 90d54261..779116b3 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Plotting facade for measured and calculated patterns. diff --git a/src/easydiffraction/display/tablers/__init__.py b/src/easydiffraction/display/tablers/__init__.py index 93d84417..d68c1cda 100644 --- a/src/easydiffraction/display/tablers/__init__.py +++ b/src/easydiffraction/display/tablers/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Tabular rendering backends. diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index 2b710d8f..01d67c07 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Low-level backends for rendering tables. diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index da05b5e8..c05cc6ed 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Pandas-based table renderer for notebooks using DataFrame Styler.""" diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index e7a8afbb..693451b4 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Rich-based table renderer for terminals and notebooks.""" diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index e149609d..cac8ac44 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Table rendering engines: console (Rich) and Jupyter (pandas).""" diff --git a/src/easydiffraction/display/utils.py b/src/easydiffraction/display/utils.py index 8c06f384..a369c819 100644 --- a/src/easydiffraction/display/utils.py +++ b/src/easydiffraction/display/utils.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/io/__init__.py b/src/easydiffraction/io/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/io/__init__.py +++ b/src/easydiffraction/io/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/io/cif/__init__.py b/src/easydiffraction/io/cif/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/io/cif/__init__.py +++ b/src/easydiffraction/io/cif/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/io/cif/handler.py b/src/easydiffraction/io/cif/handler.py index 16ed4343..62fb59ac 100644 --- a/src/easydiffraction/io/cif/handler.py +++ b/src/easydiffraction/io/cif/handler.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Minimal CIF tag handler used by descriptors/parameters.""" diff --git a/src/easydiffraction/io/cif/parse.py b/src/easydiffraction/io/cif/parse.py index ad0d736d..a320cf60 100644 --- a/src/easydiffraction/io/cif/parse.py +++ b/src/easydiffraction/io/cif/parse.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import gemmi diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index 971b08c4..eb539d83 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/project/__init__.py b/src/easydiffraction/project/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/project/__init__.py +++ b/src/easydiffraction/project/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index f7a3f487..8a2cf5ba 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Project facade to orchestrate models, experiments, and analysis.""" diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py index 998ba200..e641a1e5 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_info.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Project metadata container used by Project.""" diff --git a/src/easydiffraction/summary/__init__.py b/src/easydiffraction/summary/__init__.py index 429f2648..4e798e20 100644 --- a/src/easydiffraction/summary/__init__.py +++ b/src/easydiffraction/summary/__init__.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index 7e72d825..b89c363d 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from textwrap import wrap diff --git a/src/easydiffraction/utils/__init__.py b/src/easydiffraction/utils/__init__.py index d193bf2f..53fde2c6 100644 --- a/src/easydiffraction/utils/__init__.py +++ b/src/easydiffraction/utils/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.utils.utils import _is_dev_version diff --git a/src/easydiffraction/utils/_vendored/__init__.py b/src/easydiffraction/utils/_vendored/__init__.py index 20506363..0ec35f4c 100644 --- a/src/easydiffraction/utils/_vendored/__init__.py +++ b/src/easydiffraction/utils/_vendored/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Vendored third-party modules. diff --git a/src/easydiffraction/utils/_vendored/theme_detect.py b/src/easydiffraction/utils/_vendored/theme_detect.py index 1d555f0a..bae7826d 100644 --- a/src/easydiffraction/utils/_vendored/theme_detect.py +++ b/src/easydiffraction/utils/_vendored/theme_detect.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Jupyter theme detection with custom detection order. diff --git a/src/easydiffraction/utils/environment.py b/src/easydiffraction/utils/environment.py index aa7de5a6..cee1ba46 100644 --- a/src/easydiffraction/utils/environment.py +++ b/src/easydiffraction/utils/environment.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index 056d650b..dddaf1a0 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Project-wide logging utilities built on top of Rich. diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 82118302..c1224ae8 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from __future__ import annotations diff --git a/tests/integration/fitting/test_multi.py b/tests/integration/fitting/test_multi.py index 60e78f0a..6b1a0c8f 100644 --- a/tests/integration/fitting/test_multi.py +++ b/tests/integration/fitting/test_multi.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import tempfile diff --git a/tests/integration/fitting/test_pair-distribution-function.py b/tests/integration/fitting/test_pair-distribution-function.py index 823fd420..066e727e 100644 --- a/tests/integration/fitting/test_pair-distribution-function.py +++ b/tests/integration/fitting/test_pair-distribution-function.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import tempfile diff --git a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py index 49e7b4b1..5045dd27 100644 --- a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py +++ b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import tempfile diff --git a/tests/integration/fitting/test_powder-diffraction_joint-fit.py b/tests/integration/fitting/test_powder-diffraction_joint-fit.py index cc3e600e..fb94a8ff 100644 --- a/tests/integration/fitting/test_powder-diffraction_joint-fit.py +++ b/tests/integration/fitting/test_powder-diffraction_joint-fit.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import tempfile diff --git a/tests/integration/fitting/test_powder-diffraction_time-of-flight.py b/tests/integration/fitting/test_powder-diffraction_time-of-flight.py index ae702b4d..18123254 100644 --- a/tests/integration/fitting/test_powder-diffraction_time-of-flight.py +++ b/tests/integration/fitting/test_powder-diffraction_time-of-flight.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import tempfile diff --git a/tests/integration/fitting/test_single-crystal-diffraction.py b/tests/integration/fitting/test_single-crystal-diffraction.py index af915d92..f5273fdd 100644 --- a/tests/integration/fitting/test_single-crystal-diffraction.py +++ b/tests/integration/fitting/test_single-crystal-diffraction.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import tempfile diff --git a/tests/integration/scipp-analysis/dream/conftest.py b/tests/integration/scipp-analysis/dream/conftest.py index 56aabf1f..b16da0a1 100644 --- a/tests/integration/scipp-analysis/dream/conftest.py +++ b/tests/integration/scipp-analysis/dream/conftest.py @@ -1,5 +1,6 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors # 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 diff --git a/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py b/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py index cde09c8a..23821ee2 100644 --- a/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py +++ b/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py @@ -1,5 +1,6 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -# Copyright (c) 2026 DMSC + """Tests for analyzing reduced diffraction data using easydiffraction. These tests verify the complete workflow: diff --git a/tests/integration/scipp-analysis/dream/test_package_import.py b/tests/integration/scipp-analysis/dream/test_package_import.py index 7c10d02b..03125806 100644 --- a/tests/integration/scipp-analysis/dream/test_package_import.py +++ b/tests/integration/scipp-analysis/dream/test_package_import.py @@ -1,5 +1,6 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -# Copyright (c) 2026 DMSC + """Tests for verifying package installation and version consistency. These tests check that easydiffraction and essdiffraction packages are diff --git a/tests/integration/scipp-analysis/dream/test_read_reduced_data.py b/tests/integration/scipp-analysis/dream/test_read_reduced_data.py index 616c9876..db589354 100644 --- a/tests/integration/scipp-analysis/dream/test_read_reduced_data.py +++ b/tests/integration/scipp-analysis/dream/test_read_reduced_data.py @@ -1,5 +1,6 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -# Copyright (c) 2026 DMSC + """Tests for reading reduced data from CIF files. These tests verify that the CIF file can be fetched, read as text, diff --git a/tests/integration/scipp-analysis/dream/test_validate_meta_data.py b/tests/integration/scipp-analysis/dream/test_validate_meta_data.py index 7712dbc3..6f07845a 100644 --- a/tests/integration/scipp-analysis/dream/test_validate_meta_data.py +++ b/tests/integration/scipp-analysis/dream/test_validate_meta_data.py @@ -1,5 +1,6 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -# Copyright (c) 2026 DMSC + """Tests for validating metadata structure in CIF files. These tests verify that the CIF file contains the expected data blocks, diff --git a/tests/integration/scipp-analysis/dream/test_validate_physical_data.py b/tests/integration/scipp-analysis/dream/test_validate_physical_data.py index f1be5eb3..a8e5d4fb 100644 --- a/tests/integration/scipp-analysis/dream/test_validate_physical_data.py +++ b/tests/integration/scipp-analysis/dream/test_validate_physical_data.py @@ -1,5 +1,6 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -# Copyright (c) 2026 DMSC + """Tests for validating physical data values in CIF files. These tests verify that numerical data columns contain valid, diff --git a/tests/unit/easydiffraction/analysis/calculators/test_base.py b/tests/unit/easydiffraction/analysis/calculators/test_base.py index 004a266b..5070b9a3 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_base.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/analysis/calculators/test_crysfml.py b/tests/unit/easydiffraction/analysis/calculators/test_crysfml.py index e35d1bd5..a6a1371c 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_crysfml.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_crysfml.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py b/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py index d42514e4..8593bfe0 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/analysis/calculators/test_factory.py b/tests/unit/easydiffraction/analysis/calculators/test_factory.py index 7df08b97..681299fa 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_factory.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py b/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py index 268d0d84..9dce59f5 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/analysis/categories/test_aliases.py b/tests/unit/easydiffraction/analysis/categories/test_aliases.py index f44609ee..2545218a 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_aliases.py +++ b/tests/unit/easydiffraction/analysis/categories/test_aliases.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.categories.aliases import Alias diff --git a/tests/unit/easydiffraction/analysis/categories/test_constraints.py b/tests/unit/easydiffraction/analysis/categories/test_constraints.py index 2ed29866..443f9b1b 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_constraints.py +++ b/tests/unit/easydiffraction/analysis/categories/test_constraints.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.categories.constraints import Constraint diff --git a/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py b/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py index 9e8e8d1f..51777f11 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py +++ b/tests/unit/easydiffraction/analysis/categories/test_joint_fit_experiments.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.analysis.categories.joint_fit_experiments import JointFitExperiment diff --git a/tests/unit/easydiffraction/analysis/fit_helpers/test_metrics.py b/tests/unit/easydiffraction/analysis/fit_helpers/test_metrics.py index 7c8b75c9..2fc968f8 100644 --- a/tests/unit/easydiffraction/analysis/fit_helpers/test_metrics.py +++ b/tests/unit/easydiffraction/analysis/fit_helpers/test_metrics.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py b/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py index 50a50cd6..ee8014f2 100644 --- a/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py +++ b/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/analysis/fit_helpers/test_tracking.py b/tests/unit/easydiffraction/analysis/fit_helpers/test_tracking.py index bf3873ab..561eb54c 100644 --- a/tests/unit/easydiffraction/analysis/fit_helpers/test_tracking.py +++ b/tests/unit/easydiffraction/analysis/fit_helpers/test_tracking.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/analysis/minimizers/test_base.py b/tests/unit/easydiffraction/analysis/minimizers/test_base.py index 5d04a75a..501a2a98 100644 --- a/tests/unit/easydiffraction/analysis/minimizers/test_base.py +++ b/tests/unit/easydiffraction/analysis/minimizers/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/analysis/minimizers/test_dfols.py b/tests/unit/easydiffraction/analysis/minimizers/test_dfols.py index 88e22dee..72d01949 100644 --- a/tests/unit/easydiffraction/analysis/minimizers/test_dfols.py +++ b/tests/unit/easydiffraction/analysis/minimizers/test_dfols.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/analysis/minimizers/test_factory.py b/tests/unit/easydiffraction/analysis/minimizers/test_factory.py index 33c18236..961ef782 100644 --- a/tests/unit/easydiffraction/analysis/minimizers/test_factory.py +++ b/tests/unit/easydiffraction/analysis/minimizers/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/analysis/minimizers/test_lmfit.py b/tests/unit/easydiffraction/analysis/minimizers/test_lmfit.py index 77b694ee..69005bd1 100644 --- a/tests/unit/easydiffraction/analysis/minimizers/test_lmfit.py +++ b/tests/unit/easydiffraction/analysis/minimizers/test_lmfit.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import types diff --git a/tests/unit/easydiffraction/analysis/test_analysis.py b/tests/unit/easydiffraction/analysis/test_analysis.py index 5ef51324..19bc3c2a 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis.py +++ b/tests/unit/easydiffraction/analysis/test_analysis.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py index df8e490e..bdd5ead0 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py +++ b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py b/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py index e341e56e..7f2895b4 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py +++ b/tests/unit/easydiffraction/analysis/test_analysis_show_empty.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/analysis/test_fitting.py b/tests/unit/easydiffraction/analysis/test_fitting.py index dc006bfa..2f703f77 100644 --- a/tests/unit/easydiffraction/analysis/test_fitting.py +++ b/tests/unit/easydiffraction/analysis/test_fitting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/core/test_category.py b/tests/unit/easydiffraction/core/test_category.py index 50c66e4e..632d96f1 100644 --- a/tests/unit/easydiffraction/core/test_category.py +++ b/tests/unit/easydiffraction/core/test_category.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.core.category import CategoryCollection diff --git a/tests/unit/easydiffraction/core/test_collection.py b/tests/unit/easydiffraction/core/test_collection.py index b1831008..817b76dd 100644 --- a/tests/unit/easydiffraction/core/test_collection.py +++ b/tests/unit/easydiffraction/core/test_collection.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/core/test_datablock.py b/tests/unit/easydiffraction/core/test_datablock.py index 133ae596..b41f68b7 100644 --- a/tests/unit/easydiffraction/core/test_datablock.py +++ b/tests/unit/easydiffraction/core/test_datablock.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/core/test_diagnostic.py b/tests/unit/easydiffraction/core/test_diagnostic.py index 98e96320..cda7ce98 100644 --- a/tests/unit/easydiffraction/core/test_diagnostic.py +++ b/tests/unit/easydiffraction/core/test_diagnostic.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/core/test_factory.py b/tests/unit/easydiffraction/core/test_factory.py index ee85b97c..78150ea5 100644 --- a/tests/unit/easydiffraction/core/test_factory.py +++ b/tests/unit/easydiffraction/core/test_factory.py @@ -1,5 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause - -# core/factory.py was removed — FactoryBase and _validate_args are no -# longer part of the codebase. This test file is intentionally empty. diff --git a/tests/unit/easydiffraction/core/test_guard.py b/tests/unit/easydiffraction/core/test_guard.py index 20fc8a4e..64d46680 100644 --- a/tests/unit/easydiffraction/core/test_guard.py +++ b/tests/unit/easydiffraction/core/test_guard.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/core/test_identity.py b/tests/unit/easydiffraction/core/test_identity.py index 69c5e73b..61da0723 100644 --- a/tests/unit/easydiffraction/core/test_identity.py +++ b/tests/unit/easydiffraction/core/test_identity.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/core/test_parameters.py b/tests/unit/easydiffraction/core/test_parameters.py index 57341af4..4bf46f0d 100644 --- a/tests/unit/easydiffraction/core/test_parameters.py +++ b/tests/unit/easydiffraction/core/test_parameters.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/core/test_singletons.py b/tests/unit/easydiffraction/core/test_singletons.py index bd2c5aef..ba69f07a 100644 --- a/tests/unit/easydiffraction/core/test_singletons.py +++ b/tests/unit/easydiffraction/core/test_singletons.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/core/test_validation.py b/tests/unit/easydiffraction/core/test_validation.py index a0ff2e54..3bbd0ac3 100644 --- a/tests/unit/easydiffraction/core/test_validation.py +++ b/tests/unit/easydiffraction/core/test_validation.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/crystallography/test_crystallography.py b/tests/unit/easydiffraction/crystallography/test_crystallography.py index f2968180..73e1a134 100644 --- a/tests/unit/easydiffraction/crystallography/test_crystallography.py +++ b/tests/unit/easydiffraction/crystallography/test_crystallography.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/crystallography/test_space_groups.py b/tests/unit/easydiffraction/crystallography/test_space_groups.py index 0b663257..dd1482cd 100644 --- a/tests/unit/easydiffraction/crystallography/test_space_groups.py +++ b/tests/unit/easydiffraction/crystallography/test_space_groups.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py index 985c2d5b..88049e83 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py index b59ea102..d10a3c40 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py index f8ea00af..b37a22e6 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_enums.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py index 285f3b81..09679489 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py index e4e89605..ff231943 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py index 10514fe0..1a3f233c 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py index 0b5c0cfe..534fd192 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_sc.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py index bb4c7ffb..6f52cdff 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py index ae520bfc..41e638e5 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py index fff741db..38bcb8c7 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_cwl.py b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_cwl.py index 205abd50..0816f6e7 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_cwl.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_cwl.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.instrument.cwl import CwlPdInstrument diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py index 5b399346..04117aa9 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py index 91c4f0d0..bfd6cc12 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_base.py index ad3708dc..229fc734 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.peak.base import PeakBase diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py index 854bc3cc..d941afe9 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl_mixins.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl_mixins.py index 3bdc4466..19026f50 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl_mixins.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_cwl_mixins.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.peak.cwl import CwlPseudoVoigt diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_factory.py index 6ff155ac..4b0ccd35 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py index fde6d062..78e25d52 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.peak.tof import TofPseudoVoigt diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py index f68e6fbf..c2f114a0 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total.py index c49d1cf7..46a6497b 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total_mixins.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total_mixins.py index 0979dcb9..c3534847 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total_mixins.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_total_mixins.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.experiment.categories.peak.total import TotalGaussianDampedSinc diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py index dc290dcf..8f34b8de 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_excluded_regions.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py index 3fcf2650..9190071e 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_experiment_type.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py index b4621cf2..dcee6f6e 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py index 06b55151..69f96e7b 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py index 86d73aa5..3b4b9fa7 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py index ac159b34..c2a0ab92 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py index 8ae3bcf2..89c763dc 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py index 5fedd3d5..a69dd1bd 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py index 8796876b..983f991b 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_enums.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py index deb9dd6b..c185ed30 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py index 78d40afc..d021dc34 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py index 87db1e56..bcb9f822 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py +++ b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py b/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py index 89786b9e..4025bd83 100644 --- a/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py +++ b/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.structure.categories.space_group import SpaceGroup diff --git a/tests/unit/easydiffraction/datablocks/structure/item/test_base.py b/tests/unit/easydiffraction/datablocks/structure/item/test_base.py index 33bb878b..154d0405 100644 --- a/tests/unit/easydiffraction/datablocks/structure/item/test_base.py +++ b/tests/unit/easydiffraction/datablocks/structure/item/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.structure.item.base import Structure diff --git a/tests/unit/easydiffraction/datablocks/structure/item/test_factory.py b/tests/unit/easydiffraction/datablocks/structure/item/test_factory.py index d1b50776..148e010a 100644 --- a/tests/unit/easydiffraction/datablocks/structure/item/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/structure/item/test_factory.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from easydiffraction.datablocks.structure.item.factory import StructureFactory diff --git a/tests/unit/easydiffraction/datablocks/structure/test_collection.py b/tests/unit/easydiffraction/datablocks/structure/test_collection.py index 429f2648..4e798e20 100644 --- a/tests/unit/easydiffraction/datablocks/structure/test_collection.py +++ b/tests/unit/easydiffraction/datablocks/structure/test_collection.py @@ -1,2 +1,2 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/display/plotters/test_ascii.py b/tests/unit/easydiffraction/display/plotters/test_ascii.py index 616bc60d..e0c04f8b 100644 --- a/tests/unit/easydiffraction/display/plotters/test_ascii.py +++ b/tests/unit/easydiffraction/display/plotters/test_ascii.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tests/unit/easydiffraction/display/plotters/test_base.py b/tests/unit/easydiffraction/display/plotters/test_base.py index af8d402f..b1029d2c 100644 --- a/tests/unit/easydiffraction/display/plotters/test_base.py +++ b/tests/unit/easydiffraction/display/plotters/test_base.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import sys diff --git a/tests/unit/easydiffraction/display/plotters/test_plotly.py b/tests/unit/easydiffraction/display/plotters/test_plotly.py index 3933d0ab..5a1549f0 100644 --- a/tests/unit/easydiffraction/display/plotters/test_plotly.py +++ b/tests/unit/easydiffraction/display/plotters/test_plotly.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/display/test_plotting.py b/tests/unit/easydiffraction/display/test_plotting.py index 4eb069ca..be5b4239 100644 --- a/tests/unit/easydiffraction/display/test_plotting.py +++ b/tests/unit/easydiffraction/display/test_plotting.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/io/cif/test_handler.py b/tests/unit/easydiffraction/io/cif/test_handler.py index 42bbe3ca..a9c1be85 100644 --- a/tests/unit/easydiffraction/io/cif/test_handler.py +++ b/tests/unit/easydiffraction/io/cif/test_handler.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/io/cif/test_serialize.py b/tests/unit/easydiffraction/io/cif/test_serialize.py index 910d2f49..6b92a0e4 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/io/cif/test_serialize_more.py b/tests/unit/easydiffraction/io/cif/test_serialize_more.py index 441ba9fe..fc8ef714 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize_more.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize_more.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/project/test_project.py b/tests/unit/easydiffraction/project/test_project.py index fcd7f429..c6a64055 100644 --- a/tests/unit/easydiffraction/project/test_project.py +++ b/tests/unit/easydiffraction/project/test_project.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/project/test_project_info.py b/tests/unit/easydiffraction/project/test_project_info.py index 1e513c2f..8c3455ab 100644 --- a/tests/unit/easydiffraction/project/test_project_info.py +++ b/tests/unit/easydiffraction/project/test_project_info.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py index d52956eb..cdeafd35 100644 --- a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py +++ b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/project/test_project_save.py b/tests/unit/easydiffraction/project/test_project_save.py index e319155a..421e8928 100644 --- a/tests/unit/easydiffraction/project/test_project_save.py +++ b/tests/unit/easydiffraction/project/test_project_save.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/summary/test_summary.py b/tests/unit/easydiffraction/summary/test_summary.py index e98471a6..b57b5598 100644 --- a/tests/unit/easydiffraction/summary/test_summary.py +++ b/tests/unit/easydiffraction/summary/test_summary.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/summary/test_summary_details.py b/tests/unit/easydiffraction/summary/test_summary_details.py index 41f3aabd..d0e0c97b 100644 --- a/tests/unit/easydiffraction/summary/test_summary_details.py +++ b/tests/unit/easydiffraction/summary/test_summary_details.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/test___init__.py b/tests/unit/easydiffraction/test___init__.py index 5eb8c38f..75f273e1 100644 --- a/tests/unit/easydiffraction/test___init__.py +++ b/tests/unit/easydiffraction/test___init__.py @@ -1,7 +1,6 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -# Focused tests for package __init__: lazy attributes and error path import importlib from pathlib import Path diff --git a/tests/unit/easydiffraction/test___main__.py b/tests/unit/easydiffraction/test___main__.py index 885f73ef..76ba7cec 100644 --- a/tests/unit/easydiffraction/test___main__.py +++ b/tests/unit/easydiffraction/test___main__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause from typer.testing import CliRunner diff --git a/tests/unit/easydiffraction/utils/test_logging.py b/tests/unit/easydiffraction/utils/test_logging.py index 79da58c4..6dd520c2 100644 --- a/tests/unit/easydiffraction/utils/test_logging.py +++ b/tests/unit/easydiffraction/utils/test_logging.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/unit/easydiffraction/utils/test_theme_detect.py b/tests/unit/easydiffraction/utils/test_theme_detect.py index f52750e7..c3277a0a 100644 --- a/tests/unit/easydiffraction/utils/test_theme_detect.py +++ b/tests/unit/easydiffraction/utils/test_theme_detect.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """Unit tests for theme detection module.""" diff --git a/tests/unit/easydiffraction/utils/test_utils.py b/tests/unit/easydiffraction/utils/test_utils.py index 472584c1..48d5a182 100644 --- a/tests/unit/easydiffraction/utils/test_utils.py +++ b/tests/unit/easydiffraction/utils/test_utils.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors +# SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import numpy as np diff --git a/tools/add_license_headers.py b/tools/add_license_headers.py deleted file mode 100644 index 28febde7..00000000 --- a/tools/add_license_headers.py +++ /dev/null @@ -1,151 +0,0 @@ -# SPDX-FileCopyrightText: 2026 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -"""Add SPDX headers to Python files. - -- SPDX-FileCopyrightText with the license holder name and organization - URL from ``pyproject.toml`` as well as the file's creation year. -- SPDX-License-Identifier is taken from the project license value in - ``pyproject.toml``. -""" - -from __future__ import annotations - -import argparse -import tomllib -from datetime import datetime -from pathlib import Path -from typing import Optional -from typing import Union - -from git import Repo -from spdx_headers.core import find_repository_root -from spdx_headers.core import get_copyright_info -from spdx_headers.data import load_license_data -from spdx_headers.operations import add_header_to_single_file - -LICENSE_DATABASE = load_license_data() - - -def load_pyproject(repo_path: Union[str, Path]) -> dict: - """Load and return parsed ``pyproject.toml`` data for the - repository. - """ - repo_root = find_repository_root(repo_path) - pyproject_path = repo_root / 'pyproject.toml' - - with open(pyproject_path, 'rb') as file_handle: - return tomllib.load(file_handle) - - -def get_file_creation_year(file_path: Union[str, Path]) -> str: - """Return the year the file was first added to Git history. - - If the year cannot be determined, fall back to the current year. - """ - file_path = Path(file_path) - - repo = Repo(file_path, search_parent_directories=True) - root = Path(repo.working_tree_dir).resolve() - rel_path = file_path.resolve().relative_to(root) - - rel_path_git = rel_path.as_posix() # IMPORTANT for git pathspec - - # Get the year when the file was first added to Git history. - # NOTE: Do not combine `--reverse` with `--max-count=1` here, as it can - # yield an empty result with some Git versions. Instead, get the full - # filtered output and take the first line. - log_output = repo.git.log( - '--follow', - '--diff-filter=A', - '--reverse', - '--format=%ad', - '--date=format:%Y', - '--', - rel_path_git, - ).strip() - - year = log_output.splitlines()[0].strip() if log_output else '' - - return year or str(datetime.now().year) - - -def get_org_url(repo_path: Union[str, Path]) -> str: - """Return the organization URL derived from the repository source - URL. - """ - pyproject_data = load_pyproject(repo_path) - repo_url = pyproject_data['project']['urls']['Source Code'] - return repo_url.rsplit('/', 1)[0] - - -def get_project_license(repo_path: Union[str, Path]) -> str: - """Return the project license value from ``pyproject.toml``.""" - pyproject_data = load_pyproject(repo_path) - return pyproject_data['project']['license'] - - -def get_copyright_holder(repo_path: Union[str, Path]) -> str: - """Return the repository copyright holder name.""" - _, name, _ = get_copyright_info(repo_path) - return name - - -def add_spdx_header( - target_file: Union[str, Path], - *, - license_key: str, - copyright_holder: str, - org_url: str, -) -> None: - """Add SPDX headers.""" - year = get_file_creation_year(target_file) - - add_header_to_single_file( - filepath=target_file, - license_key=license_key, - license_data=LICENSE_DATABASE, - year=year, - name=copyright_holder, - email=org_url, - ) - - -def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - description='Add SPDX headers to Python files under the given paths.', - ) - parser.add_argument( - 'paths', - nargs='+', - help='Relative paths to scan (e.g. src tests)', - ) - return parser - - -def main(argv: Optional[list[str]] = None) -> int: - parser = build_parser() - args = parser.parse_args(argv) - - repo_path = Path('.').resolve() - license_key = get_project_license(repo_path) - copyright_holder = get_copyright_holder(repo_path) - org_url = get_org_url(repo_path) - - for base_dir in args.paths: - base_path = Path(base_dir) - if not base_path.exists(): - parser.error(f'Path does not exist: {base_dir}') - - for py_file in base_path.rglob('*.py'): - add_spdx_header( - py_file, - license_key=license_key, - copyright_holder=copyright_holder, - org_url=org_url, - ) - - return 0 - - -if __name__ == '__main__': - raise SystemExit(main()) diff --git a/tools/check_license_headers.py b/tools/check_license_headers.py deleted file mode 100644 index c8a4df21..00000000 --- a/tools/check_license_headers.py +++ /dev/null @@ -1,45 +0,0 @@ -# SPDX-FileCopyrightText: 2026 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -"""Check SPDX headers in Python files.""" - -from __future__ import annotations - -import argparse -from pathlib import Path -from typing import Optional - -from spdx_headers.operations import check_headers - - -def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - description='Check SPDX headers in Python files under the given paths.', - ) - parser.add_argument( - 'paths', - nargs='+', - help='Relative paths to scan (e.g. src tests)', - ) - return parser - - -def main(argv: Optional[list[str]] = None) -> int: - parser = build_parser() - args = parser.parse_args(argv) - - exit_codes = [] - - for base_dir in args.paths: - base_path = Path(base_dir) - if not base_path.exists(): - parser.error(f'Path does not exist: {base_dir}') - - print('=' * 50) - print(f'Checking SPDX headers in: {base_dir}') - exit_codes.append(check_headers(base_dir)) - - return 0 if all(code == 0 for code in exit_codes) else 1 - - -if __name__ == '__main__': - raise SystemExit(main()) diff --git a/tools/license_headers.py b/tools/license_headers.py new file mode 100644 index 00000000..47d23524 --- /dev/null +++ b/tools/license_headers.py @@ -0,0 +1,315 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Add, remove, or check SPDX headers in Python files.""" + +from __future__ import annotations + +import argparse +import fnmatch +import tomllib +from datetime import datetime +from pathlib import Path +from typing import Any +from typing import Optional +from typing import Union + +from git import Repo +from spdx_headers.core import find_repository_root +from spdx_headers.core import get_copyright_info +from spdx_headers.core import has_spdx_header +from spdx_headers.data import load_license_data +from spdx_headers.operations import add_header_to_single_file +from spdx_headers.operations import remove_header_from_single_file + +LICENSE_DATABASE = load_license_data() + + +def load_pyproject(repo_path: Union[str, Path]) -> dict[str, Any]: + """Load and return parsed ``pyproject.toml`` data for the repository.""" + repo_root = find_repository_root(repo_path) + pyproject_path = repo_root / 'pyproject.toml' + + with pyproject_path.open('rb') as file_handle: + return tomllib.load(file_handle) + + +def get_pyproject_value(pyproject_data: dict[str, Any], dotted_key: str) -> Any: + """Return a nested ``pyproject.toml`` value from a dotted key.""" + value: Any = pyproject_data + for part in dotted_key.split('.'): + if not isinstance(value, dict) or part not in value: + raise KeyError(dotted_key) + value = value[part] + return value + + +def normalize_pattern(pattern: str) -> str: + """Normalize an exclude pattern to a POSIX-style relative path.""" + normalized = Path(pattern).as_posix() + if normalized.startswith('./'): + normalized = normalized[2:] + return normalized.rstrip('/') + + +def get_exclude_patterns( + repo_path: Union[str, Path], + exclude_values: list[str], + exclude_from_pyproject_toml: Optional[str], +) -> list[str]: + """Return normalized exclude patterns from CLI and ``pyproject.toml``.""" + pyproject_data = load_pyproject(repo_path) + patterns: list[str] = [] + + if exclude_from_pyproject_toml: + value = get_pyproject_value(pyproject_data, exclude_from_pyproject_toml) + if not isinstance(value, list) or not all(isinstance(item, str) for item in value): + raise ValueError( + f'{exclude_from_pyproject_toml} in pyproject.toml must be a list of strings.', + ) + patterns.extend(value) + + for item in exclude_values: + try: + value = get_pyproject_value(pyproject_data, item) + except KeyError: + patterns.append(item) + continue + + if not isinstance(value, list) or not all(isinstance(entry, str) for entry in value): + raise ValueError(f'{item} in pyproject.toml must be a list of strings.') + patterns.extend(value) + + normalized_patterns: list[str] = [] + seen: set[str] = set() + for pattern in patterns: + normalized = normalize_pattern(pattern) + if normalized and normalized not in seen: + normalized_patterns.append(normalized) + seen.add(normalized) + + return normalized_patterns + + +def get_file_creation_year(file_path: Union[str, Path]) -> str: + """Return the year the file was first added to Git history. + + If the year cannot be determined, fall back to the current year. + """ + file_path = Path(file_path) + + repo = Repo(file_path, search_parent_directories=True) + root = Path(repo.working_tree_dir).resolve() + rel_path = file_path.resolve().relative_to(root) + + rel_path_git = rel_path.as_posix() + + log_output = repo.git.log( + '--follow', + '--diff-filter=A', + '--reverse', + '--format=%ad', + '--date=format:%Y', + '--', + rel_path_git, + ).strip() + + year = log_output.splitlines()[0].strip() if log_output else '' + + return year or str(datetime.now().year) + + +def get_org_url(repo_path: Union[str, Path]) -> str: + """Return the organization URL derived from the repository source URL.""" + pyproject_data = load_pyproject(repo_path) + repo_url = pyproject_data['project']['urls']['Source Code'] + return repo_url.rsplit('/', 1)[0] + + +def get_project_license(repo_path: Union[str, Path]) -> str: + """Return the project license value from ``pyproject.toml``.""" + pyproject_data = load_pyproject(repo_path) + return pyproject_data['project']['license'] + + +def get_copyright_holder(repo_path: Union[str, Path]) -> str: + """Return the repository copyright holder name.""" + _, name, _ = get_copyright_info(repo_path) + return name + + +def add_spdx_header( + target_file: Union[str, Path], + *, + license_key: str, + copyright_holder: str, + org_url: str, +) -> None: + """Add SPDX headers to one file.""" + year = get_file_creation_year(target_file) + + add_header_to_single_file( + filepath=target_file, + license_key=license_key, + license_data=LICENSE_DATABASE, + year=year, + name=copyright_holder, + email=org_url, + ) + + +def is_excluded(relative_path: str, exclude_patterns: list[str]) -> bool: + """Return whether a relative path should be excluded.""" + for pattern in exclude_patterns: + if fnmatch.fnmatch(relative_path, pattern): + return True + if relative_path == pattern: + return True + if relative_path.startswith(f'{pattern}/'): + return True + return False + + +def iter_python_files( + paths: list[str], + *, + repo_root: Path, + exclude_patterns: list[str], + parser: argparse.ArgumentParser, +) -> list[Path]: + """Collect Python files under the given paths after exclusions.""" + files: list[Path] = [] + seen: set[Path] = set() + + for base_dir in paths: + base_path = Path(base_dir) + if not base_path.exists(): + parser.error(f'Path does not exist: {base_dir}') + + if base_path.is_file(): + candidates = [base_path] if base_path.suffix == '.py' else [] + else: + candidates = sorted(base_path.rglob('*.py')) + + for py_file in candidates: + resolved = py_file.resolve() + try: + relative_path = resolved.relative_to(repo_root).as_posix() + except ValueError: + relative_path = py_file.as_posix() + + if is_excluded(relative_path, exclude_patterns): + continue + + if resolved not in seen: + files.append(py_file) + seen.add(resolved) + + return files + + +def run_add( + files: list[Path], + *, + license_key: str, + copyright_holder: str, + org_url: str, +) -> int: + """Add SPDX headers to all selected files.""" + for py_file in files: + add_spdx_header( + py_file, + license_key=license_key, + copyright_holder=copyright_holder, + org_url=org_url, + ) + return 0 + + +def run_remove(files: list[Path]) -> int: + """Remove SPDX headers from all selected files.""" + for py_file in files: + remove_header_from_single_file(py_file) + return 0 + + +def run_check(files: list[Path]) -> int: + """Check SPDX headers in all selected files.""" + missing_files = [py_file for py_file in files if not has_spdx_header(py_file)] + + if not missing_files: + print('✓ All Python files have valid SPDX headers.') + return 0 + + print('✗ The following files are missing SPDX headers:') + for py_file in missing_files: + print(f' - {py_file.as_posix()}') + print(f'\nFound {len(missing_files)} files without SPDX headers.') + return 1 + + +def build_parser() -> argparse.ArgumentParser: + """Build the CLI argument parser.""" + parser = argparse.ArgumentParser( + description='Add, remove, or check SPDX headers in Python files.', + ) + subparsers = parser.add_subparsers(dest='command', required=True) + + for command_name in ('check', 'remove', 'add'): + command_parser = subparsers.add_parser(command_name) + command_parser.add_argument( + 'paths', + nargs='+', + help='Relative paths to scan (e.g. src tests)', + ) + command_parser.add_argument( + '--exclude', + nargs='*', + default=[], + help='Exclude paths, glob patterns, or pyproject dotted keys.', + ) + command_parser.add_argument( + '--exclude-from-pyproject-toml', + help='Read exclude patterns from a dotted key in pyproject.toml.', + ) + + return parser + + +def main(argv: Optional[list[str]] = None) -> int: + """Run the SPDX header CLI.""" + parser = build_parser() + args = parser.parse_args(argv) + + repo_path = Path('.').resolve() + repo_root = find_repository_root(repo_path).resolve() + exclude_patterns = get_exclude_patterns( + repo_path, + args.exclude, + args.exclude_from_pyproject_toml, + ) + files = iter_python_files( + args.paths, + repo_root=repo_root, + exclude_patterns=exclude_patterns, + parser=parser, + ) + + if args.command == 'check': + return run_check(files) + + if args.command == 'remove': + return run_remove(files) + + license_key = get_project_license(repo_path) + copyright_holder = get_copyright_holder(repo_path) + org_url = get_org_url(repo_path) + return run_add( + files, + license_key=license_key, + copyright_holder=copyright_holder, + org_url=org_url, + ) + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tools/remove_license_headers.py b/tools/remove_license_headers.py deleted file mode 100644 index f5d09da5..00000000 --- a/tools/remove_license_headers.py +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-FileCopyrightText: 2026 EasyScience contributors -# SPDX-License-Identifier: BSD-3-Clause -"""Remove SPDX headers from Python files.""" - -from __future__ import annotations - -import argparse -from pathlib import Path -from typing import Optional - -from spdx_headers.operations import remove_header_from_py_files - - -def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - description='Remove SPDX headers from Python files under the given paths.', - ) - parser.add_argument( - 'paths', - nargs='+', - help='Relative paths to scan (e.g. src tests)', - ) - return parser - - -def main(argv: Optional[list[str]] = None) -> int: - parser = build_parser() - args = parser.parse_args(argv) - - for base_dir in args.paths: - base_path = Path(base_dir) - if not base_path.exists(): - parser.error(f'Path does not exist: {base_dir}') - - remove_header_from_py_files(base_dir) - - return 0 - - -if __name__ == '__main__': - raise SystemExit(main()) diff --git a/tools/update_spdx.py b/tools/update_spdx.py deleted file mode 100644 index df9236af..00000000 --- a/tools/update_spdx.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Update or insert SPDX headers in Python files. - -- Ensures SPDX-FileCopyrightText has the current year. -- Ensures SPDX-License-Identifier is set to BSD-3-Clause. -""" - -import datetime -import fnmatch -import re -from pathlib import Path - -CURRENT_YEAR = datetime.datetime.now().year -COPYRIGHT_TEXT = ( - f'# SPDX-FileCopyrightText: 2021-{CURRENT_YEAR} EasyDiffraction contributors ' - '' -) -LICENSE_TEXT = '# SPDX-License-Identifier: BSD-3-Clause' - -# Patterns to exclude from SPDX header updates (vendored code) -EXCLUDE_PATTERNS = [ - '*/_vendored/jupyter_dark_detect/*', -] - - -def should_exclude(file_path: Path) -> bool: - """Check if a file should be excluded from SPDX header updates.""" - path_str = str(file_path) - return any(fnmatch.fnmatch(path_str, pattern) for pattern in EXCLUDE_PATTERNS) - - -def update_spdx_header(file_path: Path): - # Use Path.open to satisfy lint rule PTH123. - with file_path.open('r', encoding='utf-8') as f: - original_lines = f.readlines() - - # Regexes for SPDX lines - copy_re = re.compile(r'^#\s*SPDX-FileCopyrightText:.*$') - lic_re = re.compile(r'^#\s*SPDX-License-Identifier:.*$') - - # 1) Preserve any leading shebang / coding cookie lines - prefix = [] - body_start = 0 - if original_lines: - # Shebang line like "#!/usr/bin/env python3" - if original_lines[0].startswith('#!'): - prefix.append(original_lines[0]) - body_start = 1 - # PEP 263 coding cookie on first or second line - # e.g. "# -*- coding: utf-8 -*-" or "# coding: utf-8" - for _ in range(2): # at most one more line to inspect - if body_start < len(original_lines): - line = original_lines[body_start] - if re.match(r'^#.*coding[:=]\s*[-\w.]+', line): - prefix.append(line) - body_start += 1 - else: - break - - # 2) Work on the remaining body - body = original_lines[body_start:] - - # Remove any existing SPDX lines anywhere in the body - body = [ln for ln in body if not (copy_re.match(ln) or lic_re.match(ln))] - - # Strip leading blank lines in the body so header is tight - while body and not body[0].strip(): - body.pop(0) - - # 3) Build canonical SPDX block: two lines + exactly one blank - spdx_block = [ - COPYRIGHT_TEXT + '\n', - LICENSE_TEXT + '\n', - '\n', - ] - - # 4) New content: prefix + SPDX + body - new_lines = prefix + spdx_block + body - - # 5) Normalize: collapse any extra blank lines immediately after - # LICENSE to exactly one. This keeps the script idempotent. - # Find the index of LICENSE we just inserted (prefix may be 0, 1, - # or 2 lines) - lic_idx = len(prefix) + 1 # spdx_block[1] is the license line - # Ensure exactly one blank line after LICENSE - # Remove all blank lines after lic_idx, then insert a single blank. - j = lic_idx + 1 - # Remove any number of blank lines following - while j < len(new_lines) and not new_lines[j].strip(): - new_lines.pop(j) - # Insert exactly one blank line at this position - new_lines.insert(j, '\n') - - with file_path.open('w', encoding='utf-8') as f: - f.writelines(new_lines) - - -def main(): - """Recursively update or insert SPDX headers in all Python files - under the 'src' and 'tests' directories. - """ - for base_dir in ('src', 'tests'): - for py_file in Path(base_dir).rglob('*.py'): - if should_exclude(py_file): - continue - update_spdx_header(py_file) - - -if __name__ == '__main__': - main() From d6af14147fbc60a4f234542fa4880c1b81f12383 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 00:09:07 +0100 Subject: [PATCH 08/48] Convert notebooks --- docs/docs/tutorials/ed-1.ipynb | 203 +++ docs/docs/tutorials/ed-10.ipynb | 222 +++ docs/docs/tutorials/ed-11.ipynb | 254 +++ docs/docs/tutorials/ed-12.ipynb | 303 ++++ docs/docs/tutorials/ed-13.ipynb | 2923 +++++++++++++++++++++++++++++++ docs/docs/tutorials/ed-14.ipynb | 319 ++++ docs/docs/tutorials/ed-15.ipynb | 296 ++++ docs/docs/tutorials/ed-16.ipynb | 623 +++++++ docs/docs/tutorials/ed-2.ipynb | 344 ++++ docs/docs/tutorials/ed-3.ipynb | 1805 +++++++++++++++++++ docs/docs/tutorials/ed-4.ipynb | 705 ++++++++ docs/docs/tutorials/ed-5.ipynb | 638 +++++++ docs/docs/tutorials/ed-6.ipynb | 849 +++++++++ docs/docs/tutorials/ed-7.ipynb | 741 ++++++++ docs/docs/tutorials/ed-8.ipynb | 747 ++++++++ docs/docs/tutorials/ed-9.ipynb | 704 ++++++++ 16 files changed, 11676 insertions(+) create mode 100644 docs/docs/tutorials/ed-1.ipynb create mode 100644 docs/docs/tutorials/ed-10.ipynb create mode 100644 docs/docs/tutorials/ed-11.ipynb create mode 100644 docs/docs/tutorials/ed-12.ipynb create mode 100644 docs/docs/tutorials/ed-13.ipynb create mode 100644 docs/docs/tutorials/ed-14.ipynb create mode 100644 docs/docs/tutorials/ed-15.ipynb create mode 100644 docs/docs/tutorials/ed-16.ipynb create mode 100644 docs/docs/tutorials/ed-2.ipynb create mode 100644 docs/docs/tutorials/ed-3.ipynb create mode 100644 docs/docs/tutorials/ed-4.ipynb create mode 100644 docs/docs/tutorials/ed-5.ipynb create mode 100644 docs/docs/tutorials/ed-6.ipynb create mode 100644 docs/docs/tutorials/ed-7.ipynb create mode 100644 docs/docs/tutorials/ed-8.ipynb create mode 100644 docs/docs/tutorials/ed-9.ipynb diff --git a/docs/docs/tutorials/ed-1.ipynb b/docs/docs/tutorials/ed-1.ipynb new file mode 100644 index 00000000..9b46deb0 --- /dev/null +++ b/docs/docs/tutorials/ed-1.ipynb @@ -0,0 +1,203 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: LBCO, HRPT\n", + "\n", + "This minimalistic example is designed to show how Rietveld refinement\n", + "can be performed when both the crystal structure and experiment\n", + "parameters are defined using CIF files.\n", + "\n", + "For this example, constant-wavelength neutron powder diffraction data\n", + "for La0.5Ba0.5CoO3 from HRPT at PSI is used.\n", + "\n", + "It does not contain any advanced features or options, and includes no\n", + "comments or explanations—these can be found in the other tutorials.\n", + "Default values are used for all parameters if not specified. Only\n", + "essential and self-explanatory code is provided.\n", + "\n", + "The example is intended for users who are already familiar with the\n", + "EasyDiffraction library and want to quickly get started with a simple\n", + "refinement. It is also useful for those who want to see what a\n", + "refinement might look like in code. For a more detailed explanation of\n", + "the code, please refer to the other tutorials." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Step 1: Define Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# Create minimal project without name and description\n", + "project = ed.Project()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Step 2: Define Crystal Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# Download CIF file from repository\n", + "structure_path = ed.download_data(id=1, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add_from_cif_path(structure_path)" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Step 3: Define Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "# Download CIF file from repository\n", + "expt_path = ed.download_data(id=2, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_cif_path(expt_path)" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Step 4: Perform Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "# Start refinement. All parameters, which have standard uncertainties\n", + "# in the input CIF files, are refined by default.\n", + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "# Show fit results summary\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.show_names()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## Step 5: Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-10.ipynb b/docs/docs/tutorials/ed-10.ipynb new file mode 100644 index 00000000..d6596816 --- /dev/null +++ b/docs/docs/tutorials/ed-10.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Pair Distribution Function: Ni, NPD\n", + "\n", + "This example demonstrates a pair distribution function (PDF) analysis\n", + "of Ni, based on data collected from a constant wavelength neutron\n", + "powder diffraction experiment.\n", + "\n", + "The dataset is taken from:\n", + "https://github.com/diffpy/cmi_exchange/tree/main/cmi_scripts/fitNiPDF" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "project = ed.Project()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.create(name='ni')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['ni'].space_group.name_h_m = 'F m -3 m'\n", + "project.structures['ni'].space_group.it_coordinate_system_code = '1'\n", + "project.structures['ni'].cell.length_a = 3.52387\n", + "project.structures['ni'].atom_sites.create(\n", + " label='Ni',\n", + " type_symbol='Ni',\n", + " fract_x=0.0,\n", + " fract_y=0.0,\n", + " fract_z=0.0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Add Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = ed.download_data(id=6, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='pdf',\n", + " data_path=data_path,\n", + " sample_form='powder',\n", + " beam_mode='constant wavelength',\n", + " radiation_probe='neutron',\n", + " scattering_type='total',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['pdf'].linked_phases.create(id='ni', scale=1.0)\n", + "project.experiments['pdf'].peak.damp_q = 0\n", + "project.experiments['pdf'].peak.broad_q = 0.03\n", + "project.experiments['pdf'].peak.cutoff_q = 27.0\n", + "project.experiments['pdf'].peak.sharp_delta_1 = 0.0\n", + "project.experiments['pdf'].peak.sharp_delta_2 = 2.0\n", + "project.experiments['pdf'].peak.damp_particle_diameter = 0" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "## Select Fitting Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['ni'].cell.length_a.free = True\n", + "project.structures['ni'].atom_sites['Ni'].b_iso.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['pdf'].linked_phases['ni'].scale.free = True\n", + "project.experiments['pdf'].peak.broad_q.free = True\n", + "project.experiments['pdf'].peak.sharp_delta_2.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "## Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='pdf', show_residual=True)" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-11.ipynb b/docs/docs/tutorials/ed-11.ipynb new file mode 100644 index 00000000..ddacaac7 --- /dev/null +++ b/docs/docs/tutorials/ed-11.ipynb @@ -0,0 +1,254 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Pair Distribution Function: Si, NPD\n", + "\n", + "This example demonstrates a pair distribution function (PDF) analysis\n", + "of Si, based on data collected from a time-of-flight neutron powder\n", + "diffraction experiment at NOMAD at SNS." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "project = ed.Project()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Set Plotting Engine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# Keep the auto-selected engine. Alternatively, you can uncomment the\n", + "# line below to explicitly set the engine to the required one.\n", + "# project.plotter.engine = 'plotly'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "# Set global plot range for plots\n", + "project.plotter.x_max = 40" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.create(name='si')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure = project.structures['si']\n", + "structure.space_group.name_h_m.value = 'F d -3 m'\n", + "structure.space_group.it_coordinate_system_code = '1'\n", + "structure.cell.length_a = 5.43146\n", + "structure.atom_sites.create(\n", + " label='Si',\n", + " type_symbol='Si',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Add Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = ed.download_data(id=5, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='nomad',\n", + " data_path=data_path,\n", + " sample_form='powder',\n", + " beam_mode='time-of-flight',\n", + " radiation_probe='neutron',\n", + " scattering_type='total',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "experiment = project.experiments['nomad']\n", + "experiment.linked_phases.create(id='si', scale=1.0)\n", + "experiment.peak.damp_q = 0.02\n", + "experiment.peak.broad_q = 0.03\n", + "experiment.peak.cutoff_q = 35.0\n", + "experiment.peak.sharp_delta_1 = 0.0\n", + "experiment.peak.sharp_delta_2 = 4.0\n", + "experiment.peak.damp_particle_diameter = 0" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## Select Fitting Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['si'].cell.length_a.free = True\n", + "project.structures['si'].atom_sites['Si'].b_iso.free = True\n", + "experiment.linked_phases['si'].scale.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.peak.damp_q.free = True\n", + "experiment.peak.broad_q.free = True\n", + "experiment.peak.sharp_delta_1.free = True\n", + "experiment.peak.sharp_delta_2.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "## Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "## Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='nomad', show_residual=False)" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-12.ipynb b/docs/docs/tutorials/ed-12.ipynb new file mode 100644 index 00000000..a6394390 --- /dev/null +++ b/docs/docs/tutorials/ed-12.ipynb @@ -0,0 +1,303 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Pair Distribution Function: NaCl, XRD\n", + "\n", + "This example demonstrates a pair distribution function (PDF) analysis\n", + "of NaCl, based on data collected from an X-ray powder diffraction\n", + "experiment.\n", + "\n", + "The dataset is taken from:\n", + "https://github.com/diffpy/add2019-diffpy-cmi/tree/master" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "project = ed.Project()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Set Plotting Engine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# Keep the auto-selected engine. Alternatively, you can uncomment the\n", + "# line below to explicitly set the engine to the required one.\n", + "# project.plotter.engine = 'plotly'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "# Set global plot range for plots\n", + "project.plotter.x_min = 2.0\n", + "project.plotter.x_max = 30.0" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.create(name='nacl')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['nacl'].space_group.name_h_m = 'F m -3 m'\n", + "project.structures['nacl'].space_group.it_coordinate_system_code = '1'\n", + "project.structures['nacl'].cell.length_a = 5.62\n", + "project.structures['nacl'].atom_sites.create(\n", + " label='Na',\n", + " type_symbol='Na',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=1.0,\n", + ")\n", + "project.structures['nacl'].atom_sites.create(\n", + " label='Cl',\n", + " type_symbol='Cl',\n", + " fract_x=0.5,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='b',\n", + " b_iso=1.0,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Add Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = ed.download_data(id=4, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='xray_pdf',\n", + " data_path=data_path,\n", + " sample_form='powder',\n", + " beam_mode='constant wavelength',\n", + " radiation_probe='xray',\n", + " scattering_type='total',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['xray_pdf'].show_supported_peak_profile_types()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['xray_pdf'].show_current_peak_profile_type()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['xray_pdf'].peak_profile_type = 'gaussian-damped-sinc'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['xray_pdf'].peak.damp_q = 0.03\n", + "project.experiments['xray_pdf'].peak.broad_q = 0\n", + "project.experiments['xray_pdf'].peak.cutoff_q = 21\n", + "project.experiments['xray_pdf'].peak.sharp_delta_1 = 0\n", + "project.experiments['xray_pdf'].peak.sharp_delta_2 = 5\n", + "project.experiments['xray_pdf'].peak.damp_particle_diameter = 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['xray_pdf'].linked_phases.create(id='nacl', scale=0.5)" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "## Select Fitting Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['nacl'].cell.length_a.free = True\n", + "project.structures['nacl'].atom_sites['Na'].b_iso.free = True\n", + "project.structures['nacl'].atom_sites['Cl'].b_iso.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['xray_pdf'].linked_phases['nacl'].scale.free = True\n", + "project.experiments['xray_pdf'].peak.damp_q.free = True\n", + "project.experiments['xray_pdf'].peak.sharp_delta_2.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "## Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "## Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='xray_pdf')" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-13.ipynb b/docs/docs/tutorials/ed-13.ipynb new file mode 100644 index 00000000..d35f3628 --- /dev/null +++ b/docs/docs/tutorials/ed-13.ipynb @@ -0,0 +1,2923 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Fitting Powder Diffraction data\n", + "\n", + "This notebook guides you through the Rietveld refinement of crystal\n", + "structures using simulated powder diffraction data. It consists of two\n", + "parts:\n", + "- Introduction: A simple reference fit using silicon (Si) crystal\n", + " structure.\n", + "- Exercise: A more complex fit using La₀.₅Ba₀.₅CoO₃ (LBCO) crystal\n", + " structure.\n", + "\n", + "## 🛠️ Import Library\n", + "\n", + "We start by importing the necessary library for the analysis. In this\n", + "notebook, we use the EasyDiffraction library. As mentioned in the\n", + "introduction to EasyScience, EasyDiffraction is built on that\n", + "framework and offers a high-level interface focused specifically for\n", + "diffraction analysis.\n", + "\n", + "This notebook is self-contained and designed for hands-on learning.\n", + "However, if you're interested in exploring more advanced features or\n", + "learning about additional capabilities of the EasyDiffraction library,\n", + "please refer to the official documentation:\n", + "https://docs.easydiffraction.org/lib\n", + "\n", + "Depending on your requirements, you may choose to import only specific\n", + "classes. However, for the sake of simplicity in this notebook, we will\n", + "import the entire library." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/first-steps/#importing-easydiffraction)\n", + "for more details about importing the EasyDiffraction library and its\n", + "components." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## 📘 Introduction: Simple Reference Fit – Si\n", + "\n", + "Before diving into the more complex fitting exercise with the\n", + "La₀.₅Ba₀.₅CoO₃ (LBCO) crystal structure, let's start with a simpler\n", + "example using the silicon (Si) crystal structure. This will help us\n", + "understand the basic concepts and steps involved in fitting a crystal\n", + "structure using powder diffraction data.\n", + "\n", + "For this part of the notebook, we will use the powder diffraction data\n", + "previously simulated using the Si crystal structure.\n", + "\n", + "### 📦 Create a Project – 'reference'\n", + "\n", + "In EasyDiffraction, a project serves as a container for all\n", + "information related to the analysis of a specific experiment or set of\n", + "experiments. It enables you to organize your data, experiments,\n", + "crystal structures, and fitting parameters in an organized manner. You\n", + "can think of it as a folder containing all the essential details about\n", + "your analysis. The project also allows us to visualize both the\n", + "measured and calculated diffraction patterns, among other things." + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/project/)\n", + "for more details about creating a project and its purpose in the\n", + "analysis workflow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "project_1 = ed.Project(name='reference')" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "You can set the title and description of the project to provide\n", + "context and information about the analysis being performed. This is\n", + "useful for documentation purposes and helps others (or yourself in the\n", + "future) understand the purpose of the project at a glance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.info.title = 'Reference Silicon Fit'\n", + "project_1.info.description = 'Fitting simulated powder diffraction pattern of Si.'" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "### 🔬 Create an Experiment\n", + "\n", + "An experiment represents a specific diffraction measurement performed\n", + "on a specific sample using a particular instrument. It contains\n", + "details about the measured data, instrument parameters, and other\n", + "relevant information." + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/)\n", + "for more details about experiments and their purpose in the analysis\n", + "workflow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "data_dir = 'data'\n", + "file_name = 'reduced_Si.xye'\n", + "si_xye_path = f'{data_dir}/{file_name}'" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "Uncomment the following cell if your data reduction failed and the\n", + "reduced data file is missing. In this case, you can download our\n", + "pre-generated reduced data file from the EasyDiffraction repository.\n", + "The `download_data` function will not overwrite an existing file\n", + "unless you set `overwrite=True`, so it's safe to run even if the\n", + "file is already present." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "si_xye_path = ed.download_data(id=17, destination=data_dir)" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "Now we can create the experiment and load the measured data. In this\n", + "case, the experiment is defined as a powder diffraction measurement\n", + "using time-of-flight neutrons. The measured data is loaded from a file\n", + "containing the reduced diffraction pattern of Si from the data\n", + "reduction notebook." + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#defining-an-experiment-manually)\n", + "for more details about different types of experiments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.experiments.add_from_data_path(\n", + " name='sim_si',\n", + " data_path=si_xye_path,\n", + " sample_form='powder',\n", + " beam_mode='time-of-flight',\n", + " radiation_probe='neutron',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "#### Inspect Measured Data\n", + "\n", + "After creating the experiment, we can examine the measured data. The\n", + "measured data consists of a diffraction pattern having time-of-flight\n", + "(TOF) values and corresponding intensities. The TOF values are given\n", + "in microseconds (μs), and the intensities are in arbitrary units.\n", + "\n", + "The data is stored in XYE format, a simple text format containing\n", + "three columns: TOF, intensity, and intensity error (if available)." + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#measured-data-category)\n", + "for more details about the measured data and its format.\n", + "\n", + "To visualize the measured data, we can use the `plot_meas` method of\n", + "the project. Before plotting, we need to set the plotting engine to\n", + "'plotly', which provides interactive visualizations." + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://easyscience.github.io/diffraction-lib/user-guide/first-steps/#supported-plotters)\n", + "for more details about setting the plotting engine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "# Keep the auto-selected engine. Alternatively, you can uncomment the\n", + "# line below to explicitly set the engine to the required one.\n", + "# project.plotter.engine = 'plotly'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.plot_meas(expt_name='sim_si')" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "If you zoom in on the highest TOF peak (around 120,000 μs), you will\n", + "notice that it has a broad and unusual shape. This distortion, along\n", + "with additional effects on the low TOF peaks, is most likely an\n", + "artifact related to the simplifications made during the simulation\n", + "and/or reduction process and is currently under investigation.\n", + "However, this is outside the scope of this school. Therefore, we will\n", + "simply exclude both the low and high TOF regions from the analysis by\n", + "adding an excluded regions to the experiment.\n", + "\n", + "In real experiments, it is often necessary to exclude certain regions\n", + "from the measured data. For example, the direct beam can significantly\n", + "increase the background at very low angles, making those parts of the\n", + "diffractogram unreliable. Additionally, sample environment components\n", + "may introduce unwanted peaks. In such cases, excluding specific\n", + "regions is often simpler and more effective than modeling them with an\n", + "additional sample phase." + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#excluded-regions-category)\n", + "for more details about excluding regions from the measured data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.experiments['sim_si'].excluded_regions.create(id='1', start=0, end=55000)\n", + "project_1.experiments['sim_si'].excluded_regions.create(id='2', start=105500, end=200000)" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "To visualize the effect of excluding the high TOF region, we can plot\n", + "the measured data again. The excluded region will be omitted from the\n", + "plot and is not used in the fitting process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.plot_meas(expt_name='sim_si')" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "#### Set Instrument Parameters\n", + "\n", + "After the experiment is created and measured data is loaded, we need\n", + "to set the instrument parameters.\n", + "\n", + "In this type of experiment, the instrument parameters define how the\n", + "measured data is converted between d-spacing and time-of-flight (TOF)\n", + "during the data reduction process as well as the angular position of\n", + "the detector. So, we put values based on those from the reduction.\n", + "These values can be found in the header of the corresponding .XYE\n", + "file. Their names are `two_theta` and `DIFC`, which stand for the\n", + "two-theta angle and the linear conversion factor from d-spacing to\n", + "TOF, respectively.\n", + "\n", + "You can set them manually, but it is more convenient to use the\n", + "`get_value_from_xye_header` function from the EasyDiffraction library." + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#instrument-category)\n", + "for more details about the instrument parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.experiments['sim_si'].instrument.setup_twotheta_bank = ed.get_value_from_xye_header(\n", + " si_xye_path, 'two_theta'\n", + ")\n", + "project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = ed.get_value_from_xye_header(\n", + " si_xye_path, 'DIFC'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "Before proceeding, let's take a quick look at the concept of\n", + "parameters in EasyDiffraction, which is similar to the parameter\n", + "concept in EasyScience. The current version of EasyDiffraction is\n", + "transitioning to reuse the parameter system from EasyScience.\n", + "\n", + "That is, every parameter is an object, which has different attributes,\n", + "such as `value`, `units`, etc. To display the parameter of interest,\n", + "you can simply print the parameter object.\n", + "\n", + "For example, to display the linear conversion factor from d-spacing to\n", + "TOF, which is the `calib_d_to_tof_linear` parameter, you can do the\n", + "following:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "print(project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear)" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "The `value` attribute represents the current value of the parameter as\n", + "a float. You can access it directly by using the `value` attribute of\n", + "the parameter. This is useful when you want to use the parameter value\n", + "in calculations or when you want to assign it to another parameter.\n", + "For example, to get only the value of the same parameter as floating\n", + "point number, but not the whole object, you can do the following:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "print(project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear.value)" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "Note that to set the value of the parameter, you can simply assign a\n", + "new value to the parameter object without using the `value` attribute,\n", + "as we did above." + ] + }, + { + "cell_type": "markdown", + "id": "34", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/parameters/)\n", + "for more details about parameters in EasyDiffraction and their\n", + "attributes." + ] + }, + { + "cell_type": "markdown", + "id": "35", + "metadata": {}, + "source": [ + "#### Set Peak Profile Parameters\n", + "\n", + "The next set of parameters is needed to define the peak profile used\n", + "in the fitting process. The peak profile describes the shape of the\n", + "diffraction peaks. They include parameters for the broadening and\n", + "asymmetry of the peaks.\n", + "\n", + "There are several commonly used peak profile functions:\n", + "- **Gaussian**: Describes peaks with a symmetric bell-shaped curve,\n", + " often used when instrumental broadening dominates. [Click for more\n", + " details.](https://mantidproject.github.io/docs-versioned/v6.1.0/fitting/fitfunctions/Gaussian.html)\n", + "- **Lorentzian**: Produces narrower central peaks with longer tails,\n", + " frequently used to model size broadening effects. [Click for more\n", + " details.](https://mantidproject.github.io/docs-versioned/v6.1.0/fitting/fitfunctions/Lorentzian.html)\n", + "- **Pseudo-Voigt**: A linear combination of Gaussian and Lorentzian\n", + " components, providing flexibility to represent real diffraction\n", + " peaks. [Click for more\n", + " details.](https://mantidproject.github.io/docs-versioned/v6.1.0/fitting/fitfunctions/PseudoVoigt.html)\n", + "- **Pseudo-Voigt convoluted with Ikeda-Carpenter**: Incorporates the\n", + " asymmetry introduced by the neutron pulse shape in time-of-flight\n", + " instruments. This is a common choice for TOF neutron powder\n", + " diffraction data. [Click for more\n", + " details.](https://docs.mantidproject.org/v6.1.0/fitting/fitfunctions/IkedaCarpenterPV.html)\n", + "\n", + "Here, we use a pseudo-Voigt peak profile function with Ikeda-Carpenter\n", + "asymmetry.\n", + "\n", + "The parameter values are typically determined experimentally on the\n", + "same instrument and under the same configuration as the data being\n", + "analyzed, using measurements of a standard sample. In our case, the Si\n", + "sample serves as this standard reference. We will refine the peak\n", + "profile parameters here, and these refined values will be used as\n", + "starting points for the more complex fit in the next part of the\n", + "notebook. For this initial fit, we will provide reasonable physical\n", + "guesses as starting values." + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#peak-category)\n", + "for more details about the peak profile types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.experiments['sim_si'].peak_profile_type = 'pseudo-voigt * ikeda-carpenter'\n", + "project_1.experiments['sim_si'].peak.broad_gauss_sigma_0 = 69498\n", + "project_1.experiments['sim_si'].peak.broad_gauss_sigma_1 = -55578\n", + "project_1.experiments['sim_si'].peak.broad_gauss_sigma_2 = 14560\n", + "project_1.experiments['sim_si'].peak.broad_mix_beta_0 = 0.0019\n", + "project_1.experiments['sim_si'].peak.broad_mix_beta_1 = 0.0137\n", + "project_1.experiments['sim_si'].peak.asym_alpha_0 = -0.0055\n", + "project_1.experiments['sim_si'].peak.asym_alpha_1 = 0.0147" + ] + }, + { + "cell_type": "markdown", + "id": "38", + "metadata": {}, + "source": [ + "#### Set Background\n", + "\n", + "The background of the diffraction pattern represents the portion of\n", + "the pattern that is not related to the crystal structure of the\n", + "sample. It's rather represents noise and other sources of scattering\n", + "that can affect the measured intensities. This includes contributions\n", + "from the instrument, the sample holder, the sample environment, and\n", + "other sources of incoherent scattering.\n", + "\n", + "The background can be modeled in various ways. In this example, we\n", + "will use a simple line segment background, which is a common approach\n", + "for powder diffraction data. The background intensity at any point is\n", + "defined by linear interpolation between neighboring points. The\n", + "background points are selected to span the range of the diffraction\n", + "pattern while avoiding the peaks.\n", + "\n", + "We will add several background points at specific TOF values (in μs)\n", + "and corresponding intensity values. These points are chosen to\n", + "represent the background level in the diffraction pattern free from\n", + "any peaks.\n", + "\n", + "The background points are added using the `add` method of the\n", + "`background` object. The `x` parameter represents the TOF value, and\n", + "the `y` parameter represents the intensity value at that TOF.\n", + "\n", + "Let's set all the background points at a constant value of 0.01, which\n", + "can be roughly estimated by the eye, and we will refine them later\n", + "during the fitting process." + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#background-category)\n", + "for more details about the background and its types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.experiments['sim_si'].background_type = 'line-segment'\n", + "project_1.experiments['sim_si'].background.create(id='1', x=50000, y=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='2', x=60000, y=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='3', x=70000, y=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='4', x=80000, y=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='5', x=90000, y=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='6', x=100000, y=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='7', x=110000, y=0.01)" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "### 🧩 Create a Structure – Si\n", + "\n", + "After setting up the experiment, we need to create a structure that\n", + "describes the crystal structure of the sample being analyzed.\n", + "\n", + "In this case, we will create a structure for silicon (Si) with a\n", + "cubic crystal structure. The structure contains information about\n", + "the space group, lattice parameters, atomic positions of the atoms in\n", + "the unit cell, atom types, occupancies and atomic displacement\n", + "parameters. The structure is essential for the fitting process, as\n", + "it is used to calculate the expected diffraction pattern.\n", + "\n", + "EasyDiffraction refines the crystal structure of the sample, but does\n", + "not solve it. Therefore, we need a good starting point with reasonable\n", + "structural parameters.\n", + "\n", + "Here, we define the Si structure as a cubic structure. As this is a\n", + "cubic structure, we only need to define the single lattice parameter,\n", + "which is the length of the unit cell edge. The Si crystal structure\n", + "has a single atom in the unit cell, which is located at the origin (0,\n", + "0, 0) of the unit cell. The symmetry of this site is defined by the\n", + "Wyckoff letter 'a'. The atomic displacement parameter defines the\n", + "thermal vibrations of the atoms in the unit cell and is presented as\n", + "an isotropic parameter (B_iso).\n", + "\n", + "Sometimes, the initial crystal structure parameters can be obtained\n", + "from one of the crystallographic databases, like for example the\n", + "Crystallography Open Database (COD). In this case, we use the COD\n", + "entry for silicon as a reference for the initial crystal structure\n", + "model: https://www.crystallography.net/cod/4507226.html\n", + "\n", + "Usually, the crystal structure parameters are provided in a CIF file\n", + "format, which is a standard format for crystallographic data. An\n", + "example of a CIF file for silicon is shown below. The CIF file\n", + "contains the space group information, unit cell parameters, and atomic\n", + "positions." + ] + }, + { + "cell_type": "markdown", + "id": "42", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/data-format/)\n", + "for more details about the CIF format and its use in EasyDiffraction." + ] + }, + { + "cell_type": "markdown", + "id": "43", + "metadata": {}, + "source": [ + "```\n", + "data_si\n", + "\n", + "_space_group.name_H-M_alt \"F d -3 m\"\n", + "_space_group.IT_coordinate_system_code 2\n", + "\n", + "_cell.length_a 5.43\n", + "_cell.length_b 5.43\n", + "_cell.length_c 5.43\n", + "_cell.angle_alpha 90.0\n", + "_cell.angle_beta 90.0\n", + "_cell.angle_gamma 90.0\n", + "\n", + "loop_\n", + "_atom_site.label\n", + "_atom_site.type_symbol\n", + "_atom_site.fract_x\n", + "_atom_site.fract_y\n", + "_atom_site.fract_z\n", + "_atom_site.wyckoff_letter\n", + "_atom_site.occupancy\n", + "_atom_site.ADP_type\n", + "_atom_site.B_iso_or_equiv\n", + "Si Si 0 0 0 a 1.0 Biso 0.89\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "44", + "metadata": {}, + "source": [ + "As with adding the experiment in the previous step, we will create a\n", + "default structure and then modify its parameters to match the Si\n", + "structure." + ] + }, + { + "cell_type": "markdown", + "id": "45", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/)\n", + "for more details about structures and their purpose in the data\n", + "analysis workflow." + ] + }, + { + "cell_type": "markdown", + "id": "46", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.structures.create(name='si')" + ] + }, + { + "cell_type": "markdown", + "id": "48", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "markdown", + "id": "49", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/#space-group-category)\n", + "for more details about the space group." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.structures['si'].space_group.name_h_m = 'F d -3 m'\n", + "project_1.structures['si'].space_group.it_coordinate_system_code = '2'" + ] + }, + { + "cell_type": "markdown", + "id": "51", + "metadata": {}, + "source": [ + "#### Set Lattice Parameters" + ] + }, + { + "cell_type": "markdown", + "id": "52", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/#cell-category)\n", + "for more details about the unit cell parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.structures['si'].cell.length_a = 5.43" + ] + }, + { + "cell_type": "markdown", + "id": "54", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "markdown", + "id": "55", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/#atom-sites-category)\n", + "for more details about the atom sites category." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.structures['si'].atom_sites.create(\n", + " label='Si',\n", + " type_symbol='Si',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.89,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "57", + "metadata": {}, + "source": [ + "### 🔗 Assign Structure to Experiment\n", + "\n", + "Now we need to assign, or link, this structure to the experiment\n", + "created above. This linked crystallographic phase will be used to\n", + "calculate the expected diffraction pattern based on the crystal\n", + "structure defined in the structure." + ] + }, + { + "cell_type": "markdown", + "id": "58", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#linked-phases-category)\n", + "for more details about linking a structure to an experiment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.experiments['sim_si'].linked_phases.create(id='si', scale=1.0)" + ] + }, + { + "cell_type": "markdown", + "id": "60", + "metadata": {}, + "source": [ + "### 🚀 Analyze and Fit the Data\n", + "\n", + "After setting up the experiment and structure, we can now analyze\n", + "the measured diffraction pattern and perform the fit. Building on the\n", + "analogies from the EasyScience library and the previous notebooks, we\n", + "can say that all the parameters we introduced earlier — those defining\n", + "the structure (crystal structure parameters) and the experiment\n", + "(instrument, background, and peak profile parameters) — together form\n", + "the complete set of parameters that can be refined during the fitting\n", + "process.\n", + "\n", + "Unlike in the previous analysis notebooks, we will not create a\n", + "**math_model** object here. The mathematical model used to calculate\n", + "the expected diffraction pattern is already defined in the library and\n", + "will be applied automatically during the fitting process." + ] + }, + { + "cell_type": "markdown", + "id": "61", + "metadata": { + "title": "**Reminder:**" + }, + "source": [ + "\n", + "The fitting process involves comparing the measured diffraction\n", + "pattern with the calculated diffraction pattern based on the crystal\n", + "structure and instrument parameters. The goal is to adjust the\n", + "parameters of the structure and the experiment to minimize the\n", + "difference between the measured and calculated diffraction patterns.\n", + "This is done by refining the parameters of the structure and the\n", + "instrument settings to achieve a better fit." + ] + }, + { + "cell_type": "markdown", + "id": "62", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/analysis/#minimization-optimization)\n", + "for more details about the fitting process in EasyDiffraction." + ] + }, + { + "cell_type": "markdown", + "id": "63", + "metadata": {}, + "source": [ + "#### Set Fit Parameters\n", + "\n", + "To perform the fit, we need to specify the refinement parameters.\n", + "These are the parameters that will be adjusted during the fitting\n", + "process to minimize the difference between the measured and calculated\n", + "diffraction patterns. This is done by setting the `free` attribute of\n", + "the corresponding parameters to `True`.\n", + "\n", + "Note: setting `param.free = True` is equivalent to using `param.fixed\n", + "= False` in the EasyScience library.\n", + "\n", + "We will refine the scale factor of the Si phase, the intensities of\n", + "the background points as well as the peak profile parameters. The\n", + "structure parameters of the Si phase will not be refined, as this\n", + "sample is considered a reference sample with known parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.experiments['sim_si'].linked_phases['si'].scale.free = True\n", + "\n", + "for line_segment in project_1.experiments['sim_si'].background:\n", + " line_segment.y.free = True\n", + "\n", + "project_1.experiments['sim_si'].peak.broad_gauss_sigma_0.free = True\n", + "project_1.experiments['sim_si'].peak.broad_gauss_sigma_1.free = True\n", + "project_1.experiments['sim_si'].peak.broad_gauss_sigma_2.free = True\n", + "project_1.experiments['sim_si'].peak.broad_mix_beta_0.free = True\n", + "project_1.experiments['sim_si'].peak.broad_mix_beta_1.free = True\n", + "project_1.experiments['sim_si'].peak.asym_alpha_0.free = True\n", + "project_1.experiments['sim_si'].peak.asym_alpha_1.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "65", + "metadata": {}, + "source": [ + "#### Show Free Parameters\n", + "\n", + "We can check which parameters are free to be refined by calling the\n", + "`show_free_params` method of the `analysis` object of the project." + ] + }, + { + "cell_type": "markdown", + "id": "66", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://easyscience.github.io/diffraction-lib/user-guide/first-steps/#available-parameters)\n", + "for more details on how to\n", + "- show all parameters of the project,\n", + "- show all fittable parameters, and\n", + "- show only free parameters of the project." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "68", + "metadata": {}, + "source": [ + "#### Visualize Diffraction Patterns\n", + "\n", + "Before performing the fit, we can visually compare the measured\n", + "diffraction pattern with the calculated diffraction pattern based on\n", + "the initial parameters of the structure and the instrument. This\n", + "provides an indication of how well the initial parameters match the\n", + "measured data. The `plot_meas_vs_calc` method of the project allows\n", + "this comparison." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.plot_meas_vs_calc(expt_name='sim_si')" + ] + }, + { + "cell_type": "markdown", + "id": "70", + "metadata": {}, + "source": [ + "#### Run Fitting\n", + "\n", + "We can now perform the fit using the `fit` method of the `analysis`\n", + "object of the project." + ] + }, + { + "cell_type": "markdown", + "id": "71", + "metadata": { + "tags": [ + "doc-link" + ] + }, + "source": [ + "📖 See\n", + "[documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/analysis/#perform-fit)\n", + "for more details about the fitting process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.analysis.fit()\n", + "project_1.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "73", + "metadata": {}, + "source": [ + "#### Check Fit Results\n", + "\n", + "You can see that the agreement between the measured and calculated\n", + "diffraction patterns is now much improved and that the intensities of\n", + "the calculated peaks align much better with the measured peaks. To\n", + "check the quality of the fit numerically, we can look at the\n", + "goodness-of-fit χ² value and the reliability R-factors. The χ² value\n", + "is a measure of how well the calculated diffraction pattern matches\n", + "the measured pattern, and it is calculated as the sum of the squared\n", + "differences between the measured and calculated intensities, divided\n", + "by the number of data points. Ideally, the χ² value should be close to\n", + "1, indicating a good fit." + ] + }, + { + "cell_type": "markdown", + "id": "74", + "metadata": {}, + "source": [ + "#### Visualize Fit Results\n", + "\n", + "After the fit is completed, we can plot the comparison between the\n", + "measured and calculated diffraction patterns again to see how well the\n", + "fit improved the agreement between the two. The calculated diffraction\n", + "pattern is now based on the refined parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.plot_meas_vs_calc(expt_name='sim_si')" + ] + }, + { + "cell_type": "markdown", + "id": "76", + "metadata": {}, + "source": [ + "#### TOF vs d-spacing\n", + "\n", + "The diffraction pattern is typically analyzed and plotted in the\n", + "time-of-flight (TOF) axis, which represents the time it takes for\n", + "neutrons to travel from the sample to the detector. However, it is\n", + "sometimes more convenient to visualize the diffraction pattern in the\n", + "d-spacing axis, which represents the distance between planes in the\n", + "crystal lattice.\n", + "\n", + "The conversion from d-spacing to TOF was already introduced in the\n", + "data reduction notebook. As a reminder, the two are related through\n", + "the instrument calibration parameters according to the equation:\n", + "\n", + "$$ \\text{TOF} = \\text{offset} + \\text{linear} \\cdot d + \\text{quad}\n", + "\\cdot d^{2}, $$\n", + "\n", + "where `offset`, `linear`, and `quad` are calibration parameters.\n", + "\n", + "In our case, only the `linear` term is used (the\n", + "`calib_d_to_tof_linear` parameter we set earlier). The `offset` and\n", + "`quad` terms were not part of the data reduction and are therefore set\n", + "to 0 by default.\n", + "\n", + "The `plot_meas_vs_calc` method of the project allows us to plot the\n", + "measured and calculated diffraction patterns in the d-spacing axis by\n", + "setting the `d_spacing` parameter to `True`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.plot_meas_vs_calc(expt_name='sim_si', x='d_spacing')" + ] + }, + { + "cell_type": "markdown", + "id": "78", + "metadata": {}, + "source": [ + "As you can see, the calculated diffraction pattern now matches the\n", + "measured pattern much more closely. Typically, additional experimental\n", + "parameters are included in the refinement process to further improve\n", + "the fit. In this example, the structural parameters are not refined\n", + "because the Si crystal structure is a well-known standard reference\n", + "used to calibrate both the instrument and the experimental setup. The\n", + "refined experimental parameters obtained here will then be applied\n", + "when fitting the crystal structures of other materials.\n", + "\n", + "In the next part of the notebook, we will move to a more advanced case\n", + "and fit a more complex crystal structure: La₀.₅Ba₀.₅CoO₃ (LBCO).\n", + "\n", + "#### Save Project\n", + "\n", + "Before moving on, we can save the project to disk for later use. This\n", + "will preserve the entire project structure, including experiments,\n", + "structures, and fitting results. The project is saved into a\n", + "directory specified by the `dir_path` attribute of the project object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79", + "metadata": {}, + "outputs": [], + "source": [ + "project_1.save_as(dir_path='powder_diffraction_Si')" + ] + }, + { + "cell_type": "markdown", + "id": "80", + "metadata": {}, + "source": [ + "## 💪 Exercise: Complex Fit – LBCO\n", + "\n", + "Now that you have a basic understanding of the fitting process, we\n", + "will undertake a more complex fit of the La₀.₅Ba₀.₅CoO₃ (LBCO) crystal\n", + "structure using simulated powder diffraction data from the data\n", + "reduction notebook.\n", + "\n", + "You can use the same approach as in the previous part of the notebook,\n", + "but this time we will refine a more complex crystal structure LBCO\n", + "with multiple atoms in the unit cell.\n", + "\n", + "### 📦 Exercise 1: Create a Project\n", + "\n", + "Create a new project for the LBCO fit." + ] + }, + { + "cell_type": "markdown", + "id": "81", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "82", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can use the same approach as in the previous part of the notebook,\n", + "but this time we will create a new project for the LBCO fit." + ] + }, + { + "cell_type": "markdown", + "id": "83", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2 = ed.Project(name='main')\n", + "project_2.info.title = 'La0.5Ba0.5CoO3 Fit'\n", + "project_2.info.description = 'Fitting simulated powder diffraction pattern of La0.5Ba0.5CoO3.'" + ] + }, + { + "cell_type": "markdown", + "id": "85", + "metadata": {}, + "source": [ + "### 🔬 Exercise 2: Define an Experiment\n", + "\n", + "#### Exercise 2.1: Create an Experiment\n", + "\n", + "Create an experiment within the new project and load the reduced\n", + "diffraction pattern for LBCO." + ] + }, + { + "cell_type": "markdown", + "id": "86", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "87", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can use the same approach as in the previous part of the notebook,\n", + "but this time you need to use the data file for LBCO." + ] + }, + { + "cell_type": "markdown", + "id": "88", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "data_dir = 'data'\n", + "file_name = 'reduced_LBCO.xye'\n", + "lbco_xye_path = f'{data_dir}/{file_name}'\n", + "\n", + "# Uncomment the following line if your data reduction failed and the\n", + "# reduced data file is missing.\n", + "lbco_xye_path = ed.download_data(id=18, destination=data_dir)\n", + "\n", + "project_2.experiments.add_from_data_path(\n", + " name='sim_lbco',\n", + " data_path=lbco_xye_path,\n", + " sample_form='powder',\n", + " beam_mode='time-of-flight',\n", + " radiation_probe='neutron',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "90", + "metadata": {}, + "source": [ + "#### Exercise 2.1: Inspect Measured Data\n", + "\n", + "Check the measured data of the LBCO experiment. Are there any peaks\n", + "with the shape similar to those excluded in the Si fit? If so, exclude\n", + "them from this analysis as well." + ] + }, + { + "cell_type": "markdown", + "id": "91", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "92", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can use the `plot_meas` method of the project to visualize the\n", + "measured diffraction pattern. You can also use the `excluded_regions`\n", + "attribute of the experiment to exclude specific regions from the\n", + "analysis as we did in the previous part of the notebook." + ] + }, + { + "cell_type": "markdown", + "id": "93", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.plot_meas(expt_name='sim_lbco')\n", + "\n", + "project_2.experiments['sim_lbco'].excluded_regions.create(id='1', start=0, end=55000)\n", + "project_2.experiments['sim_lbco'].excluded_regions.create(id='2', start=105500, end=200000)\n", + "\n", + "project_2.plot_meas(expt_name='sim_lbco')" + ] + }, + { + "cell_type": "markdown", + "id": "95", + "metadata": {}, + "source": [ + "#### Exercise 2.2: Set Instrument Parameters\n", + "\n", + "Set the instrument parameters for the LBCO experiment." + ] + }, + { + "cell_type": "markdown", + "id": "96", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "97", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the values from the data reduction process for the LBCO and\n", + "follow the same approach as in the previous part of the notebook." + ] + }, + { + "cell_type": "markdown", + "id": "98", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.experiments['sim_lbco'].instrument.setup_twotheta_bank = ed.get_value_from_xye_header(\n", + " lbco_xye_path, 'two_theta'\n", + ")\n", + "project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = ed.get_value_from_xye_header(\n", + " lbco_xye_path, 'DIFC'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "100", + "metadata": {}, + "source": [ + "#### Exercise 2.3: Set Peak Profile Parameters\n", + "\n", + "Set the peak profile parameters for the LBCO experiment." + ] + }, + { + "cell_type": "markdown", + "id": "101", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "102", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the values from the\n", + "previous part of the notebook. You can either manually copy the values\n", + "from the Si fit or use the `value` attribute of the parameters from\n", + "the Si experiment to set the initial values for the LBCO experiment.\n", + "This will help us to have a good starting point for the fit." + ] + }, + { + "cell_type": "markdown", + "id": "103", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "104", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "# # Create a reference to the peak profile parameters from the Si\n", + "sim_si_peak = project_1.experiments['sim_si'].peak\n", + "project_2.experiments['sim_lbco'].peak_profile_type = 'pseudo-voigt * ikeda-carpenter'\n", + "project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_0 = sim_si_peak.broad_gauss_sigma_0.value\n", + "project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_1 = sim_si_peak.broad_gauss_sigma_1.value\n", + "project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_2 = sim_si_peak.broad_gauss_sigma_2.value\n", + "project_2.experiments['sim_lbco'].peak.broad_mix_beta_0 = sim_si_peak.broad_mix_beta_0.value\n", + "project_2.experiments['sim_lbco'].peak.broad_mix_beta_1 = sim_si_peak.broad_mix_beta_1.value\n", + "project_2.experiments['sim_lbco'].peak.asym_alpha_0 = sim_si_peak.asym_alpha_0.value\n", + "project_2.experiments['sim_lbco'].peak.asym_alpha_1 = sim_si_peak.asym_alpha_1.value" + ] + }, + { + "cell_type": "markdown", + "id": "105", + "metadata": {}, + "source": [ + "#### Exercise 2.4: Set Background\n", + "\n", + "Set the background points for the LBCO experiment. What would you\n", + "suggest as the initial intensity value for the background points?" + ] + }, + { + "cell_type": "markdown", + "id": "106", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "107", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the same approach as in the previous part of the notebook, but\n", + "this time you need to set the background points for the LBCO\n", + "experiment. You can zoom in on the measured diffraction pattern to\n", + "determine the approximate background level." + ] + }, + { + "cell_type": "markdown", + "id": "108", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "109", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.experiments['sim_lbco'].background_type = 'line-segment'\n", + "project_2.experiments['sim_lbco'].background.create(id='1', x=50000, y=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='2', x=60000, y=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='3', x=70000, y=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='4', x=80000, y=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='5', x=90000, y=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='6', x=100000, y=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='7', x=110000, y=0.2)" + ] + }, + { + "cell_type": "markdown", + "id": "110", + "metadata": {}, + "source": [ + "### 🧩 Exercise 3: Define a Structure – LBCO\n", + "\n", + "The LBSO structure is not as simple as the Si one, as it contains\n", + "multiple atoms in the unit cell. It is not in COD, so we give you the\n", + "structural parameters in CIF format to create the structure.\n", + "\n", + "Note that those parameters are not necessarily the most accurate ones,\n", + "but they are a good starting point for the fit. The aim of the study\n", + "is to refine the LBCO lattice parameters." + ] + }, + { + "cell_type": "markdown", + "id": "111", + "metadata": {}, + "source": [ + "```\n", + "data_lbco\n", + "\n", + "_space_group.name_H-M_alt \"P m -3 m\"\n", + "_space_group.IT_coordinate_system_code 1\n", + "\n", + "_cell.length_a 3.89\n", + "_cell.length_b 3.89\n", + "_cell.length_c 3.89\n", + "_cell.angle_alpha 90.0\n", + "_cell.angle_beta 90.0\n", + "_cell.angle_gamma 90.0\n", + "\n", + "loop_\n", + "_atom_site.label\n", + "_atom_site.type_symbol\n", + "_atom_site.fract_x\n", + "_atom_site.fract_y\n", + "_atom_site.fract_z\n", + "_atom_site.wyckoff_letter\n", + "_atom_site.occupancy\n", + "_atom_site.ADP_type\n", + "_atom_site.B_iso_or_equiv\n", + "La La 0.0 0.0 0.0 a 0.5 Biso 0.95\n", + "Ba Ba 0.0 0.0 0.0 a 0.5 Biso 0.95\n", + "Co Co 0.5 0.5 0.5 b 1.0 Biso 0.80\n", + "O O 0.0 0.5 0.5 c 1.0 Biso 1.66\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "112", + "metadata": {}, + "source": [ + "Note that the `occupancy` of the La and Ba atoms is 0.5\n", + "and those atoms are located in the same position (0, 0, 0) in the unit\n", + "cell. This means that an extra attribute `occupancy` needs to be set\n", + "for those atoms later in the structure.\n", + "\n", + "We model the La/Ba site using the virtual crystal approximation. In\n", + "this approach, the scattering is taken as a weighted average of La and\n", + "Ba. This reproduces the average diffraction pattern well but does not\n", + "capture certain real-world effects.\n", + "\n", + "The edge cases are:\n", + "- **Random distribution**. La and Ba atoms are placed randomly. The\n", + " Bragg peaks still match the average structure, but the pattern also\n", + " shows extra background (diffuse scattering) between the peaks, but\n", + " this is usually neglected in the analysis.\n", + "- **Perfect ordering**. La and Ba arrange themselves in a regular\n", + " pattern, creating a larger repeating unit. This gives rise to extra\n", + " peaks (\"superlattice reflections\") and changes the intensity of\n", + " some existing peaks.\n", + "- **Virtual crystal approximation (our model)**. We replace the site\n", + " with a single \"virtual atom\" that averages La and Ba. This gives\n", + " the correct average Bragg peaks but leaves out the extra background\n", + " of the random case and the extra peaks of the ordered case." + ] + }, + { + "cell_type": "markdown", + "id": "113", + "metadata": {}, + "source": [ + "#### Exercise 3.1: Create Structure\n", + "\n", + "Add a structure for LBCO to the project. The structure\n", + "parameters will be set in the next exercises." + ] + }, + { + "cell_type": "markdown", + "id": "114", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "115", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can use the same approach as in the previous part of the notebook,\n", + "but this time you need to use the name corresponding to the LBCO\n", + "structure, e.g. 'lbco'." + ] + }, + { + "cell_type": "markdown", + "id": "116", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "117", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.structures.create(name='lbco')" + ] + }, + { + "cell_type": "markdown", + "id": "118", + "metadata": {}, + "source": [ + "#### Exercise 3.2: Set Space Group\n", + "\n", + "Set the space group for the LBCO structure." + ] + }, + { + "cell_type": "markdown", + "id": "119", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "120", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the space group name and IT coordinate system code from the CIF\n", + "data." + ] + }, + { + "cell_type": "markdown", + "id": "121", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "122", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.structures['lbco'].space_group.name_h_m = 'P m -3 m'\n", + "project_2.structures['lbco'].space_group.it_coordinate_system_code = '1'" + ] + }, + { + "cell_type": "markdown", + "id": "123", + "metadata": {}, + "source": [ + "#### Exercise 3.3: Set Lattice Parameters\n", + "\n", + "Set the lattice parameters for the LBCO structure." + ] + }, + { + "cell_type": "markdown", + "id": "124", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "125", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the lattice parameters from the CIF data." + ] + }, + { + "cell_type": "markdown", + "id": "126", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "127", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.structures['lbco'].cell.length_a = 3.88" + ] + }, + { + "cell_type": "markdown", + "id": "128", + "metadata": {}, + "source": [ + "#### Exercise 3.4: Set Atom Sites\n", + "\n", + "Set the atom sites for the LBCO structure." + ] + }, + { + "cell_type": "markdown", + "id": "129", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "130", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the atom sites from the CIF data. You can use the `add` method of\n", + "the `atom_sites` attribute of the structure to add the atom sites." + ] + }, + { + "cell_type": "markdown", + "id": "131", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "132", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.structures['lbco'].atom_sites.create(\n", + " label='La',\n", + " type_symbol='La',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.95,\n", + " occupancy=0.5,\n", + ")\n", + "project_2.structures['lbco'].atom_sites.create(\n", + " label='Ba',\n", + " type_symbol='Ba',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.95,\n", + " occupancy=0.5,\n", + ")\n", + "project_2.structures['lbco'].atom_sites.create(\n", + " label='Co',\n", + " type_symbol='Co',\n", + " fract_x=0.5,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='b',\n", + " b_iso=0.80,\n", + ")\n", + "project_2.structures['lbco'].atom_sites.create(\n", + " label='O',\n", + " type_symbol='O',\n", + " fract_x=0,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='c',\n", + " b_iso=1.66,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "133", + "metadata": {}, + "source": [ + "### 🔗 Exercise 4: Assign Structure to Experiment\n", + "\n", + "Now assign the LBCO structure to the experiment created above." + ] + }, + { + "cell_type": "markdown", + "id": "134", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "135", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the `linked_phases` attribute of the experiment to link the\n", + "crystal structure." + ] + }, + { + "cell_type": "markdown", + "id": "136", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "137", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.experiments['sim_lbco'].linked_phases.create(id='lbco', scale=1.0)" + ] + }, + { + "cell_type": "markdown", + "id": "138", + "metadata": {}, + "source": [ + "### 🚀 Exercise 5: Analyze and Fit the Data\n", + "\n", + "#### Exercise 5.1: Set Fit Parameters\n", + "\n", + "Select the initial set of parameters to be refined during the fitting\n", + "process." + ] + }, + { + "cell_type": "markdown", + "id": "139", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "140", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can start with the scale factor and the background points, as in\n", + "the Si fit." + ] + }, + { + "cell_type": "markdown", + "id": "141", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "142", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.experiments['sim_lbco'].linked_phases['lbco'].scale.free = True\n", + "\n", + "for line_segment in project_2.experiments['sim_lbco'].background:\n", + " line_segment.y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "143", + "metadata": {}, + "source": [ + "#### Exercise 5.2: Run Fitting\n", + "\n", + "Visualize the measured and calculated diffraction patterns before\n", + "fitting and then run the fitting process." + ] + }, + { + "cell_type": "markdown", + "id": "144", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "145", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the `plot_meas_vs_calc` method of the project to visualize the\n", + "measured and calculated diffraction patterns before fitting. Then, use\n", + "the `fit` method of the `analysis` object of the project to perform\n", + "the fitting process." + ] + }, + { + "cell_type": "markdown", + "id": "146", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "147", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.plot_meas_vs_calc(expt_name='sim_lbco')\n", + "\n", + "project_2.analysis.fit()\n", + "project_2.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "148", + "metadata": {}, + "source": [ + "#### Exercise 5.3: Find the Misfit in the Fit\n", + "\n", + "Visualize the measured and calculated diffraction patterns after the\n", + "fit. As you can see, the fit shows noticeable discrepancies. If you\n", + "zoom in on different regions of the pattern, you will observe that all\n", + "the calculated peaks are shifted to the left.\n", + "\n", + "What could be the reason for the misfit?" + ] + }, + { + "cell_type": "markdown", + "id": "149", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "150", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Consider the following options:\n", + "1. The conversion parameters from TOF to d-spacing are not correct.\n", + "2. The lattice parameters of the LBCO phase are not correct.\n", + "3. The peak profile parameters are not correct.\n", + "4. The background points are not correct." + ] + }, + { + "cell_type": "markdown", + "id": "151", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "markdown", + "id": "152", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "\n", + "1. ❌ The conversion parameters from TOF to d-spacing were set based\n", + "on the data reduction step. While they are specific to each dataset\n", + "and thus differ from those used for the Si data, the full reduction\n", + "workflow has already been validated with the Si fit. Therefore, they\n", + "are not the cause of the misfit in this case.\n", + "2. ✅ The lattice parameters of the LBCO phase were set based on the\n", + "CIF data, which is a good starting point, but they are not necessarily\n", + "as accurate as needed for the fit. The lattice parameters may need to\n", + "be refined.\n", + "3. ❌ The peak profile parameters do not change the position of the\n", + "peaks, but rather their shape.\n", + "4. ❌ The background points affect the background level, but not the\n", + "peak positions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "153", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.plot_meas_vs_calc(expt_name='sim_lbco')" + ] + }, + { + "cell_type": "markdown", + "id": "154", + "metadata": {}, + "source": [ + "#### Exercise 5.4: Refine the LBCO Lattice Parameter\n", + "\n", + "To improve the fit, refine the lattice parameters of the LBCO phase." + ] + }, + { + "cell_type": "markdown", + "id": "155", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "156", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "To achieve this, we will set the `free` attribute of the `length_a`\n", + "parameter of the LBCO cell to `True`.\n", + "\n", + "LBCO has a cubic crystal structure (space group `P m -3 m`), which\n", + "means that `length_b` and `length_c` are constrained to be equal to\n", + "`length_a`. Therefore, only `length_a` needs to be refined; the other\n", + "two will be updated automatically. All cell angles are fixed at 90°,\n", + "so they do not require refinement." + ] + }, + { + "cell_type": "markdown", + "id": "157", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "158", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.structures['lbco'].cell.length_a.free = True\n", + "\n", + "project_2.analysis.fit()\n", + "project_2.analysis.show_fit_results()\n", + "\n", + "project_2.plot_meas_vs_calc(expt_name='sim_lbco')" + ] + }, + { + "cell_type": "markdown", + "id": "159", + "metadata": {}, + "source": [ + "One of the main goals of this study was to refine the lattice\n", + "parameter of the LBCO phase. As shown in the updated fit results, the\n", + "overall fit has improved significantly, even though the change in cell\n", + "length is less than 1% of the initial value. This demonstrates how\n", + "even a small adjustment to the lattice parameter can have a\n", + "substantial impact on the quality of the fit." + ] + }, + { + "cell_type": "markdown", + "id": "160", + "metadata": {}, + "source": [ + "#### Exercise 5.5: Visualize the Fit Results in d-spacing\n", + "\n", + "Plot measured vs calculated diffraction patterns in d-spacing instead\n", + "of TOF." + ] + }, + { + "cell_type": "markdown", + "id": "161", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "162", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Use the `plot_meas_vs_calc` method of the project and set the\n", + "`d_spacing` parameter to `True`." + ] + }, + { + "cell_type": "markdown", + "id": "163", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "164", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing')" + ] + }, + { + "cell_type": "markdown", + "id": "165", + "metadata": {}, + "source": [ + "#### Exercise 5.6: Refine the Peak Profile Parameters\n", + "\n", + "As you can see, the fit is now relatively good and the peak positions\n", + "are much closer to the measured data.\n", + "\n", + "The peak profile parameters were not refined, and their starting\n", + "values were set based on the previous fit of the Si standard sample.\n", + "Although these starting values are reasonable and provide a good\n", + "starting point for the fit, they are not necessarily optimal for the\n", + "LBCO phase. This can be seen while inspecting the individual peaks in\n", + "the diffraction pattern. For example, the calculated curve does not\n", + "perfectly describe the peak at about 1.38 Å, as can be seen below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "166", + "metadata": {}, + "outputs": [], + "source": [ + "project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1.35, x_max=1.40)" + ] + }, + { + "cell_type": "markdown", + "id": "167", + "metadata": {}, + "source": [ + "The peak profile parameters are determined based on both the\n", + "instrument and the sample characteristics, so they can vary when\n", + "analyzing different samples on the same instrument. Therefore, it is\n", + "better to refine them as well.\n", + "\n", + "Select the peak profile parameters to be refined during the fitting\n", + "process." + ] + }, + { + "cell_type": "markdown", + "id": "168", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "169", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can set the `free` attribute of the peak profile parameters to\n", + "`True` to allow the fitting process to adjust them. You can use the\n", + "same approach as in the previous part of the notebook, but this time\n", + "you will refine the peak profile parameters of the LBCO phase." + ] + }, + { + "cell_type": "markdown", + "id": "170", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "171", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_0.free = True\n", + "project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_1.free = True\n", + "project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_2.free = True\n", + "project_2.experiments['sim_lbco'].peak.broad_mix_beta_0.free = True\n", + "project_2.experiments['sim_lbco'].peak.broad_mix_beta_1.free = True\n", + "project_2.experiments['sim_lbco'].peak.asym_alpha_0.free = True\n", + "project_2.experiments['sim_lbco'].peak.asym_alpha_1.free = True\n", + "\n", + "project_2.analysis.fit()\n", + "project_2.analysis.show_fit_results()\n", + "\n", + "project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1.35, x_max=1.40)" + ] + }, + { + "cell_type": "markdown", + "id": "172", + "metadata": {}, + "source": [ + "#### Exercise 5.7: Find Undefined Features\n", + "\n", + "After refining the lattice parameter and the peak profile parameters,\n", + "the fit is significantly improved, but inspect the diffraction pattern\n", + "again. Are you noticing anything undefined?" + ] + }, + { + "cell_type": "markdown", + "id": "173", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "174", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "While the fit is now significantly better, there are still some\n", + "unexplained peaks in the diffraction pattern. These peaks are not\n", + "accounted for by the LBCO phase. For example, if you zoom in on the\n", + "region around 1.6 Å (or 95,000 μs), you will notice that the rightmost\n", + "peak is not explained by the LBCO phase at all." + ] + }, + { + "cell_type": "markdown", + "id": "175", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "176", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1.53, x_max=1.7)" + ] + }, + { + "cell_type": "markdown", + "id": "177", + "metadata": {}, + "source": [ + "#### Exercise 5.8: Identify the Cause of the Unexplained Peaks\n", + "\n", + "Analyze the residual peaks that remain after refining the LBCO phase\n", + "and the peak-profile parameters. Based on their positions and\n", + "characteristics, decide which potential cause best explains the\n", + "misfit." + ] + }, + { + "cell_type": "markdown", + "id": "178", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "179", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Consider the following options:\n", + "1. The LBCO phase is not correctly modeled.\n", + "2. The LBCO phase is not the only phase present in the sample.\n", + "3. The data reduction process introduced artifacts.\n", + "4. The studied sample is not LBCO, but rather a different phase." + ] + }, + { + "cell_type": "markdown", + "id": "180", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "markdown", + "id": "181", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "1. ❌ In principle, this could be the case, as sometimes the presence\n", + "of extra peaks in the diffraction pattern can indicate lower symmetry\n", + "than the one used in the model, or that the model is not complete.\n", + "However, in this case, the LBCO phase is correctly modeled based on\n", + "the CIF data.\n", + "2. ✅ The unexplained peaks are due to the presence of an impurity\n", + "phase in the sample, which is not included in the current model.\n", + "3. ❌ The data reduction process is not likely to introduce such\n", + "specific peaks, as it is tested and verified in the previous part of\n", + "the notebook.\n", + "4. ❌ This could also be the case in real experiments, but in this\n", + "case, we know that the sample is LBCO, as it was simulated based on\n", + "the CIF data." + ] + }, + { + "cell_type": "markdown", + "id": "182", + "metadata": {}, + "source": [ + "#### Exercise 5.9: Identify the impurity phase\n", + "\n", + "Use the positions of the unexplained peaks to identify the most likely\n", + "secondary phase present in the sample." + ] + }, + { + "cell_type": "markdown", + "id": "183", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "184", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "Check the positions of the unexplained peaks in the diffraction\n", + "pattern. Compare them with the known diffraction patterns in the\n", + "previous part of the notebook." + ] + }, + { + "cell_type": "markdown", + "id": "185", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "markdown", + "id": "186", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "The unexplained peaks are likely due to the presence of a small amount\n", + "of Si in the LBCO sample. In real experiments, it might happen, e.g.,\n", + "because the sample holder was not cleaned properly after the Si\n", + "experiment.\n", + "\n", + "You can visalize both the patterns of the Si and LBCO phases to\n", + "confirm this hypothesis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "187", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "project_1.plot_meas_vs_calc(expt_name='sim_si', x='d_spacing', x_min=1, x_max=1.7)\n", + "project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1, x_max=1.7)" + ] + }, + { + "cell_type": "markdown", + "id": "188", + "metadata": {}, + "source": [ + "#### Exercise 5.10: Create a Second Structure – Si as Impurity\n", + "\n", + "Create a second structure for the Si phase, which is the impurity\n", + "phase identified in the previous step. Link this structure to the\n", + "LBCO experiment." + ] + }, + { + "cell_type": "markdown", + "id": "189", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "190", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can use the same approach as in the previous part of the notebook,\n", + "but this time you need to create a structure for Si and link it to\n", + "the LBCO experiment." + ] + }, + { + "cell_type": "markdown", + "id": "191", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "192", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "# Set Space Group\n", + "project_2.structures.create(name='si')\n", + "project_2.structures['si'].space_group.name_h_m = 'F d -3 m'\n", + "project_2.structures['si'].space_group.it_coordinate_system_code = '2'\n", + "\n", + "# Set Lattice Parameters\n", + "project_2.structures['si'].cell.length_a = 5.43\n", + "\n", + "# Set Atom Sites\n", + "project_2.structures['si'].atom_sites.create(\n", + " label='Si',\n", + " type_symbol='Si',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.89,\n", + ")\n", + "\n", + "# Assign Structure to Experiment\n", + "project_2.experiments['sim_lbco'].linked_phases.create(id='si', scale=1.0)" + ] + }, + { + "cell_type": "markdown", + "id": "193", + "metadata": {}, + "source": [ + "#### Exercise 5.11: Refine the Scale of the Si Phase\n", + "\n", + "Visualize the measured diffraction pattern and the calculated\n", + "diffraction pattern. Check if the Si phase is contributing to the\n", + "calculated diffraction pattern. Refine the scale factor of the Si\n", + "phase to improve the fit." + ] + }, + { + "cell_type": "markdown", + "id": "194", + "metadata": {}, + "source": [ + "**Hint:**" + ] + }, + { + "cell_type": "markdown", + "id": "195", + "metadata": { + "tags": [ + "dmsc-school-hint" + ] + }, + "source": [ + "You can use the `plot_meas_vs_calc` method of the project to visualize\n", + "the patterns. Then, set the `free` attribute of the `scale` parameter\n", + "of the Si phase to `True` to allow the fitting process to adjust the\n", + "scale factor." + ] + }, + { + "cell_type": "markdown", + "id": "196", + "metadata": {}, + "source": [ + "**Solution:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "197", + "metadata": { + "tags": [ + "solution", + "hide-input" + ] + }, + "outputs": [], + "source": [ + "# Before optimizing the parameters, we can visualize the measured\n", + "# diffraction pattern and the calculated diffraction pattern based on\n", + "# the two phases: LBCO and Si.\n", + "project_2.plot_meas_vs_calc(expt_name='sim_lbco')\n", + "\n", + "# As you can see, the calculated pattern is now the sum of both phases,\n", + "# and Si peaks are visible in the calculated pattern. However, their\n", + "# intensities are much too high. Therefore, we need to refine the scale\n", + "# factor of the Si phase.\n", + "project_2.experiments['sim_lbco'].linked_phases['si'].scale.free = True\n", + "\n", + "# Now we can perform the fit with both phases included.\n", + "project_2.analysis.fit()\n", + "project_2.analysis.show_fit_results()\n", + "\n", + "# Let's plot the measured diffraction pattern and the calculated\n", + "# diffraction pattern both for the full range and for a zoomed-in region\n", + "# around the previously unexplained peak near 95,000 μs. The calculated\n", + "# pattern will be the sum of the two phases.\n", + "project_2.plot_meas_vs_calc(expt_name='sim_lbco')\n", + "project_2.plot_meas_vs_calc(expt_name='sim_lbco', x_min=88000, x_max=101000)" + ] + }, + { + "cell_type": "markdown", + "id": "198", + "metadata": {}, + "source": [ + "All previously unexplained peaks are now accounted for in the pattern,\n", + "and the fit is improved. Some discrepancies in the peak intensities\n", + "remain, but further improvements would require more advanced data\n", + "reduction and analysis, which are beyond the scope of this school.\n", + "\n", + "To review the analysis results, you can generate and print a summary\n", + "report using the `show_report()` method, as demonstrated in the cell\n", + "below. The report includes parameters related to the structure and\n", + "the experiment, such as the refined unit cell parameter `a` of LBCO.\n", + "\n", + "Information about the crystal or magnetic structure, along with\n", + "experimental details, fitting quality, and other relevant data, is\n", + "often submitted to crystallographic journals as part of a scientific\n", + "publication. It can also be deposited in crystallographic databases\n", + "when relevant." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "199", + "metadata": {}, + "outputs": [], + "source": [ + "project_2.summary.show_report()" + ] + }, + { + "cell_type": "markdown", + "id": "200", + "metadata": {}, + "source": [ + "Finally, we save the project to disk to preserve the current state of\n", + "the analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "201", + "metadata": {}, + "outputs": [], + "source": [ + "project_2.save_as(dir_path='powder_diffraction_LBCO_Si')" + ] + }, + { + "cell_type": "markdown", + "id": "202", + "metadata": {}, + "source": [ + "#### Final Remarks\n", + "\n", + "In this part of the notebook, you learned how to use EasyDiffraction\n", + "to refine lattice parameters of a more complex crystal structure,\n", + "La₀.₅Ba₀.₅CoO₃ (LBCO).\n", + "\n", + "In real experiments, you might also refine\n", + "additional parameters, such as atomic positions, occupancies, and\n", + "atomic displacement factors, to achieve an even better fit. For our\n", + "purposes, we'll stop here, as the goal was to give you a starting\n", + "point for analyzing more complex crystal structures with\n", + "EasyDiffraction." + ] + }, + { + "cell_type": "markdown", + "id": "203", + "metadata": {}, + "source": [ + "## 🎁 Bonus\n", + "\n", + "Congratulations — you've now completed the diffraction data analysis\n", + "part of the DMSC Summer School!\n", + "\n", + "If you'd like to keep exploring, the EasyDiffraction library offers\n", + "many additional tutorials and examples on the official documentation\n", + "site: 👉 https://docs.easydiffraction.org/lib/tutorials/\n", + "\n", + "Besides the Python package, EasyDiffraction also comes with a\n", + "graphical user interface (GUI) that lets you perform similar analyses\n", + "without writing code. To be fair, it's not *quite* feature-complete\n", + "compared to the Python library yet — but we're working on it! 🚧\n", + "\n", + "If you prefer a point-and-click interface over coding, the GUI\n", + "provides a user-friendly way to analyze diffraction data. You can\n", + "download it as a standalone application here: 👉\n", + "https://easydiffraction.org\n", + "\n", + "We'd love to hear your feedback on EasyDiffraction — both the library\n", + "and the GUI! 💬" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "tags,title,-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-14.ipynb b/docs/docs/tutorials/ed-14.ipynb new file mode 100644 index 00000000..25927d6a --- /dev/null +++ b/docs/docs/tutorials/ed-14.ipynb @@ -0,0 +1,319 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: Tb2TiO7, HEiDi\n", + "\n", + "Crystal structure refinement of Tb2TiO7 using single crystal neutron\n", + "diffraction data from HEiDi at FRM II." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Step 1: Define Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# Create minimal project without name and description\n", + "project = ed.Project()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Step 2: Define Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# Download CIF file from repository\n", + "structure_path = ed.download_data(id=20, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add_from_cif_path(structure_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.show_names()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "structure = project.structures['tbti']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites['Tb'].b_iso = 0.0\n", + "structure.atom_sites['Ti'].b_iso = 0.0\n", + "structure.atom_sites['O1'].b_iso = 0.0\n", + "structure.atom_sites['O2'].b_iso = 0.0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "structure.show_as_cif()" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "## Step 3: Define Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = ed.download_data(id=19, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='heidi',\n", + " data_path=data_path,\n", + " sample_form='single crystal',\n", + " beam_mode='constant wavelength',\n", + " radiation_probe='neutron',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "experiment = project.experiments['heidi'] # TODO: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.linked_crystal.id = 'tbti'\n", + "experiment.linked_crystal.scale = 1.0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.instrument.setup_wavelength = 0.793" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.extinction.mosaicity = 29820\n", + "experiment.extinction.radius = 30" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "## Step 4: Perform Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='heidi')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.linked_crystal.scale.free = True\n", + "experiment.extinction.radius.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.show_as_cif()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "# Start refinement. All parameters, which have standard uncertainties\n", + "# in the input CIF files, are refined by default.\n", + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "# Show fit results summary\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.show_as_cif()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.show_names()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='heidi')" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "## Step 5: Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-15.ipynb b/docs/docs/tutorials/ed-15.ipynb new file mode 100644 index 00000000..a426f2ee --- /dev/null +++ b/docs/docs/tutorials/ed-15.ipynb @@ -0,0 +1,296 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: Taurine, SENJU\n", + "\n", + "Crystal structure refinement of Taurine using time-of-flight single\n", + "crystal neutron diffraction data from SENJU at J-PARC." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Step 1: Define Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# Create minimal project without name and description\n", + "project = ed.Project()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Step 2: Define Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# Download CIF file from repository\n", + "structure_path = ed.download_data(id=21, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add_from_cif_path(structure_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.show_names()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "structure = project.structures['taurine']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "# structure.show_as_cif()" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Step 3: Define Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = ed.download_data(id=22, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='senju',\n", + " data_path=data_path,\n", + " sample_form='single crystal',\n", + " beam_mode='time-of-flight',\n", + " radiation_probe='neutron',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "experiment = project.experiments['senju']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.linked_crystal.id = 'taurine'\n", + "experiment.linked_crystal.scale = 1.0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.extinction.mosaicity = 1000.0\n", + "experiment.extinction.radius = 100.0" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "## Step 4: Perform Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='senju')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.linked_crystal.scale.free = True\n", + "experiment.extinction.radius.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "# experiment.show_as_cif()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "# Start refinement. All parameters, which have standard uncertainties\n", + "# in the input CIF files, are refined by default.\n", + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "# Show fit results summary\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "# experiment.show_as_cif()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.show_names()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='senju')" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "## Step 5: Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-16.ipynb b/docs/docs/tutorials/ed-16.ipynb new file mode 100644 index 00000000..688297e6 --- /dev/null +++ b/docs/docs/tutorials/ed-16.ipynb @@ -0,0 +1,623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Joint Refinement: Si, Bragg + PDF\n", + "\n", + "This example demonstrates a joint refinement of the Si crystal\n", + "structure combining Bragg diffraction and pair distribution function\n", + "(PDF) analysis. The Bragg experiment uses time-of-flight neutron\n", + "powder diffraction data from SEPD at Argonne, while the PDF\n", + "experiment uses data from NOMAD at SNS. A single shared Si structure\n", + "is refined simultaneously against both datasets." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from easydiffraction import ExperimentFactory\n", + "from easydiffraction import Project\n", + "from easydiffraction import StructureFactory\n", + "from easydiffraction import download_data" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Define Structure\n", + "\n", + "A single Si structure is shared between the Bragg and PDF\n", + "experiments. Structural parameters refined against both datasets\n", + "simultaneously.\n", + "\n", + "#### Create Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "structure = StructureFactory.from_scratch(name='si')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "structure.space_group.name_h_m = 'F d -3 m'\n", + "structure.space_group.it_coordinate_system_code = '1'" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a = 5.42" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites.create(\n", + " label='Si',\n", + " type_symbol='Si',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.2,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Define Experiments\n", + "\n", + "Two experiments are defined: one for Bragg diffraction and one for\n", + "PDF analysis. Both are linked to the same Si structure.\n", + "\n", + "### Experiment 1: Bragg (SEPD, TOF)\n", + "\n", + "#### Download Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "bragg_data_path = download_data(id=7, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "bragg_expt = ExperimentFactory.from_data_path(\n", + " name='sepd', data_path=bragg_data_path, beam_mode='time-of-flight'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "bragg_expt.instrument.setup_twotheta_bank = 144.845\n", + "bragg_expt.instrument.calib_d_to_tof_offset = -9.2\n", + "bragg_expt.instrument.calib_d_to_tof_linear = 7476.91\n", + "bragg_expt.instrument.calib_d_to_tof_quad = -1.54" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "bragg_expt.peak_profile_type = 'pseudo-voigt * ikeda-carpenter'\n", + "bragg_expt.peak.broad_gauss_sigma_0 = 5.0\n", + "bragg_expt.peak.broad_gauss_sigma_1 = 45.0\n", + "bragg_expt.peak.broad_gauss_sigma_2 = 1.0\n", + "bragg_expt.peak.broad_mix_beta_0 = 0.04221\n", + "bragg_expt.peak.broad_mix_beta_1 = 0.00946\n", + "bragg_expt.peak.asym_alpha_0 = 0.0\n", + "bragg_expt.peak.asym_alpha_1 = 0.5971" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "bragg_expt.background_type = 'line-segment'\n", + "for x in range(0, 35000, 5000):\n", + " bragg_expt.background.create(id=str(x), x=x, y=200)" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "bragg_expt.linked_phases.create(id='si', scale=13.0)" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "### Experiment 2: PDF (NOMAD, TOF)\n", + "\n", + "#### Download Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "pdf_data_path = download_data(id=5, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "pdf_expt = ExperimentFactory.from_data_path(\n", + " name='nomad',\n", + " data_path=pdf_data_path,\n", + " beam_mode='time-of-flight',\n", + " scattering_type='total',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "#### Set Peak Profile (PDF Parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "pdf_expt.peak.damp_q = 0.02\n", + "pdf_expt.peak.broad_q = 0.02\n", + "pdf_expt.peak.cutoff_q = 35.0\n", + "pdf_expt.peak.sharp_delta_1 = 0.001\n", + "pdf_expt.peak.sharp_delta_2 = 4.0\n", + "pdf_expt.peak.damp_particle_diameter = 0" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "pdf_expt.linked_phases.create(id='si', scale=1.0)" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "## Define Project\n", + "\n", + "The project object manages the shared structure, both experiments,\n", + "and the analysis.\n", + "\n", + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add(structure)" + ] + }, + { + "cell_type": "markdown", + "id": "35", + "metadata": {}, + "source": [ + "#### Add Experiments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add(bragg_expt)\n", + "project.experiments.add(pdf_expt)" + ] + }, + { + "cell_type": "markdown", + "id": "37", + "metadata": {}, + "source": [ + "## Perform Analysis\n", + "\n", + "This section shows the joint analysis process. The calculator is\n", + "auto-resolved per experiment: CrysPy for Bragg, PDFfit for PDF.\n", + "\n", + "#### Set Fit Mode and Weights" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit_mode.mode = 'joint'\n", + "project.analysis.joint_fit_experiments.create(id='sepd', weight=0.7)\n", + "project.analysis.joint_fit_experiments.create(id='nomad', weight=0.3)" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": {}, + "source": [ + "#### Set Minimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated (Before Fit)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', show_residual=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='nomad', show_residual=False)" + ] + }, + { + "cell_type": "markdown", + "id": "44", + "metadata": {}, + "source": [ + "#### Set Fitting Parameters\n", + "\n", + "Shared structural parameters are refined against both datasets\n", + "simultaneously." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a.free = True\n", + "structure.atom_sites['Si'].b_iso.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "46", + "metadata": {}, + "source": [ + "Bragg experiment parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47", + "metadata": {}, + "outputs": [], + "source": [ + "bragg_expt.linked_phases['si'].scale.free = True\n", + "bragg_expt.instrument.calib_d_to_tof_offset.free = True\n", + "bragg_expt.peak.broad_gauss_sigma_0.free = True\n", + "bragg_expt.peak.broad_gauss_sigma_1.free = True\n", + "bragg_expt.peak.broad_gauss_sigma_2.free = True\n", + "for point in bragg_expt.background:\n", + " point.y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "48", + "metadata": {}, + "source": [ + "PDF experiment parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49", + "metadata": {}, + "outputs": [], + "source": [ + "pdf_expt.linked_phases['si'].scale.free = True\n", + "pdf_expt.peak.damp_q.free = True\n", + "pdf_expt.peak.broad_q.free = True\n", + "pdf_expt.peak.sharp_delta_1.free = True\n", + "pdf_expt.peak.sharp_delta_2.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "50", + "metadata": {}, + "source": [ + "#### Show Free Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "52", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "54", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated (After Fit)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', show_residual=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='nomad', show_residual=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-2.ipynb b/docs/docs/tutorials/ed-2.ipynb new file mode 100644 index 00000000..0a4af0f3 --- /dev/null +++ b/docs/docs/tutorials/ed-2.ipynb @@ -0,0 +1,344 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: LBCO, HRPT\n", + "\n", + "This minimalistic example is designed to show how Rietveld refinement\n", + "can be performed when both the crystal structure and experiment are\n", + "defined directly in code. Only the experimentally measured data is\n", + "loaded from an external file.\n", + "\n", + "For this example, constant-wavelength neutron powder diffraction data\n", + "for La0.5Ba0.5CoO3 from HRPT at PSI is used.\n", + "\n", + "It does not contain any advanced features or options, and includes no\n", + "comments or explanations—these can be found in the other tutorials.\n", + "Default values are used for all parameters if not specified. Only\n", + "essential and self-explanatory code is provided.\n", + "\n", + "The example is intended for users who are already familiar with the\n", + "EasyDiffraction library and want to quickly get started with a simple\n", + "refinement. It is also useful for those who want to see what a\n", + "refinement might look like in code. For a more detailed explanation of\n", + "the code, please refer to the other tutorials." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Step 1: Define Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "project = ed.Project()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Step 2: Define Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.create(name='lbco')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "structure = project.structures['lbco']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure.space_group.name_h_m = 'P m -3 m'\n", + "structure.space_group.it_coordinate_system_code = '1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a = 3.88" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites.create(\n", + " label='La',\n", + " type_symbol='La',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.5,\n", + " occupancy=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Ba',\n", + " type_symbol='Ba',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.5,\n", + " occupancy=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Co',\n", + " type_symbol='Co',\n", + " fract_x=0.5,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='b',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O',\n", + " type_symbol='O',\n", + " fract_x=0,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='c',\n", + " b_iso=0.5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Step 3: Define Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = ed.download_data(id=3, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='hrpt',\n", + " data_path=data_path,\n", + " sample_form='powder',\n", + " beam_mode='constant wavelength',\n", + " radiation_probe='neutron',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "experiment = project.experiments['hrpt']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.instrument.setup_wavelength = 1.494\n", + "experiment.instrument.calib_twotheta_offset = 0.6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.peak.broad_gauss_u = 0.1\n", + "experiment.peak.broad_gauss_v = -0.1\n", + "experiment.peak.broad_gauss_w = 0.1\n", + "experiment.peak.broad_lorentz_y = 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.background.create(id='1', x=10, y=170)\n", + "experiment.background.create(id='2', x=30, y=170)\n", + "experiment.background.create(id='3', x=50, y=170)\n", + "experiment.background.create(id='4', x=110, y=170)\n", + "experiment.background.create(id='5', x=165, y=170)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.excluded_regions.create(id='1', start=0, end=5)\n", + "experiment.excluded_regions.create(id='2', start=165, end=180)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.linked_phases.create(id='lbco', scale=10.0)" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "## Step 4: Perform Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a.free = True\n", + "\n", + "structure.atom_sites['La'].b_iso.free = True\n", + "structure.atom_sites['Ba'].b_iso.free = True\n", + "structure.atom_sites['Co'].b_iso.free = True\n", + "structure.atom_sites['O'].b_iso.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "experiment.instrument.calib_twotheta_offset.free = True\n", + "\n", + "experiment.peak.broad_gauss_u.free = True\n", + "experiment.peak.broad_gauss_v.free = True\n", + "experiment.peak.broad_gauss_w.free = True\n", + "experiment.peak.broad_lorentz_y.free = True\n", + "\n", + "experiment.background['1'].y.free = True\n", + "experiment.background['2'].y.free = True\n", + "experiment.background['3'].y.free = True\n", + "experiment.background['4'].y.free = True\n", + "experiment.background['5'].y.free = True\n", + "\n", + "experiment.linked_phases['lbco'].scale.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-3.ipynb b/docs/docs/tutorials/ed-3.ipynb new file mode 100644 index 00000000..0fadb404 --- /dev/null +++ b/docs/docs/tutorials/ed-3.ipynb @@ -0,0 +1,1805 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: LBCO, HRPT\n", + "\n", + "This example demonstrates how to use the EasyDiffraction API in a\n", + "simplified, user-friendly manner that closely follows the GUI workflow\n", + "for a Rietveld refinement of La0.5Ba0.5CoO3 crystal structure using\n", + "constant wavelength neutron powder diffraction data from HRPT at PSI.\n", + "\n", + "It is intended for users with minimal programming experience who want\n", + "to learn how to perform standard crystal structure fitting using\n", + "diffraction data. This script covers creating a project, adding\n", + "crystal structures and experiments, performing analysis, and refining\n", + "parameters.\n", + "\n", + "Only a single import of `easydiffraction` is required, and all\n", + "operations are performed through high-level components of the\n", + "`project` object, such as `project.structures`,\n", + "`project.experiments`, and `project.analysis`. The `project` object is\n", + "the main container for all information." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Step 1: Create a Project\n", + "\n", + "This section explains how to create a project and define its metadata." + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "project = ed.Project(name='lbco_hrpt')" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "#### Set Project Metadata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "project.info.title = 'La0.5Ba0.5CoO3 at HRPT@PSI'\n", + "project.info.description = \"\"\"This project demonstrates a standard\n", + "refinement of La0.5Ba0.5CoO3, which crystallizes in a perovskite-type\n", + "structure, using neutron powder diffraction data collected in constant\n", + "wavelength mode at the HRPT diffractometer (PSI).\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "#### Show Project Metadata as CIF" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "project.info.show_as_cif()" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "#### Save Project\n", + "\n", + "When saving the project for the first time, you need to specify the\n", + "directory path. In the example below, the project is saved to a\n", + "temporary location defined by the system." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='lbco_hrpt', temporary=True)" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "#### Set Up Data Plotter" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "Show supported plotting engines." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "project.plotter.show_supported_engines()" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "Show current plotting configuration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "project.plotter.show_config()" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "Set plotting engine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "# Keep the auto-selected engine. Alternatively, you can uncomment the\n", + "# line below to explicitly set the engine to the required one.\n", + "# project.plotter.engine = 'plotly'" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "## Step 2: Define Structure\n", + "\n", + "This section shows how to add structures and modify their\n", + "parameters." + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.create(name='lbco')" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "#### Show Defined Structures\n", + "\n", + "Show the names of the crystal structures added. These names are used\n", + "to access the structure using the syntax:\n", + "`project.structures[name]`. All structure parameters can be accessed\n", + "via the `project` object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.show_names()" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "#### Set Space Group\n", + "\n", + "Modify the default space group parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].space_group.name_h_m = 'P m -3 m'\n", + "project.structures['lbco'].space_group.it_coordinate_system_code = '1'" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "#### Set Unit Cell\n", + "\n", + "Modify the default unit cell parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].cell.length_a = 3.88" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "#### Set Atom Sites\n", + "\n", + "Add atom sites to the structure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].atom_sites.create(\n", + " label='La',\n", + " type_symbol='La',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.5,\n", + " occupancy=0.5,\n", + ")\n", + "project.structures['lbco'].atom_sites.create(\n", + " label='Ba',\n", + " type_symbol='Ba',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.5,\n", + " occupancy=0.5,\n", + ")\n", + "project.structures['lbco'].atom_sites.create(\n", + " label='Co',\n", + " type_symbol='Co',\n", + " fract_x=0.5,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='b',\n", + " b_iso=0.5,\n", + ")\n", + "project.structures['lbco'].atom_sites.create(\n", + " label='O',\n", + " type_symbol='O',\n", + " fract_x=0,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='c',\n", + " b_iso=0.5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "#### Show Structure as CIF" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].show_as_cif()" + ] + }, + { + "cell_type": "markdown", + "id": "32", + "metadata": {}, + "source": [ + "#### Show Structure Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].show()" + ] + }, + { + "cell_type": "markdown", + "id": "34", + "metadata": {}, + "source": [ + "#### Save Project State\n", + "\n", + "Save the project state after adding the structure. This ensures\n", + "that all changes are stored and can be accessed later. The project\n", + "state is saved in the directory specified during project creation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35", + "metadata": {}, + "outputs": [], + "source": [ + "project.save()" + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": {}, + "source": [ + "## Step 3: Define Experiment\n", + "\n", + "This section shows how to add experiments, configure their parameters,\n", + "and link the structures defined in the previous step." + ] + }, + { + "cell_type": "markdown", + "id": "37", + "metadata": {}, + "source": [ + "#### Download Measured Data\n", + "\n", + "Download the data file from the EasyDiffraction repository on GitHub." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = ed.download_data(id=3, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": {}, + "source": [ + "#### Add Diffraction Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='hrpt',\n", + " data_path=data_path,\n", + " sample_form='powder',\n", + " beam_mode='constant wavelength',\n", + " radiation_probe='neutron',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "#### Show Defined Experiments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.show_names()" + ] + }, + { + "cell_type": "markdown", + "id": "43", + "metadata": {}, + "source": [ + "#### Show Measured Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas(expt_name='hrpt')" + ] + }, + { + "cell_type": "markdown", + "id": "45", + "metadata": {}, + "source": [ + "#### Set Instrument\n", + "\n", + "Modify the default instrument parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].instrument.setup_wavelength = 1.494\n", + "project.experiments['hrpt'].instrument.calib_twotheta_offset = 0.6" + ] + }, + { + "cell_type": "markdown", + "id": "47", + "metadata": {}, + "source": [ + "#### Set Peak Profile\n", + "\n", + "Show supported peak profile types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].show_supported_peak_profile_types()" + ] + }, + { + "cell_type": "markdown", + "id": "49", + "metadata": {}, + "source": [ + "Show the current peak profile type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].show_current_peak_profile_type()" + ] + }, + { + "cell_type": "markdown", + "id": "51", + "metadata": {}, + "source": [ + "Select the desired peak profile type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].peak_profile_type = 'pseudo-voigt'" + ] + }, + { + "cell_type": "markdown", + "id": "53", + "metadata": {}, + "source": [ + "Modify default peak profile parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].peak.broad_gauss_u = 0.1\n", + "project.experiments['hrpt'].peak.broad_gauss_v = -0.1\n", + "project.experiments['hrpt'].peak.broad_gauss_w = 0.1\n", + "project.experiments['hrpt'].peak.broad_lorentz_x = 0\n", + "project.experiments['hrpt'].peak.broad_lorentz_y = 0.1" + ] + }, + { + "cell_type": "markdown", + "id": "55", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "markdown", + "id": "56", + "metadata": {}, + "source": [ + "Show supported background types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].show_supported_background_types()" + ] + }, + { + "cell_type": "markdown", + "id": "58", + "metadata": {}, + "source": [ + "Show current background type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].show_current_background_type()" + ] + }, + { + "cell_type": "markdown", + "id": "60", + "metadata": {}, + "source": [ + "Select the desired background type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].background_type = 'line-segment'" + ] + }, + { + "cell_type": "markdown", + "id": "62", + "metadata": {}, + "source": [ + "Add background points." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].background.create(id='10', x=10, y=170)\n", + "project.experiments['hrpt'].background.create(id='30', x=30, y=170)\n", + "project.experiments['hrpt'].background.create(id='50', x=50, y=170)\n", + "project.experiments['hrpt'].background.create(id='110', x=110, y=170)\n", + "project.experiments['hrpt'].background.create(id='165', x=165, y=170)" + ] + }, + { + "cell_type": "markdown", + "id": "64", + "metadata": {}, + "source": [ + "Show current background points." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].background.show()" + ] + }, + { + "cell_type": "markdown", + "id": "66", + "metadata": {}, + "source": [ + "#### Set Linked Phases\n", + "\n", + "Link the structure defined in the previous step to the experiment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].linked_phases.create(id='lbco', scale=10.0)" + ] + }, + { + "cell_type": "markdown", + "id": "68", + "metadata": {}, + "source": [ + "#### Show Experiment as CIF" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].show_as_cif()" + ] + }, + { + "cell_type": "markdown", + "id": "70", + "metadata": {}, + "source": [ + "#### Save Project State" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71", + "metadata": {}, + "outputs": [], + "source": [ + "project.save()" + ] + }, + { + "cell_type": "markdown", + "id": "72", + "metadata": {}, + "source": [ + "## Step 4: Perform Analysis\n", + "\n", + "This section explains the analysis process, including how to set up\n", + "calculation and fitting engines.\n", + "\n", + "#### Set Calculator\n", + "\n", + "Show supported calculation engines for this experiment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].show_supported_calculator_types()" + ] + }, + { + "cell_type": "markdown", + "id": "74", + "metadata": {}, + "source": [ + "Show current calculation engine for this experiment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].show_current_calculator_type()" + ] + }, + { + "cell_type": "markdown", + "id": "76", + "metadata": {}, + "source": [ + "Select the desired calculation engine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].calculator_type = 'cryspy'" + ] + }, + { + "cell_type": "markdown", + "id": "78", + "metadata": {}, + "source": [ + "#### Show Calculated Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_calc(expt_name='hrpt')" + ] + }, + { + "cell_type": "markdown", + "id": "80", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=38, x_max=41, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "83", + "metadata": {}, + "source": [ + "#### Show Parameters\n", + "\n", + "Show all parameters of the project." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84", + "metadata": {}, + "outputs": [], + "source": [ + "# project.analysis.show_all_params()" + ] + }, + { + "cell_type": "markdown", + "id": "85", + "metadata": {}, + "source": [ + "Show all fittable parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_fittable_params()" + ] + }, + { + "cell_type": "markdown", + "id": "87", + "metadata": {}, + "source": [ + "Show only free parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "89", + "metadata": {}, + "source": [ + "Show how to access parameters in the code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90", + "metadata": {}, + "outputs": [], + "source": [ + "# project.analysis.how_to_access_parameters()" + ] + }, + { + "cell_type": "markdown", + "id": "91", + "metadata": {}, + "source": [ + "#### Set Fit Mode\n", + "\n", + "Show supported fit modes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_supported_fit_mode_types()" + ] + }, + { + "cell_type": "markdown", + "id": "93", + "metadata": {}, + "source": [ + "Show current fit mode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_current_fit_mode_type()" + ] + }, + { + "cell_type": "markdown", + "id": "95", + "metadata": {}, + "source": [ + "Select desired fit mode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit_mode.mode = 'single'" + ] + }, + { + "cell_type": "markdown", + "id": "97", + "metadata": {}, + "source": [ + "#### Set Minimizer\n", + "\n", + "Show supported fitting engines." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_available_minimizers()" + ] + }, + { + "cell_type": "markdown", + "id": "99", + "metadata": {}, + "source": [ + "Show current fitting engine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "100", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_current_minimizer()" + ] + }, + { + "cell_type": "markdown", + "id": "101", + "metadata": {}, + "source": [ + "Select desired fitting engine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "102", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "103", + "metadata": {}, + "source": [ + "### Perform Fit 1/5\n", + "\n", + "Set structure parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "104", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].cell.length_a.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "105", + "metadata": {}, + "source": [ + "Set experiment parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "106", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].linked_phases['lbco'].scale.free = True\n", + "project.experiments['hrpt'].instrument.calib_twotheta_offset.free = True\n", + "project.experiments['hrpt'].background['10'].y.free = True\n", + "project.experiments['hrpt'].background['30'].y.free = True\n", + "project.experiments['hrpt'].background['50'].y.free = True\n", + "project.experiments['hrpt'].background['110'].y.free = True\n", + "project.experiments['hrpt'].background['165'].y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "107", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "108", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "109", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "110", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "111", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "112", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "113", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=38, x_max=41, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "114", + "metadata": {}, + "source": [ + "#### Save Project State" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "115", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='lbco_hrpt', temporary=True)" + ] + }, + { + "cell_type": "markdown", + "id": "116", + "metadata": {}, + "source": [ + "### Perform Fit 2/5\n", + "\n", + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "117", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['hrpt'].peak.broad_gauss_u.free = True\n", + "project.experiments['hrpt'].peak.broad_gauss_v.free = True\n", + "project.experiments['hrpt'].peak.broad_gauss_w.free = True\n", + "project.experiments['hrpt'].peak.broad_lorentz_y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "118", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "119", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "120", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "121", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "122", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "123", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "124", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=38, x_max=41, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "125", + "metadata": {}, + "source": [ + "#### Save Project State" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "126", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='lbco_hrpt', temporary=True)" + ] + }, + { + "cell_type": "markdown", + "id": "127", + "metadata": {}, + "source": [ + "### Perform Fit 3/5\n", + "\n", + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "128", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].atom_sites['La'].b_iso.free = True\n", + "project.structures['lbco'].atom_sites['Ba'].b_iso.free = True\n", + "project.structures['lbco'].atom_sites['Co'].b_iso.free = True\n", + "project.structures['lbco'].atom_sites['O'].b_iso.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "129", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "130", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "131", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "132", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "133", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "134", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "135", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=38, x_max=41, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "136", + "metadata": {}, + "source": [ + "#### Save Project State" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "137", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='lbco_hrpt', temporary=True)" + ] + }, + { + "cell_type": "markdown", + "id": "138", + "metadata": {}, + "source": [ + "### Perform Fit 4/5\n", + "\n", + "#### Set Constraints\n", + "\n", + "Set aliases for parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "139", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.aliases.create(\n", + " label='biso_La',\n", + " param_uid=project.structures['lbco'].atom_sites['La'].b_iso.uid,\n", + ")\n", + "project.analysis.aliases.create(\n", + " label='biso_Ba',\n", + " param_uid=project.structures['lbco'].atom_sites['Ba'].b_iso.uid,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "140", + "metadata": {}, + "source": [ + "Set constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "141", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.constraints.create(lhs_alias='biso_Ba', rhs_expr='biso_La')" + ] + }, + { + "cell_type": "markdown", + "id": "142", + "metadata": {}, + "source": [ + "Show defined constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "143", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_constraints()" + ] + }, + { + "cell_type": "markdown", + "id": "144", + "metadata": {}, + "source": [ + "Show free parameters before applying constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "145", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "146", + "metadata": {}, + "source": [ + "Apply constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "147", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.apply_constraints()" + ] + }, + { + "cell_type": "markdown", + "id": "148", + "metadata": {}, + "source": [ + "Show free parameters after applying constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "149", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "150", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "151", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "152", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "153", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "154", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=38, x_max=41, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "155", + "metadata": {}, + "source": [ + "#### Save Project State" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='lbco_hrpt', temporary=True)" + ] + }, + { + "cell_type": "markdown", + "id": "157", + "metadata": {}, + "source": [ + "### Perform Fit 5/5\n", + "\n", + "#### Set Constraints\n", + "\n", + "Set more aliases for parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "158", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.aliases.create(\n", + " label='occ_La',\n", + " param_uid=project.structures['lbco'].atom_sites['La'].occupancy.uid,\n", + ")\n", + "project.analysis.aliases.create(\n", + " label='occ_Ba',\n", + " param_uid=project.structures['lbco'].atom_sites['Ba'].occupancy.uid,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "159", + "metadata": {}, + "source": [ + "Set more constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "160", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.constraints.create(\n", + " lhs_alias='occ_Ba',\n", + " rhs_expr='1 - occ_La',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "161", + "metadata": {}, + "source": [ + "Show defined constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "162", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_constraints()" + ] + }, + { + "cell_type": "markdown", + "id": "163", + "metadata": {}, + "source": [ + "Apply constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "164", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.apply_constraints()" + ] + }, + { + "cell_type": "markdown", + "id": "165", + "metadata": {}, + "source": [ + "Set structure parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "166", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures['lbco'].atom_sites['La'].occupancy.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "167", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "168", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "169", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "170", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "171", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "172", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "173", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=38, x_max=41, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "174", + "metadata": {}, + "source": [ + "#### Save Project State" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "175", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='lbco_hrpt', temporary=True)" + ] + }, + { + "cell_type": "markdown", + "id": "176", + "metadata": {}, + "source": [ + "## Step 5: Summary\n", + "\n", + "This final section shows how to review the results of the analysis." + ] + }, + { + "cell_type": "markdown", + "id": "177", + "metadata": {}, + "source": [ + "#### Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "178", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "179", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-4.ipynb b/docs/docs/tutorials/ed-4.ipynb new file mode 100644 index 00000000..0b784054 --- /dev/null +++ b/docs/docs/tutorials/ed-4.ipynb @@ -0,0 +1,705 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: PbSO4, NPD + XRD\n", + "\n", + "This example demonstrates a more advanced use of the EasyDiffraction\n", + "library by explicitly creating and configuring structures and\n", + "experiments before adding them to a project. It could be more suitable\n", + "for users who are interested in creating custom workflows. This\n", + "tutorial provides minimal explanation and is intended for users\n", + "already familiar with EasyDiffraction.\n", + "\n", + "The tutorial covers a Rietveld refinement of PbSO4 crystal structure\n", + "based on the joint fit of both X-ray and neutron diffraction data." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from easydiffraction import ExperimentFactory\n", + "from easydiffraction import Project\n", + "from easydiffraction import StructureFactory\n", + "from easydiffraction import download_data" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Define Structure\n", + "\n", + "This section shows how to add structures and modify their\n", + "parameters.\n", + "\n", + "#### Create Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "structure = StructureFactory.from_scratch(name='pbso4')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "structure.space_group.name_h_m = 'P n m a'" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a = 8.47\n", + "structure.cell.length_b = 5.39\n", + "structure.cell.length_c = 6.95" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "structure.atom_sites.create(\n", + " label='Pb',\n", + " type_symbol='Pb',\n", + " fract_x=0.1876,\n", + " fract_y=0.25,\n", + " fract_z=0.167,\n", + " wyckoff_letter='c',\n", + " b_iso=1.37,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='S',\n", + " type_symbol='S',\n", + " fract_x=0.0654,\n", + " fract_y=0.25,\n", + " fract_z=0.684,\n", + " wyckoff_letter='c',\n", + " b_iso=0.3777,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O1',\n", + " type_symbol='O',\n", + " fract_x=0.9082,\n", + " fract_y=0.25,\n", + " fract_z=0.5954,\n", + " wyckoff_letter='c',\n", + " b_iso=1.9764,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O2',\n", + " type_symbol='O',\n", + " fract_x=0.1935,\n", + " fract_y=0.25,\n", + " fract_z=0.5432,\n", + " wyckoff_letter='c',\n", + " b_iso=1.4456,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O3',\n", + " type_symbol='O',\n", + " fract_x=0.0811,\n", + " fract_y=0.0272,\n", + " fract_z=0.8086,\n", + " wyckoff_letter='d',\n", + " b_iso=1.2822,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Define Experiments\n", + "\n", + "This section shows how to add experiments, configure their parameters,\n", + "and link the structures defined in the previous step.\n", + "\n", + "### Experiment 1: npd\n", + "\n", + "#### Download Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path1 = download_data(id=13, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "expt1 = ExperimentFactory.from_data_path(\n", + " name='npd',\n", + " data_path=data_path1,\n", + " radiation_probe='neutron',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "expt1.instrument.setup_wavelength = 1.91\n", + "expt1.instrument.calib_twotheta_offset = -0.1406" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "expt1.peak.broad_gauss_u = 0.139\n", + "expt1.peak.broad_gauss_v = -0.412\n", + "expt1.peak.broad_gauss_w = 0.386\n", + "expt1.peak.broad_lorentz_x = 0\n", + "expt1.peak.broad_lorentz_y = 0.088" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "Select the background type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "expt1.background_type = 'line-segment'" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "Add background points." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "for id, x, y in [\n", + " ('1', 11.0, 206.1624),\n", + " ('2', 15.0, 194.75),\n", + " ('3', 20.0, 194.505),\n", + " ('4', 30.0, 188.4375),\n", + " ('5', 50.0, 207.7633),\n", + " ('6', 70.0, 201.7002),\n", + " ('7', 120.0, 244.4525),\n", + " ('8', 153.0, 226.0595),\n", + "]:\n", + " expt1.background.create(id=id, x=x, y=y)" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "expt1.linked_phases.create(id='pbso4', scale=1.5)" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "### Experiment 2: xrd\n", + "\n", + "#### Download Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "data_path2 = download_data(id=16, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "expt2 = ExperimentFactory.from_data_path(\n", + " name='xrd',\n", + " data_path=data_path2,\n", + " radiation_probe='xray',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31", + "metadata": {}, + "outputs": [], + "source": [ + "expt2.instrument.setup_wavelength = 1.540567\n", + "expt2.instrument.calib_twotheta_offset = -0.05181" + ] + }, + { + "cell_type": "markdown", + "id": "32", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33", + "metadata": {}, + "outputs": [], + "source": [ + "expt2.peak.broad_gauss_u = 0.304138\n", + "expt2.peak.broad_gauss_v = -0.112622\n", + "expt2.peak.broad_gauss_w = 0.021272\n", + "expt2.peak.broad_lorentz_x = 0\n", + "expt2.peak.broad_lorentz_y = 0.057691" + ] + }, + { + "cell_type": "markdown", + "id": "34", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "markdown", + "id": "35", + "metadata": {}, + "source": [ + "Select background type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36", + "metadata": {}, + "outputs": [], + "source": [ + "expt2.background_type = 'chebyshev'" + ] + }, + { + "cell_type": "markdown", + "id": "37", + "metadata": {}, + "source": [ + "Add background points." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [ + "for id, x, y in [\n", + " ('1', 0, 119.195),\n", + " ('2', 1, 6.221),\n", + " ('3', 2, -45.725),\n", + " ('4', 3, 8.119),\n", + " ('5', 4, 54.552),\n", + " ('6', 5, -20.661),\n", + "]:\n", + " expt2.background.create(id=id, order=x, coef=y)" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], + "source": [ + "expt2.linked_phases.create(id='pbso4', scale=0.001)" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "## Define Project\n", + "\n", + "The project object is used to manage structures, experiments, and\n", + "analysis.\n", + "\n", + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()" + ] + }, + { + "cell_type": "markdown", + "id": "43", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add(structure)" + ] + }, + { + "cell_type": "markdown", + "id": "45", + "metadata": {}, + "source": [ + "#### Add Experiments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add(expt1)\n", + "project.experiments.add(expt2)" + ] + }, + { + "cell_type": "markdown", + "id": "47", + "metadata": {}, + "source": [ + "## Perform Analysis\n", + "\n", + "This section outlines the analysis process, including how to configure\n", + "calculation and fitting engines.\n", + "\n", + "#### Set Fit Mode" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit_mode.mode = 'joint'" + ] + }, + { + "cell_type": "markdown", + "id": "49", + "metadata": {}, + "source": [ + "#### Set Minimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "51", + "metadata": {}, + "source": [ + "#### Set Fitting Parameters\n", + "\n", + "Set structure parameters to be optimized." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a.free = True\n", + "structure.cell.length_b.free = True\n", + "structure.cell.length_c.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "53", + "metadata": {}, + "source": [ + "Set experiment parameters to be optimized." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54", + "metadata": {}, + "outputs": [], + "source": [ + "expt1.linked_phases['pbso4'].scale.free = True\n", + "\n", + "expt1.instrument.calib_twotheta_offset.free = True\n", + "\n", + "expt1.peak.broad_gauss_u.free = True\n", + "expt1.peak.broad_gauss_v.free = True\n", + "expt1.peak.broad_gauss_w.free = True\n", + "expt1.peak.broad_lorentz_y.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55", + "metadata": {}, + "outputs": [], + "source": [ + "expt2.linked_phases['pbso4'].scale.free = True\n", + "\n", + "expt2.instrument.calib_twotheta_offset.free = True\n", + "\n", + "expt2.peak.broad_gauss_u.free = True\n", + "expt2.peak.broad_gauss_v.free = True\n", + "expt2.peak.broad_gauss_w.free = True\n", + "expt2.peak.broad_lorentz_y.free = True\n", + "\n", + "for term in expt2.background:\n", + " term.coef.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "56", + "metadata": {}, + "source": [ + "#### Perform Fit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "58", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='npd', x_min=35.5, x_max=38.3, show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='xrd', x_min=29.0, x_max=30.4, show_residual=True)" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-5.ipynb b/docs/docs/tutorials/ed-5.ipynb new file mode 100644 index 00000000..2ee5ec1a --- /dev/null +++ b/docs/docs/tutorials/ed-5.ipynb @@ -0,0 +1,638 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: Co2SiO4, D20\n", + "\n", + "This example demonstrates a Rietveld refinement of Co2SiO4 crystal\n", + "structure using constant wavelength neutron powder diffraction data\n", + "from D20 at ILL." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from easydiffraction import ExperimentFactory\n", + "from easydiffraction import Project\n", + "from easydiffraction import StructureFactory\n", + "from easydiffraction import download_data" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Define Structure\n", + "\n", + "This section shows how to add structures and modify their\n", + "parameters.\n", + "\n", + "#### Create Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "structure = StructureFactory.from_scratch(name='cosio')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "structure.space_group.name_h_m = 'P n m a'\n", + "structure.space_group.it_coordinate_system_code = 'abc'" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a = 10.3\n", + "structure.cell.length_b = 6.0\n", + "structure.cell.length_c = 4.8" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites.create(\n", + " label='Co1',\n", + " type_symbol='Co',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Co2',\n", + " type_symbol='Co',\n", + " fract_x=0.279,\n", + " fract_y=0.25,\n", + " fract_z=0.985,\n", + " wyckoff_letter='c',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Si',\n", + " type_symbol='Si',\n", + " fract_x=0.094,\n", + " fract_y=0.25,\n", + " fract_z=0.429,\n", + " wyckoff_letter='c',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O1',\n", + " type_symbol='O',\n", + " fract_x=0.091,\n", + " fract_y=0.25,\n", + " fract_z=0.771,\n", + " wyckoff_letter='c',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O2',\n", + " type_symbol='O',\n", + " fract_x=0.448,\n", + " fract_y=0.25,\n", + " fract_z=0.217,\n", + " wyckoff_letter='c',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O3',\n", + " type_symbol='O',\n", + " fract_x=0.164,\n", + " fract_y=0.032,\n", + " fract_z=0.28,\n", + " wyckoff_letter='d',\n", + " b_iso=0.5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Define Experiment\n", + "\n", + "This section shows how to add experiments, configure their parameters,\n", + "and link the structures defined in the previous step.\n", + "\n", + "#### Download Measured Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = download_data(id=12, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "expt = ExperimentFactory.from_data_path(name='d20', data_path=data_path)" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "expt.instrument.setup_wavelength = 1.87\n", + "expt.instrument.calib_twotheta_offset = 0.1" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "expt.peak.broad_gauss_u = 0.3\n", + "expt.peak.broad_gauss_v = -0.5\n", + "expt.peak.broad_gauss_w = 0.4" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "expt.background.create(id='1', x=8, y=500)\n", + "expt.background.create(id='2', x=9, y=500)\n", + "expt.background.create(id='3', x=10, y=500)\n", + "expt.background.create(id='4', x=11, y=500)\n", + "expt.background.create(id='5', x=12, y=500)\n", + "expt.background.create(id='6', x=15, y=500)\n", + "expt.background.create(id='7', x=25, y=500)\n", + "expt.background.create(id='8', x=30, y=500)\n", + "expt.background.create(id='9', x=50, y=500)\n", + "expt.background.create(id='10', x=70, y=500)\n", + "expt.background.create(id='11', x=90, y=500)\n", + "expt.background.create(id='12', x=110, y=500)\n", + "expt.background.create(id='13', x=130, y=500)\n", + "expt.background.create(id='14', x=150, y=500)" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "expt.linked_phases.create(id='cosio', scale=1.0)" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "## Define Project\n", + "\n", + "The project object is used to manage the structure, experiment, and\n", + "analysis.\n", + "\n", + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "#### Set Plotting Engine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "# Keep the auto-selected engine. Alternatively, you can uncomment the\n", + "# line below to explicitly set the engine to the required one.\n", + "# project.plotter.engine = 'plotly'" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add(structure)" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "#### Add Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add(expt)" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "## Perform Analysis\n", + "\n", + "This section shows the analysis process, including how to set up\n", + "calculation and fitting engines.\n", + "\n", + "#### Set Minimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='d20', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='d20', x_min=41, x_max=54, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": {}, + "source": [ + "#### Set Free Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a.free = True\n", + "structure.cell.length_b.free = True\n", + "structure.cell.length_c.free = True\n", + "\n", + "structure.atom_sites['Co2'].fract_x.free = True\n", + "structure.atom_sites['Co2'].fract_z.free = True\n", + "structure.atom_sites['Si'].fract_x.free = True\n", + "structure.atom_sites['Si'].fract_z.free = True\n", + "structure.atom_sites['O1'].fract_x.free = True\n", + "structure.atom_sites['O1'].fract_z.free = True\n", + "structure.atom_sites['O2'].fract_x.free = True\n", + "structure.atom_sites['O2'].fract_z.free = True\n", + "structure.atom_sites['O3'].fract_x.free = True\n", + "structure.atom_sites['O3'].fract_y.free = True\n", + "structure.atom_sites['O3'].fract_z.free = True\n", + "\n", + "structure.atom_sites['Co1'].b_iso.free = True\n", + "structure.atom_sites['Co2'].b_iso.free = True\n", + "structure.atom_sites['Si'].b_iso.free = True\n", + "structure.atom_sites['O1'].b_iso.free = True\n", + "structure.atom_sites['O2'].b_iso.free = True\n", + "structure.atom_sites['O3'].b_iso.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [ + "expt.linked_phases['cosio'].scale.free = True\n", + "\n", + "expt.instrument.calib_twotheta_offset.free = True\n", + "\n", + "expt.peak.broad_gauss_u.free = True\n", + "expt.peak.broad_gauss_v.free = True\n", + "expt.peak.broad_gauss_w.free = True\n", + "expt.peak.broad_lorentz_y.free = True\n", + "\n", + "for point in expt.background:\n", + " point.y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": {}, + "source": [ + "#### Set Constraints\n", + "\n", + "Set aliases for parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.aliases.create(\n", + " label='biso_Co1',\n", + " param_uid=project.structures['cosio'].atom_sites['Co1'].b_iso.uid,\n", + ")\n", + "project.analysis.aliases.create(\n", + " label='biso_Co2',\n", + " param_uid=project.structures['cosio'].atom_sites['Co2'].b_iso.uid,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "Set constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.constraints.create(\n", + " lhs_alias='biso_Co2',\n", + " rhs_expr='biso_Co1',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "43", + "metadata": {}, + "source": [ + "Apply constraints." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.apply_constraints()" + ] + }, + { + "cell_type": "markdown", + "id": "45", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "47", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='d20', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='d20', x_min=41, x_max=54, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "50", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This final section shows how to review the results of the analysis." + ] + }, + { + "cell_type": "markdown", + "id": "51", + "metadata": {}, + "source": [ + "#### Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-6.ipynb b/docs/docs/tutorials/ed-6.ipynb new file mode 100644 index 00000000..599c09eb --- /dev/null +++ b/docs/docs/tutorials/ed-6.ipynb @@ -0,0 +1,849 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: HS, HRPT\n", + "\n", + "This example demonstrates a Rietveld refinement of HS crystal\n", + "structure using constant wavelength neutron powder diffraction data\n", + "from HRPT at PSI." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from easydiffraction import ExperimentFactory\n", + "from easydiffraction import Project\n", + "from easydiffraction import StructureFactory\n", + "from easydiffraction import download_data" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Define Structure\n", + "\n", + "This section shows how to add structures and modify their\n", + "parameters.\n", + "\n", + "#### Create Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "structure = StructureFactory.from_scratch(name='hs')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "structure.space_group.name_h_m = 'R -3 m'\n", + "structure.space_group.it_coordinate_system_code = 'h'" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a = 6.9\n", + "structure.cell.length_c = 14.1" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites.create(\n", + " label='Zn',\n", + " type_symbol='Zn',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0.5,\n", + " wyckoff_letter='b',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Cu',\n", + " type_symbol='Cu',\n", + " fract_x=0.5,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='e',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='O',\n", + " type_symbol='O',\n", + " fract_x=0.21,\n", + " fract_y=-0.21,\n", + " fract_z=0.06,\n", + " wyckoff_letter='h',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Cl',\n", + " type_symbol='Cl',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0.197,\n", + " wyckoff_letter='c',\n", + " b_iso=0.5,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='H',\n", + " type_symbol='2H',\n", + " fract_x=0.13,\n", + " fract_y=-0.13,\n", + " fract_z=0.08,\n", + " wyckoff_letter='h',\n", + " b_iso=0.5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Define Experiment\n", + "\n", + "This section shows how to add experiments, configure their parameters,\n", + "and link the structures defined in the previous step.\n", + "\n", + "#### Download Measured Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = download_data(id=11, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "expt = ExperimentFactory.from_data_path(name='hrpt', data_path=data_path)" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "expt.instrument.setup_wavelength = 1.89\n", + "expt.instrument.calib_twotheta_offset = 0.0" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "expt.peak.broad_gauss_u = 0.1\n", + "expt.peak.broad_gauss_v = -0.2\n", + "expt.peak.broad_gauss_w = 0.2\n", + "expt.peak.broad_lorentz_x = 0.0\n", + "expt.peak.broad_lorentz_y = 0" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "expt.background.create(id='1', x=4.4196, y=500)\n", + "expt.background.create(id='2', x=6.6207, y=500)\n", + "expt.background.create(id='3', x=10.4918, y=500)\n", + "expt.background.create(id='4', x=15.4634, y=500)\n", + "expt.background.create(id='5', x=45.6041, y=500)\n", + "expt.background.create(id='6', x=74.6844, y=500)\n", + "expt.background.create(id='7', x=103.4187, y=500)\n", + "expt.background.create(id='8', x=121.6311, y=500)\n", + "expt.background.create(id='9', x=159.4116, y=500)" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "expt.linked_phases.create(id='hs', scale=0.5)" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "## Define Project\n", + "\n", + "The project object is used to manage the structure, experiment, and\n", + "analysis.\n", + "\n", + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "#### Set Plotting Engine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "# Keep the auto-selected engine. Alternatively, you can uncomment the\n", + "# line below to explicitly set the engine to the required one.\n", + "# project.plotter.engine = 'plotly'" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add(structure)" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "#### Add Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add(expt)" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "## Perform Analysis\n", + "\n", + "This section shows the analysis process, including how to set up\n", + "calculation and fitting engines.\n", + "\n", + "#### Set Minimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=48, x_max=51, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": {}, + "source": [ + "### Perform Fit 1/5\n", + "\n", + "Set parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a.free = True\n", + "structure.cell.length_c.free = True\n", + "\n", + "expt.linked_phases['hs'].scale.free = True\n", + "expt.instrument.calib_twotheta_offset.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "38", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "40", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "43", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=48, x_max=51, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "46", + "metadata": {}, + "source": [ + "### Perform Fit 2/5\n", + "\n", + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47", + "metadata": {}, + "outputs": [], + "source": [ + "expt.peak.broad_gauss_u.free = True\n", + "expt.peak.broad_gauss_v.free = True\n", + "expt.peak.broad_gauss_w.free = True\n", + "expt.peak.broad_lorentz_x.free = True\n", + "\n", + "for point in expt.background:\n", + " point.y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "48", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "50", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "53", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=48, x_max=51, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "56", + "metadata": {}, + "source": [ + "### Perform Fit 3/5\n", + "\n", + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites['O'].fract_x.free = True\n", + "structure.atom_sites['O'].fract_z.free = True\n", + "structure.atom_sites['Cl'].fract_z.free = True\n", + "structure.atom_sites['H'].fract_x.free = True\n", + "structure.atom_sites['H'].fract_z.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "58", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "60", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "63", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=48, x_max=51, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "66", + "metadata": {}, + "source": [ + "### Perform Fit 4/5\n", + "\n", + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites['Zn'].b_iso.free = True\n", + "structure.atom_sites['Cu'].b_iso.free = True\n", + "structure.atom_sites['O'].b_iso.free = True\n", + "structure.atom_sites['Cl'].b_iso.free = True\n", + "structure.atom_sites['H'].b_iso.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "68", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "70", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "73", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', x_min=48, x_max=51, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "76", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This final section shows how to review the results of the analysis." + ] + }, + { + "cell_type": "markdown", + "id": "77", + "metadata": {}, + "source": [ + "#### Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-7.ipynb b/docs/docs/tutorials/ed-7.ipynb new file mode 100644 index 00000000..869dc723 --- /dev/null +++ b/docs/docs/tutorials/ed-7.ipynb @@ -0,0 +1,741 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: Si, SEPD\n", + "\n", + "This example demonstrates a Rietveld refinement of Si crystal\n", + "structure using time-of-flight neutron powder diffraction data from\n", + "SEPD at Argonne." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from easydiffraction import ExperimentFactory\n", + "from easydiffraction import Project\n", + "from easydiffraction import StructureFactory\n", + "from easydiffraction import download_data" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Define Structure\n", + "\n", + "This section shows how to add structures and modify their\n", + "parameters.\n", + "\n", + "#### Create Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "structure = StructureFactory.from_scratch(name='si')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "structure.space_group.name_h_m = 'F d -3 m'\n", + "structure.space_group.it_coordinate_system_code = '2'" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a = 5.431" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites.create(\n", + " label='Si',\n", + " type_symbol='Si',\n", + " fract_x=0.125,\n", + " fract_y=0.125,\n", + " fract_z=0.125,\n", + " b_iso=0.5,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Define Experiment\n", + "\n", + "This section shows how to add experiments, configure their\n", + "parameters, and link the structures defined in the previous step.\n", + "\n", + "#### Download Measured Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = download_data(id=7, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "expt = ExperimentFactory.from_data_path(\n", + " name='sepd', data_path=data_path, beam_mode='time-of-flight'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "expt.instrument.setup_twotheta_bank = 144.845\n", + "expt.instrument.calib_d_to_tof_offset = 0.0\n", + "expt.instrument.calib_d_to_tof_linear = 7476.91\n", + "expt.instrument.calib_d_to_tof_quad = -1.54" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "expt.peak_profile_type = 'pseudo-voigt * ikeda-carpenter'\n", + "expt.peak.broad_gauss_sigma_0 = 3.0\n", + "expt.peak.broad_gauss_sigma_1 = 40.0\n", + "expt.peak.broad_gauss_sigma_2 = 2.0\n", + "expt.peak.broad_mix_beta_0 = 0.04221\n", + "expt.peak.broad_mix_beta_1 = 0.00946" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "#### Set Peak Asymmetry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "expt.peak.asym_alpha_0 = 0.0\n", + "expt.peak.asym_alpha_1 = 0.5971" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "expt.background_type = 'line-segment'\n", + "for x in range(0, 35000, 5000):\n", + " expt.background.create(id=str(x), x=x, y=200)" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "expt.linked_phases.create(id='si', scale=10.0)" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "## Define Project\n", + "\n", + "The project object is used to manage the structure, experiment, and\n", + "analysis.\n", + "\n", + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add(structure)" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "#### Add Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add(expt)" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "## Perform Analysis\n", + "\n", + "This section shows the analysis process, including how to set up\n", + "calculation and fitting engines.\n", + "\n", + "#### Set Minimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', show_residual=True)\n", + "project.plot_meas_vs_calc(expt_name='sepd', x_min=23200, x_max=23700, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "35", + "metadata": {}, + "source": [ + "### Perform Fit 1/5\n", + "\n", + "Set parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a.free = True\n", + "\n", + "expt.linked_phases['si'].scale.free = True\n", + "expt.instrument.calib_d_to_tof_offset.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "37", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', x_min=23200, x_max=23700, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "44", + "metadata": {}, + "source": [ + "### Perform Fit 2/5\n", + "\n", + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45", + "metadata": {}, + "outputs": [], + "source": [ + "for point in expt.background:\n", + " point.y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "46", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "48", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "50", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', x_min=23200, x_max=23700, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "53", + "metadata": {}, + "source": [ + "### Perform Fit 3/5\n", + "\n", + "Fix background points." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54", + "metadata": {}, + "outputs": [], + "source": [ + "for point in expt.background:\n", + " point.y.free = False" + ] + }, + { + "cell_type": "markdown", + "id": "55", + "metadata": {}, + "source": [ + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56", + "metadata": {}, + "outputs": [], + "source": [ + "expt.peak.broad_gauss_sigma_0.free = True\n", + "expt.peak.broad_gauss_sigma_1.free = True\n", + "expt.peak.broad_gauss_sigma_2.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "57", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "59", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "61", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', x_min=23200, x_max=23700, show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "64", + "metadata": {}, + "source": [ + "### Perform Fit 4/5\n", + "\n", + "Set more parameters to be refined." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites['Si'].b_iso.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "66", + "metadata": {}, + "source": [ + "Show free parameters after selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_free_params()" + ] + }, + { + "cell_type": "markdown", + "id": "68", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "70", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='sepd', x_min=23200, x_max=23700, show_residual=True)" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-8.ipynb b/docs/docs/tutorials/ed-8.ipynb new file mode 100644 index 00000000..4bc42ad1 --- /dev/null +++ b/docs/docs/tutorials/ed-8.ipynb @@ -0,0 +1,747 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: NCAF, WISH\n", + "\n", + "This example demonstrates a Rietveld refinement of Na2Ca3Al2F14\n", + "crystal structure using time-of-flight neutron powder diffraction data\n", + "from WISH at ISIS.\n", + "\n", + "Two datasets from detector banks 5+6 and 4+7 are used for joint\n", + "fitting." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from easydiffraction import ExperimentFactory\n", + "from easydiffraction import Project\n", + "from easydiffraction import StructureFactory\n", + "from easydiffraction import download_data" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Define Structure\n", + "\n", + "This section covers how to add structures and modify their\n", + "parameters.\n", + "\n", + "#### Create Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "structure = StructureFactory.from_scratch(name='ncaf')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "structure.space_group.name_h_m = 'I 21 3'\n", + "structure.space_group.it_coordinate_system_code = '1'" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure.cell.length_a = 10.250256" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites.create(\n", + " label='Ca',\n", + " type_symbol='Ca',\n", + " fract_x=0.4663,\n", + " fract_y=0.0,\n", + " fract_z=0.25,\n", + " wyckoff_letter='b',\n", + " b_iso=0.92,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Al',\n", + " type_symbol='Al',\n", + " fract_x=0.2521,\n", + " fract_y=0.2521,\n", + " fract_z=0.2521,\n", + " wyckoff_letter='a',\n", + " b_iso=0.73,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='Na',\n", + " type_symbol='Na',\n", + " fract_x=0.0851,\n", + " fract_y=0.0851,\n", + " fract_z=0.0851,\n", + " wyckoff_letter='a',\n", + " b_iso=2.08,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='F1',\n", + " type_symbol='F',\n", + " fract_x=0.1377,\n", + " fract_y=0.3054,\n", + " fract_z=0.1195,\n", + " wyckoff_letter='c',\n", + " b_iso=0.90,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='F2',\n", + " type_symbol='F',\n", + " fract_x=0.3625,\n", + " fract_y=0.3633,\n", + " fract_z=0.1867,\n", + " wyckoff_letter='c',\n", + " b_iso=1.37,\n", + ")\n", + "structure.atom_sites.create(\n", + " label='F3',\n", + " type_symbol='F',\n", + " fract_x=0.4612,\n", + " fract_y=0.4612,\n", + " fract_z=0.4612,\n", + " wyckoff_letter='a',\n", + " b_iso=0.88,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Define Experiment\n", + "\n", + "This section shows how to add experiments, configure their parameters,\n", + "and link the structures defined in the previous step.\n", + "\n", + "#### Download Measured Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "data_path56 = download_data(id=9, destination='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "data_path47 = download_data(id=10, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "expt56 = ExperimentFactory.from_data_path(\n", + " name='wish_5_6',\n", + " data_path=data_path56,\n", + " beam_mode='time-of-flight',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "expt47 = ExperimentFactory.from_data_path(\n", + " name='wish_4_7',\n", + " data_path=data_path47,\n", + " beam_mode='time-of-flight',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "expt56.instrument.setup_twotheta_bank = 152.827\n", + "expt56.instrument.calib_d_to_tof_offset = -13.5\n", + "expt56.instrument.calib_d_to_tof_linear = 20773.0\n", + "expt56.instrument.calib_d_to_tof_quad = -1.08308" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "expt47.instrument.setup_twotheta_bank = 121.660\n", + "expt47.instrument.calib_d_to_tof_offset = -15.0\n", + "expt47.instrument.calib_d_to_tof_linear = 18660.0\n", + "expt47.instrument.calib_d_to_tof_quad = -0.47488" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "expt56.peak.broad_gauss_sigma_0 = 0.0\n", + "expt56.peak.broad_gauss_sigma_1 = 0.0\n", + "expt56.peak.broad_gauss_sigma_2 = 15.5\n", + "expt56.peak.broad_mix_beta_0 = 0.007\n", + "expt56.peak.broad_mix_beta_1 = 0.01\n", + "expt56.peak.asym_alpha_0 = -0.0094\n", + "expt56.peak.asym_alpha_1 = 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "expt47.peak.broad_gauss_sigma_0 = 0.0\n", + "expt47.peak.broad_gauss_sigma_1 = 29.8\n", + "expt47.peak.broad_gauss_sigma_2 = 18.0\n", + "expt47.peak.broad_mix_beta_0 = 0.006\n", + "expt47.peak.broad_mix_beta_1 = 0.015\n", + "expt47.peak.asym_alpha_0 = -0.0115\n", + "expt47.peak.asym_alpha_1 = 0.1" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "expt56.background_type = 'line-segment'\n", + "for idx, (x, y) in enumerate(\n", + " [\n", + " (9162, 465),\n", + " (11136, 593),\n", + " (13313, 497),\n", + " (14906, 546),\n", + " (16454, 533),\n", + " (17352, 496),\n", + " (18743, 428),\n", + " (20179, 452),\n", + " (21368, 397),\n", + " (22176, 468),\n", + " (22827, 477),\n", + " (24644, 380),\n", + " (26439, 381),\n", + " (28257, 378),\n", + " (31196, 343),\n", + " (34034, 328),\n", + " (37265, 310),\n", + " (41214, 323),\n", + " (44827, 283),\n", + " (49830, 273),\n", + " (52905, 257),\n", + " (58204, 260),\n", + " (62916, 261),\n", + " (70186, 262),\n", + " (74204, 262),\n", + " (82103, 268),\n", + " (91958, 268),\n", + " (102712, 262),\n", + " ],\n", + " start=1,\n", + "):\n", + " expt56.background.create(id=str(idx), x=x, y=y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "expt47.background_type = 'line-segment'\n", + "for idx, (x, y) in enumerate(\n", + " [\n", + " (9090, 488),\n", + " (10672, 566),\n", + " (12287, 494),\n", + " (14037, 559),\n", + " (15451, 529),\n", + " (16764, 445),\n", + " (18076, 460),\n", + " (19456, 413),\n", + " (20466, 511),\n", + " (21880, 396),\n", + " (23798, 391),\n", + " (25447, 385),\n", + " (28073, 349),\n", + " (30058, 332),\n", + " (32583, 309),\n", + " (34804, 355),\n", + " (37160, 318),\n", + " (40324, 290),\n", + " (46895, 260),\n", + " (50631, 256),\n", + " (54602, 246),\n", + " (58439, 264),\n", + " (66520, 250),\n", + " (75002, 258),\n", + " (83649, 257),\n", + " (92770, 255),\n", + " (101524, 260),\n", + " ],\n", + " start=1,\n", + "):\n", + " expt47.background.create(id=str(idx), x=x, y=y)" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "expt56.linked_phases.create(id='ncaf', scale=1.0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "expt47.linked_phases.create(id='ncaf', scale=2.0)" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "#### Set Excluded Regions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "expt56.excluded_regions.create(id='1', start=0, end=10010)\n", + "expt56.excluded_regions.create(id='2', start=100010, end=200000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31", + "metadata": {}, + "outputs": [], + "source": [ + "expt47.excluded_regions.create(id='1', start=0, end=10006)\n", + "expt47.excluded_regions.create(id='2', start=100004, end=200000)" + ] + }, + { + "cell_type": "markdown", + "id": "32", + "metadata": {}, + "source": [ + "## Define Project\n", + "\n", + "The project object is used to manage the structure, experiments,\n", + "and analysis\n", + "\n", + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()" + ] + }, + { + "cell_type": "markdown", + "id": "34", + "metadata": {}, + "source": [ + "#### Set Plotting Engine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35", + "metadata": {}, + "outputs": [], + "source": [ + "# Keep the auto-selected engine. Alternatively, you can uncomment the\n", + "# line below to explicitly set the engine to the required one.\n", + "# project.plotter.engine = 'plotly'" + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": {}, + "source": [ + "#### Add Structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add(structure)" + ] + }, + { + "cell_type": "markdown", + "id": "38", + "metadata": {}, + "source": [ + "#### Add Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add(expt56)\n", + "project.experiments.add(expt47)" + ] + }, + { + "cell_type": "markdown", + "id": "40", + "metadata": {}, + "source": [ + "## Perform Analysis\n", + "\n", + "This section shows the analysis process, including how to set up\n", + "calculation and fitting engines.\n", + "\n", + "#### Set Minimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "42", + "metadata": {}, + "source": [ + "#### Set Fit Mode" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit_mode.mode = 'joint'" + ] + }, + { + "cell_type": "markdown", + "id": "44", + "metadata": {}, + "source": [ + "#### Set Free Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45", + "metadata": {}, + "outputs": [], + "source": [ + "structure.atom_sites['Ca'].b_iso.free = True\n", + "structure.atom_sites['Al'].b_iso.free = True\n", + "structure.atom_sites['Na'].b_iso.free = True\n", + "structure.atom_sites['F1'].b_iso.free = True\n", + "structure.atom_sites['F2'].b_iso.free = True\n", + "structure.atom_sites['F3'].b_iso.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46", + "metadata": {}, + "outputs": [], + "source": [ + "expt56.linked_phases['ncaf'].scale.free = True\n", + "expt56.instrument.calib_d_to_tof_offset.free = True\n", + "expt56.instrument.calib_d_to_tof_linear.free = True\n", + "expt56.peak.broad_gauss_sigma_2.free = True\n", + "expt56.peak.broad_mix_beta_0.free = True\n", + "expt56.peak.broad_mix_beta_1.free = True\n", + "expt56.peak.asym_alpha_1.free = True\n", + "\n", + "expt47.linked_phases['ncaf'].scale.free = True\n", + "expt47.instrument.calib_d_to_tof_linear.free = True\n", + "expt47.instrument.calib_d_to_tof_offset.free = True\n", + "expt47.peak.broad_gauss_sigma_2.free = True\n", + "expt47.peak.broad_mix_beta_0.free = True\n", + "expt47.peak.broad_mix_beta_1.free = True\n", + "expt47.peak.asym_alpha_1.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "47", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='wish_5_6', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='wish_4_7', show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "50", + "metadata": {}, + "source": [ + "#### Run Fitting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "52", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='wish_5_6', show_residual=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='wish_4_7', show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "55", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This final section shows how to review the results of the analysis." + ] + }, + { + "cell_type": "markdown", + "id": "56", + "metadata": {}, + "source": [ + "#### Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-9.ipynb b/docs/docs/tutorials/ed-9.ipynb new file mode 100644 index 00000000..735964ae --- /dev/null +++ b/docs/docs/tutorials/ed-9.ipynb @@ -0,0 +1,704 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Structure Refinement: LBCO+Si, McStas\n", + "\n", + "This example demonstrates a Rietveld refinement of La0.5Ba0.5CoO3\n", + "crystal structure with a small amount of Si phase using time-of-flight\n", + "neutron powder diffraction data simulated with McStas." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from easydiffraction import ExperimentFactory\n", + "from easydiffraction import Project\n", + "from easydiffraction import StructureFactory\n", + "from easydiffraction import download_data" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Define Structures\n", + "\n", + "This section shows how to add structures and modify their\n", + "parameters.\n", + "\n", + "### Create Structure 1: LBCO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "structure_1 = StructureFactory.from_scratch(name='lbco')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "structure_1.space_group.name_h_m = 'P m -3 m'\n", + "structure_1.space_group.it_coordinate_system_code = '1'" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "structure_1.cell.length_a = 3.8909" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "structure_1.atom_sites.create(\n", + " label='La',\n", + " type_symbol='La',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.2,\n", + " occupancy=0.5,\n", + ")\n", + "structure_1.atom_sites.create(\n", + " label='Ba',\n", + " type_symbol='Ba',\n", + " fract_x=0,\n", + " fract_y=0,\n", + " fract_z=0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.2,\n", + " occupancy=0.5,\n", + ")\n", + "structure_1.atom_sites.create(\n", + " label='Co',\n", + " type_symbol='Co',\n", + " fract_x=0.5,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='b',\n", + " b_iso=0.2567,\n", + ")\n", + "structure_1.atom_sites.create(\n", + " label='O',\n", + " type_symbol='O',\n", + " fract_x=0,\n", + " fract_y=0.5,\n", + " fract_z=0.5,\n", + " wyckoff_letter='c',\n", + " b_iso=1.4041,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "### Create Structure 2: Si" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "structure_2 = StructureFactory.from_scratch(name='si')" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "#### Set Space Group" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "structure_2.space_group.name_h_m = 'F d -3 m'\n", + "structure_2.space_group.it_coordinate_system_code = '2'" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "#### Set Unit Cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "structure_2.cell.length_a = 5.43146" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "#### Set Atom Sites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "structure_2.atom_sites.create(\n", + " label='Si',\n", + " type_symbol='Si',\n", + " fract_x=0.0,\n", + " fract_y=0.0,\n", + " fract_z=0.0,\n", + " wyckoff_letter='a',\n", + " b_iso=0.0,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "## Define Experiment\n", + "\n", + "This section shows how to add experiments, configure their parameters,\n", + "and link the structures defined in the previous step.\n", + "\n", + "#### Download Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "data_path = download_data(id=8, destination='data')" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "#### Create Experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "experiment = ExperimentFactory.from_data_path(\n", + " name='mcstas',\n", + " data_path=data_path,\n", + " sample_form='powder',\n", + " beam_mode='time-of-flight',\n", + " radiation_probe='neutron',\n", + " scattering_type='bragg',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "#### Set Instrument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.instrument.setup_twotheta_bank = 94.90931761529106\n", + "experiment.instrument.calib_d_to_tof_offset = 0.0\n", + "experiment.instrument.calib_d_to_tof_linear = 58724.76869981215\n", + "experiment.instrument.calib_d_to_tof_quad = -0.00001" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "#### Set Peak Profile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "# experiment.peak_profile_type = 'pseudo-voigt * ikeda-carpenter'\n", + "experiment.peak.broad_gauss_sigma_0 = 45137\n", + "experiment.peak.broad_gauss_sigma_1 = -52394\n", + "experiment.peak.broad_gauss_sigma_2 = 22998\n", + "experiment.peak.broad_mix_beta_0 = 0.0055\n", + "experiment.peak.broad_mix_beta_1 = 0.0041\n", + "experiment.peak.asym_alpha_0 = 0\n", + "experiment.peak.asym_alpha_1 = 0.0097" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "#### Set Background" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "Select the background type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.background_type = 'line-segment'" + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "Add background points." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.background.create(id='1', x=45000, y=0.2)\n", + "experiment.background.create(id='2', x=50000, y=0.2)\n", + "experiment.background.create(id='3', x=55000, y=0.2)\n", + "experiment.background.create(id='4', x=65000, y=0.2)\n", + "experiment.background.create(id='5', x=70000, y=0.2)\n", + "experiment.background.create(id='6', x=75000, y=0.2)\n", + "experiment.background.create(id='7', x=80000, y=0.2)\n", + "experiment.background.create(id='8', x=85000, y=0.2)\n", + "experiment.background.create(id='9', x=90000, y=0.2)\n", + "experiment.background.create(id='10', x=95000, y=0.2)\n", + "experiment.background.create(id='11', x=100000, y=0.2)\n", + "experiment.background.create(id='12', x=105000, y=0.2)\n", + "experiment.background.create(id='13', x=110000, y=0.2)" + ] + }, + { + "cell_type": "markdown", + "id": "32", + "metadata": {}, + "source": [ + "#### Set Linked Phases" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.linked_phases.create(id='lbco', scale=4.0)\n", + "experiment.linked_phases.create(id='si', scale=0.2)" + ] + }, + { + "cell_type": "markdown", + "id": "34", + "metadata": {}, + "source": [ + "## Define Project\n", + "\n", + "The project object is used to manage structures, experiments, and\n", + "analysis.\n", + "\n", + "#### Create Project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()" + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": {}, + "source": [ + "#### Add Structures" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.add(structure_1)\n", + "project.structures.add(structure_2)" + ] + }, + { + "cell_type": "markdown", + "id": "38", + "metadata": {}, + "source": [ + "#### Show Structures" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39", + "metadata": {}, + "outputs": [], + "source": [ + "project.structures.show_names()" + ] + }, + { + "cell_type": "markdown", + "id": "40", + "metadata": {}, + "source": [ + "#### Add Experiments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add(experiment)" + ] + }, + { + "cell_type": "markdown", + "id": "42", + "metadata": {}, + "source": [ + "#### Set Excluded Regions\n", + "\n", + "Show measured data as loaded from the file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas(expt_name='mcstas')" + ] + }, + { + "cell_type": "markdown", + "id": "44", + "metadata": {}, + "source": [ + "Add excluded regions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.excluded_regions.create(id='1', start=0, end=40000)\n", + "experiment.excluded_regions.create(id='2', start=108000, end=200000)" + ] + }, + { + "cell_type": "markdown", + "id": "46", + "metadata": {}, + "source": [ + "Show excluded regions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.excluded_regions.show()" + ] + }, + { + "cell_type": "markdown", + "id": "48", + "metadata": {}, + "source": [ + "Show measured data after adding excluded regions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas(expt_name='mcstas')" + ] + }, + { + "cell_type": "markdown", + "id": "50", + "metadata": {}, + "source": [ + "Show experiment as CIF." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51", + "metadata": {}, + "outputs": [], + "source": [ + "project.experiments['mcstas'].show_as_cif()" + ] + }, + { + "cell_type": "markdown", + "id": "52", + "metadata": {}, + "source": [ + "## Perform Analysis\n", + "\n", + "This section outlines the analysis process, including how to configure\n", + "calculation and fitting engines.\n", + "\n", + "#### Set Minimizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.current_minimizer = 'lmfit'" + ] + }, + { + "cell_type": "markdown", + "id": "54", + "metadata": {}, + "source": [ + "#### Set Fitting Parameters\n", + "\n", + "Set structure parameters to be optimized." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55", + "metadata": {}, + "outputs": [], + "source": [ + "structure_1.cell.length_a.free = True\n", + "structure_1.atom_sites['Co'].b_iso.free = True\n", + "structure_1.atom_sites['O'].b_iso.free = True\n", + "\n", + "structure_2.cell.length_a.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "56", + "metadata": {}, + "source": [ + "Set experiment parameters to be optimized." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57", + "metadata": {}, + "outputs": [], + "source": [ + "experiment.linked_phases['lbco'].scale.free = True\n", + "experiment.linked_phases['si'].scale.free = True\n", + "\n", + "experiment.peak.broad_gauss_sigma_0.free = True\n", + "experiment.peak.broad_gauss_sigma_1.free = True\n", + "experiment.peak.broad_gauss_sigma_2.free = True\n", + "\n", + "experiment.peak.asym_alpha_1.free = True\n", + "experiment.peak.broad_mix_beta_0.free = True\n", + "experiment.peak.broad_mix_beta_1.free = True\n", + "\n", + "for point in experiment.background:\n", + " point.y.free = True" + ] + }, + { + "cell_type": "markdown", + "id": "58", + "metadata": {}, + "source": [ + "#### Perform Fit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()\n", + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "markdown", + "id": "60", + "metadata": {}, + "source": [ + "#### Plot Measured vs Calculated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='mcstas')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From a2e9c2404da52581ffe820fb8e749326839696bc Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 00:14:11 +0100 Subject: [PATCH 09/48] Clean up --- DEVELOPMENT.md | 150 ------------------------------------------------- 1 file changed, 150 deletions(-) delete mode 100644 DEVELOPMENT.md diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index 0e063133..00000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,150 +0,0 @@ -# Development - -This is an example of a workflow that describes the development process. - -## Installation and setup with Pixi - -- Install Pixi by following the instructions on the - [official Pixi Installation Guide](https://pixi.sh/latest/installation). -- Clone repositories with assets for building documentation - ```bash - git clone https://github.com/easyscience/assets-docs.git - git clone https://github.com/easyscience/assets-branding.git - ``` -- Clone EasyDiffraction library repository - ```bash - git clone https://github.com/easyscience/diffraction-lib - ``` -- Go to the cloned directory - ```bash - cd diffraction-lib - ``` -- Create the environment defined in `pixi.toml` and install all - necessary dependencies: - ```bash - pixi install - ``` -- Install and setup development dependencies - ```bash - pixi run dev - ``` - -## Making changes - -- Checkout/switch to the `develop` branch - ```bash - git checkout develop - ``` -- Create a new branch from the `develop` one - ```bash - git checkout -b new-feature - ``` -- Make changes in the code - ```bash - ... - ``` - -## Checking code quality and testing - -### Pre-commit checks - -- Check code quality (configuration is in `pyproject.toml` and - `prettierrc.toml`) - ```bash - pixi run pre-commit-check - ``` -- Fix some code quality issues automatically - ```bash - pixi run pre-commit-fix - ``` - -### Pre-push checks - -- Run tests and checks before pushing changes - ```bash - pixi run pre-push - ``` - -### Individual tests and checks (if needed) - -- Check coverage by tests and docstrings - ```bash - pixi run cov - ``` -- Run unit tests - ```bash - pixi run unit-tests - ``` -- Run integration tests - ```bash - pixi run integration-tests - ``` -- Test tutorials as python scripts - ```bash - pixi run script-tests - ``` -- Convert tutorial scripts to notebooks - ```bash - pixi run notebook-prepare - ``` -- Test tutorials as notebooks - ```bash - pixi run notebook-tests - ``` - -## Building and checking documentation with MkDocs - -- Move notebooks to docs/tutorials - ```bash - pixi run docs-notebooks - ``` -- Add extra files to build documentation (from `../assets-docs/` and - `../assets-branding/` directories) - ```bash - pixi run docs-assets - ``` -- Create mkdocs.yml file - ```bash - pixi run docs-config - ``` -- Build documentation - ```bash - pixi run docs-build - ``` -- Test the documentation locally (built in the `site/` directory). E.g., - on macOS, open the site in the default browser via the terminal - ```bash - open http://127.0.0.1:8000 - ``` -- Clean up after checking documentation - ```bash - pixi run docs-clean - ``` - -## Committing and pushing changes - -- Commit changes - ```bash - git add . - git commit -m "Add new feature" - ``` -- Push the new branch to a remote repository - ```bash - git push -u origin new-feature - ``` -- Create a pull request on - [EasyScience GitHub repository](https://github.com/easyscience/diffraction-lib/pulls) - and request a review from team members -- Add one of the required labels: - - `[maintainer] auto-pull-request` - - `[scope] bug` - - `[scope] documentation` - - `[scope] enhancement` - - `[scope] maintenance` - - `[scope] significant` -- After approval, merge the pull request into the `develop` branch using - "Squash and merge" option -- Delete the branch remotely - ```bash - git push origin --delete new-feature - ``` From 39e02c18b7f99fe6c24664a0bfac38c103a91436 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 00:28:33 +0100 Subject: [PATCH 10/48] Add missing deps to test workflow --- .github/workflows/test.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ecae0b4..f287ebf2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -199,11 +199,14 @@ jobs: pixi init easydiffraction_py$py_ver cd easydiffraction_py$py_ver + echo "Setting macOS 14.0 as minimum required" + pixi project system-requirements add macos 14.0 + echo "Adding Python $py_ver" pixi add "python=$py_ver" - echo "Setting macOS 14.0 as minimum required" - pixi project system-requirements add macos 14.0 + echo "Adding GNU Scientific Library (GSL)" + pixi add gsl echo "Looking for wheel in ../dist/py$py_ver/" ls -l "../dist/py$py_ver/" From 3887d18614f5cc72dc0e406426edf476a4286890 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 00:50:57 +0100 Subject: [PATCH 11/48] Add parameter-property consistency checker --- pixi.toml | 1 + tools/check_param_consistency.py | 276 +++++++++++++++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 tools/check_param_consistency.py diff --git a/pixi.toml b/pixi.toml index 58a61809..1fed46a0 100644 --- a/pixi.toml +++ b/pixi.toml @@ -111,6 +111,7 @@ py-lint-check = 'ruff check src/ tests/ docs/docs/tutorials/' py-format-check = "ruff format --check src/ tests/ docs/docs/tutorials/" nonpy-format-check = "npx prettier --list-different --config=prettierrc.toml --ignore-unknown ." nonpy-format-check-modified = "python tools/nonpy_prettier_modified.py" +param-consistency-check = 'python tools/check_param_consistency.py' check = 'pre-commit run --hook-stage manual --all-files' diff --git a/tools/check_param_consistency.py b/tools/check_param_consistency.py new file mode 100644 index 00000000..6e5cf75d --- /dev/null +++ b/tools/check_param_consistency.py @@ -0,0 +1,276 @@ +"""Check consistency between Parameter/Descriptor definitions and their +public properties. + +Three checks are performed for every public property whose getter +returns a GenericDescriptorBase subclass instance: + +1. **Docstring vs description** – The first sentence of the property + getter docstring must match the ``description`` string of the + backing Parameter/Descriptor (case-insensitive, ignoring trailing + punctuation). + +2. **Getter return-type annotation vs backing attribute type** – The + annotation on the getter (e.g. ``-> Parameter``) must be the exact + class (or a superclass) of the object stored in ``self._``. + +3. **Setter value-type annotation vs descriptor data type** – For + numeric descriptors the setter ``value`` argument must be annotated + ``float`` (per PEP 484, ``int`` is implicitly accepted wherever + ``float`` is declared). For string descriptors it must be ``str``. + +The script instantiates every concrete ``CategoryItem`` subclass found +under ``src/easydiffraction/`` and introspects properties at runtime. + +Exit code 0 when all checks pass, 1 otherwise. +""" + +from __future__ import annotations + +import contextlib +import importlib +import inspect +import pkgutil +import re +import sys +from pathlib import Path +from typing import get_type_hints + +# --------------------------------------------------------------------------- +# Bootstrap: make sure the package is importable +# --------------------------------------------------------------------------- + +_repo = Path(__file__).resolve().parents[1] +_src = _repo / 'src' +if str(_src) not in sys.path: + sys.path.insert(0, str(_src)) + +from easydiffraction.core.category import CategoryCollection # noqa: E402 +from easydiffraction.core.category import CategoryItem # noqa: E402 +from easydiffraction.core.variable import GenericDescriptorBase # noqa: E402 +from easydiffraction.core.variable import GenericNumericDescriptor # noqa: E402 +from easydiffraction.core.variable import GenericStringDescriptor # noqa: E402 + +# --------------------------------------------------------------------------- +# Discovery helpers +# --------------------------------------------------------------------------- + + +def _import_all_submodules(package_name: str) -> None: + """Recursively import every submodule of *package_name*.""" + package = importlib.import_module(package_name) + prefix = package.__name__ + '.' + for _importer, modname, _ispkg in pkgutil.walk_packages(package.__path__, prefix=prefix): + with contextlib.suppress(Exception): + importlib.import_module(modname) + + +def _concrete_subclasses(base): + """Return all non-abstract subclasses of *base* (deep).""" + result = set() + for sub in base.__subclasses__(): + if not inspect.isabstract(sub): + result.add(sub) + result.update(_concrete_subclasses(sub)) + return result + + +def _safe_instantiate(cls): + """Try to call ``cls()`` with no arguments. Return None on failure.""" + try: + return cls() + except Exception: + return None + + +# --------------------------------------------------------------------------- +# Normalisation helpers +# --------------------------------------------------------------------------- + +_TRAILING_PUNCT = re.compile(r'[.\s]+$') +_MARKDOWN_EMPHASIS = re.compile(r'\*{1,2}([^*]+)\*{1,2}') +_RST_ROLE = re.compile(r':[a-z]+:`([^`]+)`') +_DOUBLE_BACKTICK = re.compile(r'``([^`]+)``') +_UNICODE_DASHES = re.compile(r'[\u2013\u2014]') # en-dash, em-dash + + +def _normalise(text: str) -> str: + """Lower-case, strip markdown/rst formatting, normalise dashes and + trailing punctuation.""" + t = _MARKDOWN_EMPHASIS.sub(r'\1', text) + t = _RST_ROLE.sub(r'\1', t) + t = _DOUBLE_BACKTICK.sub(r'\1', t) + t = _UNICODE_DASHES.sub('-', t) + return _TRAILING_PUNCT.sub('', t.strip()).lower() + + +def _first_sentence(docstring: str | None) -> str: + """Extract the first paragraph / sentence from a docstring.""" + if not docstring: + return '' + first_para = docstring.strip().split('\n\n')[0] + return ' '.join(line.strip() for line in first_para.splitlines()) + + +# --------------------------------------------------------------------------- +# Expected setter annotation per descriptor family +# --------------------------------------------------------------------------- + +_NUMERIC_SETTER_TYPES = {'float', 'int | float', 'int|float'} +_STRING_SETTER_TYPES = {'str'} + + +# --------------------------------------------------------------------------- +# Main checking logic +# --------------------------------------------------------------------------- + + +def _check_class(cls, instance, errors: list[str]) -> None: + """Run all three checks for *cls* using the given *instance*.""" + + # Collect all property objects from the MRO + props: dict[str, property] = {} + for base in cls.__mro__: + for key, val in base.__dict__.items(): + if isinstance(val, property) and not key.startswith('_'): + props.setdefault(key, val) + + for prop_name, prop_obj in props.items(): + # Retrieve the runtime value + try: + val = getattr(instance, prop_name) + except Exception: # noqa: BLE001, S112 + continue + + if not isinstance(val, GenericDescriptorBase): + continue + + getter = prop_obj.fget + setter = prop_obj.fset + loc = f'{cls.__name__}.{prop_name}' + + # --------------------------------------------------------------- + # Check 1: docstring vs description + # --------------------------------------------------------------- + description = getattr(val, '_description', None) or '' + doc = _first_sentence(getter.__doc__) if getter and getter.__doc__ else '' + + if description and not doc: + errors.append( + f'{loc}: getter has no docstring, but Parameter description is "{description}"' + ) + elif description and doc and _normalise(doc) != _normalise(description): + errors.append( + f'{loc}: docstring first sentence does not match ' + f'Parameter description.\n' + f' docstring: "{doc}"\n' + f' description: "{description}"' + ) + + # --------------------------------------------------------------- + # Check 2: getter return-type annotation + # --------------------------------------------------------------- + if getter is not None: + hints = {} + with contextlib.suppress(Exception): + hints = get_type_hints(getter) + ret = hints.get('return') + + if ret is None: + errors.append( + f'{loc}: getter has no return-type annotation (expected {type(val).__name__})' + ) + elif isinstance(ret, type) and not isinstance(val, ret): + errors.append( + f'{loc}: getter return annotation is ' + f'{ret.__name__}, but runtime object is ' + f'{type(val).__name__}' + ) + + # --------------------------------------------------------------- + # Check 3: setter value-type annotation + # --------------------------------------------------------------- + if setter is not None: + hints = {} + with contextlib.suppress(Exception): + hints = get_type_hints(setter) + + # The setter's first (non-self) parameter should be 'value' + sig = inspect.signature(setter) + params = [p for name, p in sig.parameters.items() if name != 'self'] + if params: + value_param = params[0] + ann = value_param.annotation + if ann is inspect.Parameter.empty: + is_numeric = isinstance(val, GenericNumericDescriptor) + is_string = isinstance(val, GenericStringDescriptor) + expected = 'float' if is_numeric else 'str' if is_string else '?' + errors.append( + f'{loc}: setter parameter ' + f'"{value_param.name}" has no type ' + f'annotation (expected {expected})' + ) + else: + ann_str = ann.__name__ if isinstance(ann, type) else str(ann) + if ( + isinstance(val, GenericNumericDescriptor) + and ann_str.lower() not in _NUMERIC_SETTER_TYPES + ): + errors.append( + f'{loc}: setter annotated ' + f'"{ann_str}" for a numeric ' + f'descriptor (expected float)' + ) + elif ( + isinstance(val, GenericStringDescriptor) + and ann_str.lower() not in _STRING_SETTER_TYPES + ): + errors.append( + f'{loc}: setter annotated ' + f'"{ann_str}" for a string ' + f'descriptor (expected str)' + ) + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + + +def main() -> int: + """Run all consistency checks and print results.""" + # Import everything so all subclasses are registered + _import_all_submodules('easydiffraction') + + errors: list[str] = [] + checked = 0 + + # Check CategoryItem subclasses + for cls in sorted(_concrete_subclasses(CategoryItem), key=lambda c: c.__name__): + instance = _safe_instantiate(cls) + if instance is None: + continue + _check_class(cls, instance, errors) + checked += 1 + + # Also check CategoryCollection subclasses (some hold descriptors) + for cls in sorted(_concrete_subclasses(CategoryCollection), key=lambda c: c.__name__): + instance = _safe_instantiate(cls) + if instance is None: + continue + _check_class(cls, instance, errors) + checked += 1 + + # Summary + print(f'\nChecked {checked} classes') + if errors: + print(f'\n❌ {len(errors)} consistency issue(s) found:\n') + for i, err in enumerate(errors, 1): + print(f' {i}. {err}\n') + return 1 + else: + print('✅ All parameter-property consistency checks passed.') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) From 899b29baf7664fa35f7d347cd65a41b78428a2c0 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 01:06:39 +0100 Subject: [PATCH 12/48] Use structural type extraction in param consistency checker --- tools/check_param_consistency.py | 259 +++++++++++++++++++++---------- 1 file changed, 174 insertions(+), 85 deletions(-) diff --git a/tools/check_param_consistency.py b/tools/check_param_consistency.py index 6e5cf75d..22244618 100644 --- a/tools/check_param_consistency.py +++ b/tools/check_param_consistency.py @@ -7,21 +7,29 @@ 1. **Docstring vs description** – The first sentence of the property getter docstring must match the ``description`` string of the backing Parameter/Descriptor (case-insensitive, ignoring trailing - punctuation). + punctuation and markup). 2. **Getter return-type annotation vs backing attribute type** – The - annotation on the getter (e.g. ``-> Parameter``) must be the exact - class (or a superclass) of the object stored in ``self._``. + annotation on the getter (e.g. ``-> Parameter``) must resolve to a + type (or union containing a type) that the runtime object is an + instance of. Union, Optional, and Annotated wrappers are + decomposed structurally. 3. **Setter value-type annotation vs descriptor data type** – For numeric descriptors the setter ``value`` argument must be annotated - ``float`` (per PEP 484, ``int`` is implicitly accepted wherever - ``float`` is declared). For string descriptors it must be ``str``. - -The script instantiates every concrete ``CategoryItem`` subclass found -under ``src/easydiffraction/`` and introspects properties at runtime. - -Exit code 0 when all checks pass, 1 otherwise. + with a type drawn from ``{int, float}`` (per PEP 484 numeric + tower, ``float`` alone is the canonical form). For string + descriptors it must be ``str``. Annotations are decomposed + structurally, so ``float``, ``int | float``, ``float | int`` are + all accepted for numeric descriptors. + +The script instantiates every concrete ``CategoryItem`` and +``CategoryCollection`` subclass found under ``src/easydiffraction/`` +and introspects properties at runtime. + +Exit code 0 when all checks pass, 1 otherwise. Import and +instantiation failures are reported explicitly rather than silently +skipped. """ from __future__ import annotations @@ -32,7 +40,11 @@ class (or a superclass) of the object stored in ``self._``. import pkgutil import re import sys +import types from pathlib import Path +from typing import Union +from typing import get_args +from typing import get_origin from typing import get_type_hints # --------------------------------------------------------------------------- @@ -50,23 +62,44 @@ class (or a superclass) of the object stored in ``self._``. from easydiffraction.core.variable import GenericNumericDescriptor # noqa: E402 from easydiffraction.core.variable import GenericStringDescriptor # noqa: E402 +# --------------------------------------------------------------------------- +# Known intermediate bases that require constructor arguments and have +# no descriptor-backed properties of their own. Their concrete leaves +# (PdCwlData, PdTofData, TotalData, etc.) *are* checked. +# If a class is skipped and NOT listed here, the check fails. +# --------------------------------------------------------------------------- + +_KNOWN_INTERMEDIATE_BASES: frozenset[str] = frozenset({ + 'PdDataBase', + 'TotalDataBase', +}) + + # --------------------------------------------------------------------------- # Discovery helpers # --------------------------------------------------------------------------- -def _import_all_submodules(package_name: str) -> None: - """Recursively import every submodule of *package_name*.""" +def _import_all_submodules(package_name: str) -> list[str]: + """Recursively import every submodule of *package_name*. + + Returns: + List of module names that failed to import. + """ package = importlib.import_module(package_name) prefix = package.__name__ + '.' + failed: list[str] = [] for _importer, modname, _ispkg in pkgutil.walk_packages(package.__path__, prefix=prefix): - with contextlib.suppress(Exception): + try: importlib.import_module(modname) + except Exception as exc: # noqa: BLE001 + failed.append(f'{modname}: {exc}') + return failed -def _concrete_subclasses(base): +def _concrete_subclasses(base: type) -> set[type]: """Return all non-abstract subclasses of *base* (deep).""" - result = set() + result: set[type] = set() for sub in base.__subclasses__(): if not inspect.isabstract(sub): result.add(sub) @@ -74,14 +107,6 @@ def _concrete_subclasses(base): return result -def _safe_instantiate(cls): - """Try to call ``cls()`` with no arguments. Return None on failure.""" - try: - return cls() - except Exception: - return None - - # --------------------------------------------------------------------------- # Normalisation helpers # --------------------------------------------------------------------------- @@ -94,7 +119,7 @@ def _safe_instantiate(cls): def _normalise(text: str) -> str: - """Lower-case, strip markdown/rst formatting, normalise dashes and + """Lower-case, strip markup formatting, normalise dashes and trailing punctuation.""" t = _MARKDOWN_EMPHASIS.sub(r'\1', text) t = _RST_ROLE.sub(r'\1', t) @@ -112,11 +137,33 @@ def _first_sentence(docstring: str | None) -> str: # --------------------------------------------------------------------------- -# Expected setter annotation per descriptor family +# Structural type extraction # --------------------------------------------------------------------------- -_NUMERIC_SETTER_TYPES = {'float', 'int | float', 'int|float'} -_STRING_SETTER_TYPES = {'str'} +# Allowed concrete types for setter annotations +_NUMERIC_ALLOWED: frozenset[type] = frozenset({int, float}) +_STRING_ALLOWED: frozenset[type] = frozenset({str}) + + +def _extract_types(annotation: object) -> tuple[type, ...]: + """Extract concrete type objects from an annotation. + + Handles plain types, ``X | Y`` (types.UnionType), + ``typing.Union[X, Y]``, and ``typing.Optional[X]``. + ``NoneType`` members are filtered out. + + Returns: + Tuple of concrete types, or empty tuple when the annotation + cannot be decomposed (e.g. ``Any``, unresolved forward ref). + """ + origin = get_origin(annotation) + if origin is types.UnionType or origin is Union: + return tuple( + a for a in get_args(annotation) if isinstance(a, type) and a is not type(None) + ) + if isinstance(annotation, type): + return (annotation,) + return () # --------------------------------------------------------------------------- @@ -124,7 +171,7 @@ def _first_sentence(docstring: str | None) -> str: # --------------------------------------------------------------------------- -def _check_class(cls, instance, errors: list[str]) -> None: +def _check_class(cls: type, instance: object, errors: list[str]) -> None: """Run all three checks for *cls* using the given *instance*.""" # Collect all property objects from the MRO @@ -170,7 +217,7 @@ def _check_class(cls, instance, errors: list[str]) -> None: # Check 2: getter return-type annotation # --------------------------------------------------------------- if getter is not None: - hints = {} + hints: dict = {} with contextlib.suppress(Exception): hints = get_type_hints(getter) ret = hints.get('return') @@ -179,56 +226,73 @@ def _check_class(cls, instance, errors: list[str]) -> None: errors.append( f'{loc}: getter has no return-type annotation (expected {type(val).__name__})' ) - elif isinstance(ret, type) and not isinstance(val, ret): - errors.append( - f'{loc}: getter return annotation is ' - f'{ret.__name__}, but runtime object is ' - f'{type(val).__name__}' - ) + else: + ret_types = _extract_types(ret) + if not ret_types: + errors.append( + f'{loc}: getter return annotation "{ret}" ' + f'could not be structurally verified ' + f'(expected {type(val).__name__})' + ) + elif not any(isinstance(val, t) for t in ret_types): + names = ' | '.join(t.__name__ for t in ret_types) + errors.append( + f'{loc}: getter return annotation ' + f'{names} does not match runtime type ' + f'{type(val).__name__}' + ) # --------------------------------------------------------------- # Check 3: setter value-type annotation # --------------------------------------------------------------- if setter is not None: - hints = {} + setter_hints: dict = {} with contextlib.suppress(Exception): - hints = get_type_hints(setter) + setter_hints = get_type_hints(setter) - # The setter's first (non-self) parameter should be 'value' sig = inspect.signature(setter) params = [p for name, p in sig.parameters.items() if name != 'self'] - if params: - value_param = params[0] - ann = value_param.annotation - if ann is inspect.Parameter.empty: - is_numeric = isinstance(val, GenericNumericDescriptor) - is_string = isinstance(val, GenericStringDescriptor) - expected = 'float' if is_numeric else 'str' if is_string else '?' + if not params: + continue + + value_param = params[0] + # Prefer resolved hint over raw signature annotation + resolved_ann = setter_hints.get(value_param.name) + raw_ann = value_param.annotation + + if resolved_ann is None and raw_ann is inspect.Parameter.empty: + is_numeric = isinstance(val, GenericNumericDescriptor) + is_string = isinstance(val, GenericStringDescriptor) + expected = 'float' if is_numeric else 'str' if is_string else '?' + errors.append( + f'{loc}: setter parameter ' + f'"{value_param.name}" has no type ' + f'annotation (expected {expected})' + ) + else: + ann = resolved_ann if resolved_ann is not None else raw_ann + ann_types = _extract_types(ann) + + if not ann_types: + errors.append( + f'{loc}: setter annotation "{ann}" could not be structurally verified' + ) + elif isinstance(val, GenericNumericDescriptor) and not set(ann_types).issubset( + _NUMERIC_ALLOWED + ): + names = ' | '.join(t.__name__ for t in ann_types) errors.append( - f'{loc}: setter parameter ' - f'"{value_param.name}" has no type ' - f'annotation (expected {expected})' + f'{loc}: setter annotated ' + f'"{names}" for a numeric ' + f'descriptor (expected float)' + ) + elif isinstance(val, GenericStringDescriptor) and not set(ann_types).issubset( + _STRING_ALLOWED + ): + names = ' | '.join(t.__name__ for t in ann_types) + errors.append( + f'{loc}: setter annotated "{names}" for a string descriptor (expected str)' ) - else: - ann_str = ann.__name__ if isinstance(ann, type) else str(ann) - if ( - isinstance(val, GenericNumericDescriptor) - and ann_str.lower() not in _NUMERIC_SETTER_TYPES - ): - errors.append( - f'{loc}: setter annotated ' - f'"{ann_str}" for a numeric ' - f'descriptor (expected float)' - ) - elif ( - isinstance(val, GenericStringDescriptor) - and ann_str.lower() not in _STRING_SETTER_TYPES - ): - errors.append( - f'{loc}: setter annotated ' - f'"{ann_str}" for a string ' - f'descriptor (expected str)' - ) # --------------------------------------------------------------------------- @@ -239,33 +303,58 @@ def _check_class(cls, instance, errors: list[str]) -> None: def main() -> int: """Run all consistency checks and print results.""" # Import everything so all subclasses are registered - _import_all_submodules('easydiffraction') + import_failures = _import_all_submodules('easydiffraction') + if import_failures: + print(f'\n⚠️ {len(import_failures)} module(s) failed to import:') + for msg in import_failures: + print(f' {msg}') + print() errors: list[str] = [] checked = 0 + skipped: list[str] = [] + unexpected_skips: list[str] = [] - # Check CategoryItem subclasses - for cls in sorted(_concrete_subclasses(CategoryItem), key=lambda c: c.__name__): - instance = _safe_instantiate(cls) - if instance is None: - continue - _check_class(cls, instance, errors) - checked += 1 + all_targets: list[type] = [] + for base in (CategoryItem, CategoryCollection): + for cls in sorted(_concrete_subclasses(base), key=lambda c: c.__name__): + all_targets.append(cls) - # Also check CategoryCollection subclasses (some hold descriptors) - for cls in sorted(_concrete_subclasses(CategoryCollection), key=lambda c: c.__name__): - instance = _safe_instantiate(cls) - if instance is None: + for cls in all_targets: + try: + instance = cls() + except Exception as exc: # noqa: BLE001 + name = cls.__name__ + if name in _KNOWN_INTERMEDIATE_BASES: + skipped.append(f'{name} (expected: {exc})') + else: + unexpected_skips.append(f'{name}: {exc}') continue + _check_class(cls, instance, errors) checked += 1 + # Report skipped intermediate bases (informational) + if skipped: + print(f'ℹ️ {len(skipped)} known intermediate base(s) skipped:') + for msg in skipped: + print(f' {msg}') + print() + + # Unexpected skips are errors + if unexpected_skips: + print(f'❌ {len(unexpected_skips)} class(es) unexpectedly failed to instantiate:') + for msg in unexpected_skips: + print(f' {msg}') + print(' Add to _KNOWN_INTERMEDIATE_BASES if this is expected, or fix the constructor.\n') + # Summary - print(f'\nChecked {checked} classes') - if errors: - print(f'\n❌ {len(errors)} consistency issue(s) found:\n') - for i, err in enumerate(errors, 1): - print(f' {i}. {err}\n') + print(f'Checked {checked} classes') + if errors or unexpected_skips: + if errors: + print(f'\n❌ {len(errors)} consistency issue(s) found:\n') + for i, err in enumerate(errors, 1): + print(f' {i}. {err}\n') return 1 else: print('✅ All parameter-property consistency checks passed.') From 641050c588b1c2fe66613eb34dd78eda24999e98 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 01:46:36 +0100 Subject: [PATCH 13/48] Enhance property docstrings and type hints for clarity and consistency --- docs/architecture/architecture.md | 73 +++++++++ pixi.toml | 3 +- .../analysis/categories/aliases/default.py | 28 +++- .../categories/constraints/default.py | 28 +++- .../analysis/categories/fit_mode/fit_mode.py | 12 +- .../joint_fit_experiments/default.py | 28 +++- .../categories/background/chebyshev.py | 43 ++++- .../categories/background/line_segment.py | 47 +++++- .../experiment/categories/data/bragg_pd.py | 52 +++++- .../experiment/categories/data/bragg_sc.py | 54 +++++++ .../experiment/categories/data/total_pd.py | 30 ++++ .../categories/excluded_regions/default.py | 38 ++++- .../categories/experiment_type/default.py | 30 +++- .../experiment/categories/extinction/shelx.py | 28 +++- .../experiment/categories/instrument/cwl.py | 32 +++- .../experiment/categories/instrument/tof.py | 70 ++++++-- .../categories/linked_crystal/default.py | 24 ++- .../categories/linked_phases/default.py | 24 ++- .../experiment/categories/peak/cwl_mixins.py | 146 +++++++++++++++-- .../experiment/categories/peak/tof_mixins.py | 153 +++++++++++++++--- .../categories/peak/total_mixins.py | 88 ++++++++-- .../categories/atom_sites/default.py | 87 +++++----- .../structure/categories/cell/default.py | 48 +++--- .../categories/space_group/default.py | 19 +-- 24 files changed, 1004 insertions(+), 181 deletions(-) diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index 4e3a2bb3..2acbf456 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -1051,6 +1051,79 @@ npd 0.7 xrd 0.3 ``` +### 9.8 Property Docstring and Type-Hint Template + +Every public property backed by a private `Parameter`, +`NumericDescriptor`, or `StringDescriptor` attribute must follow the +template below. The `description` field on the descriptor is the +**single source of truth**; docstrings and type hints are mechanically +derived from it. + +**Definitions:** + +| Symbol | Meaning | +| ------------ | --------------------------------------------------------------------- | +| `{desc}` | `description` string without trailing period | +| `{units}` | `units` string; omit the `({units})` parenthetical when absent/empty | +| `{Type}` | Descriptor class name: `Parameter`, `NumericDescriptor`, or `StringDescriptor` | +| `{ann}` | Setter value annotation: `float` for numeric descriptors, `str` for string descriptors | + +**Template:** + +```python +# ── Private attribute (in __init__) ────────────────────────────── +self._length_a = Parameter( + name='length_a', + description='Length of the a axis of the unit cell.', + units='Å', + value_spec=AttributeSpec( + default=10.0, + validator=RangeValidator(ge=0, le=1000), + ), + cif_handler=CifHandler(names=['_cell.length_a']), +) + +# ── Getter ─────────────────────────────────────────────────────── +@property +def length_a(self) -> Parameter: + """Length of the a axis of the unit cell. + + Returns: + Parameter: Length of the a axis of the unit cell (Å). + """ + return self._length_a + +# ── Setter ─────────────────────────────────────────────────────── +@length_a.setter +def length_a(self, value: float) -> None: + """Set the length of the a axis of the unit cell. + + Args: + value: Length of the a axis of the unit cell (Å). + """ + self._length_a.value = value +``` + +**Quick-reference table:** + +| Location | Text | +| ------------------ | ------------------------------------------------------- | +| Getter 1st line | `"""{desc}.` | +| Getter `Returns:` | `{Type}: {desc} ({units}).` (or `{Type}: {desc}.`) | +| Setter 1st line | `"""Set the {desc, first letter lowercased}.` | +| Setter `Args:` | `value: {desc} ({units}).` (or `value: {desc}.`) | +| Getter annotation | `-> {Type}` | +| Setter annotation | `value: {ann}` and `-> None` | + +**Notes:** + +- Do **not** repeat the type in the docstring `Args:` line (e.g. avoid + `value (float):`). The type is already in the function signature. +- Avoid markdown emphasis (`*a*`) in docstrings; use plain text to stay + in sync with the `description` field. +- The CI tool `pixi run param-consistency-check` validates compliance; + `pixi run param-consistency-fix` auto-fixes violations. + --- ## 10. Issues diff --git a/pixi.toml b/pixi.toml index 1fed46a0..28f3532b 100644 --- a/pixi.toml +++ b/pixi.toml @@ -111,7 +111,7 @@ py-lint-check = 'ruff check src/ tests/ docs/docs/tutorials/' py-format-check = "ruff format --check src/ tests/ docs/docs/tutorials/" nonpy-format-check = "npx prettier --list-different --config=prettierrc.toml --ignore-unknown ." nonpy-format-check-modified = "python tools/nonpy_prettier_modified.py" -param-consistency-check = 'python tools/check_param_consistency.py' +param-consistency-check = 'python tools/param_consistency.py --check' check = 'pre-commit run --hook-stage manual --all-files' @@ -125,6 +125,7 @@ py-lint-fix = 'ruff check --fix src/ tests/ docs/docs/tutorials/' py-format-fix = "ruff format src/ tests/ docs/docs/tutorials/" nonpy-format-fix = 'npx prettier --write --list-different --config=prettierrc.toml --ignore-unknown .' nonpy-format-fix-modified = "python tools/nonpy_prettier_modified.py --write" +param-consistency-fix = 'python tools/param_consistency.py --fix' success-message-fix = 'echo "✅ All auto-formatting steps completed successfully!"' fix = { depends-on = [ diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 1b1cb77f..96589832 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -60,19 +60,39 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def label(self): + def label(self) -> StringDescriptor: + """... + + Returns: + StringDescriptor: ... + """ return self._label @label.setter - def label(self, value): + def label(self, value: str) -> None: + """Set the ... + + Args: + value: ... + """ self._label.value = value @property - def param_uid(self): + def param_uid(self) -> StringDescriptor: + """... + + Returns: + StringDescriptor: ... + """ return self._param_uid @param_uid.setter - def param_uid(self, value): + def param_uid(self, value: str) -> None: + """Set the ... + + Args: + value: ... + """ self._param_uid.value = value diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index b7ca9139..9de77d42 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -57,19 +57,39 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def lhs_alias(self): + def lhs_alias(self) -> StringDescriptor: + """Left-hand side of the equation. + + Returns: + StringDescriptor: Left-hand side of the equation. + """ return self._lhs_alias @lhs_alias.setter - def lhs_alias(self, value): + def lhs_alias(self, value: str) -> None: + """Set the left-hand side of the equation. + + Args: + value: Left-hand side of the equation. + """ self._lhs_alias.value = value @property - def rhs_expr(self): + def rhs_expr(self) -> StringDescriptor: + """Right-hand side expression. + + Returns: + StringDescriptor: Right-hand side expression. + """ return self._rhs_expr @rhs_expr.setter - def rhs_expr(self, value): + def rhs_expr(self, value: str) -> None: + """Set the right-hand side expression. + + Args: + value: Right-hand side expression. + """ self._rhs_expr.value = value diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index 7e473738..897cb1a9 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -47,15 +47,19 @@ def __init__(self) -> None: self._identity.category_code = 'fit_mode' @property - def mode(self): - """Active fitting strategy descriptor.""" + def mode(self) -> StringDescriptor: + """Fitting strategy. + + Returns: + StringDescriptor: Fitting strategy. + """ return self._mode @mode.setter def mode(self, value: str) -> None: - """Set the fitting strategy value. + """Set the fitting strategy. Args: - value: ``'single'`` or ``'joint'``. + value: Fitting strategy. """ self._mode.value = value diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index 6c5b1db5..f2086039 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -60,19 +60,39 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def id(self): + def id(self) -> StringDescriptor: + """Experiment identifier. + + Returns: + StringDescriptor: Experiment identifier. + """ return self._id @id.setter - def id(self, value): + def id(self, value: str) -> None: + """Set the experiment identifier. + + Args: + value: Experiment identifier. + """ self._id.value = value @property - def weight(self): + def weight(self) -> NumericDescriptor: + """Weight factor. + + Returns: + NumericDescriptor: Weight factor. + """ return self._weight @weight.setter - def weight(self, value): + def weight(self, value: float) -> None: + """Set the weight factor. + + Args: + value: Weight factor. + """ self._weight.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 6173eba5..c09b2d1b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -84,27 +84,58 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def id(self): + def id(self) -> StringDescriptor: + """Identifier for this background polynomial term. + + Returns: + StringDescriptor: Identifier for this background polynomial term. + """ return self._id @id.setter - def id(self, value): + def id(self, value: str) -> None: + """Set the identifier for this background polynomial term. + + Args: + value: Identifier for this background polynomial term. + """ self._id.value = value @property - def order(self): + def order(self) -> NumericDescriptor: + """Order used in a Chebyshev polynomial background term. + + Returns: + NumericDescriptor: Order used in a Chebyshev polynomial background term. + """ return self._order @order.setter - def order(self, value): + def order(self, value: float) -> None: + """Set the order used in a Chebyshev polynomial background term. + + Args: + value: Order used in a Chebyshev polynomial background term. + """ self._order.value = value @property - def coef(self): + def coef(self) -> Parameter: + """Coefficient used in a Chebyshev polynomial background term. + + Returns: + Parameter: Coefficient used in a Chebyshev polynomial background term. + """ return self._coef @coef.setter - def coef(self, value): + def coef(self, value: float) -> None: + """Set the coefficient used in a Chebyshev polynomial background + term. + + Args: + value: Coefficient used in a Chebyshev polynomial background term. + """ self._coef.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index 23113b26..0237c14b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -93,27 +93,62 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def id(self): + def id(self) -> StringDescriptor: + """Identifier for this background line segment. + + Returns: + StringDescriptor: Identifier for this background line segment. + """ return self._id @id.setter - def id(self, value): + def id(self, value: str) -> None: + """Set the identifier for this background line segment. + + Args: + value: Identifier for this background line segment. + """ self._id.value = value @property - def x(self): + def x(self) -> NumericDescriptor: + """X-coordinates used to create many straight-line segments + representing the background in a calculated diffractogram. + + Returns: + NumericDescriptor: X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. + """ return self._x @x.setter - def x(self, value): + def x(self, value: float) -> None: + """Set the x-coordinates used to create many straight-line + segments representing the background in a calculated + diffractogram. + + Args: + value: X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. + """ self._x.value = value @property - def y(self): + def y(self) -> Parameter: + """Intensity used to create many straight-line segments + representing the background in a calculated diffractogram. + + Returns: + Parameter: Intensity used to create many straight-line segments representing the background in a calculated diffractogram. + """ return self._y @y.setter - def y(self, value): + def y(self, value: float) -> None: + """Set the intensity used to create many straight-line segments + representing the background in a calculated diffractogram. + + Args: + value: Intensity used to create many straight-line segments representing the background in a calculated diffractogram. + """ self._y.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 8ecf2bb9..59c2ed72 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -123,30 +123,68 @@ def __init__(self): @property def point_id(self) -> StringDescriptor: + """Identifier for this data point in the dataset. + + Returns: + StringDescriptor: Identifier for this data point in the dataset. + """ return self._point_id @property def d_spacing(self) -> NumericDescriptor: + """D-spacing value corresponding to this data point. + + Returns: + NumericDescriptor: d-spacing value corresponding to this data point. + """ return self._d_spacing @property def intensity_meas(self) -> NumericDescriptor: + """Intensity recorded at each measurement point as a function of + angle/time. + + Returns: + NumericDescriptor: Intensity recorded at each measurement point as a function of angle/time. + """ return self._intensity_meas @property def intensity_meas_su(self) -> NumericDescriptor: + """Standard uncertainty of the measured intensity at this data + point. + + Returns: + NumericDescriptor: Standard uncertainty of the measured intensity at this data point. + """ return self._intensity_meas_su @property def intensity_calc(self) -> NumericDescriptor: + """Intensity value for a computed diffractogram at this data + point. + + Returns: + NumericDescriptor: Intensity value for a computed diffractogram at this data point. + """ return self._intensity_calc @property def intensity_bkg(self) -> NumericDescriptor: + """Intensity value for a computed background at this data point. + + Returns: + NumericDescriptor: Intensity value for a computed background at this data point. + """ return self._intensity_bkg @property def calc_status(self) -> StringDescriptor: + """Status code of the data point in the calculation process. + + Returns: + StringDescriptor: Status code of the data point in the calculation process. + """ return self._calc_status @@ -179,7 +217,12 @@ def __init__(self): # ------------------------------------------------------------------ @property - def two_theta(self): + def two_theta(self) -> NumericDescriptor: + """Measured 2θ diffraction angle. + + Returns: + NumericDescriptor: Measured 2θ diffraction angle (deg). + """ return self._two_theta @@ -205,7 +248,12 @@ def __init__(self): # ------------------------------------------------------------------ @property - def time_of_flight(self): + def time_of_flight(self) -> NumericDescriptor: + """Measured time for time-of-flight neutron measurement. + + Returns: + NumericDescriptor: Measured time for time-of-flight neutron measurement (µs). + """ return self._time_of_flight diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 497c13f5..3b074e45 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -139,42 +139,96 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: + """Identifier of the reflection. + + Returns: + StringDescriptor: Identifier of the reflection. + """ return self._id @property def d_spacing(self) -> NumericDescriptor: + """The distance between lattice planes in the crystal for this + reflection. + + Returns: + NumericDescriptor: The distance between lattice planes in the crystal for this reflection (Å). + """ return self._d_spacing @property def sin_theta_over_lambda(self) -> NumericDescriptor: + """The sin(θ)/λ value for this reflection. + + Returns: + NumericDescriptor: The sin(θ)/λ value for this reflection (Å⁻¹). + """ return self._sin_theta_over_lambda @property def index_h(self) -> NumericDescriptor: + """Miller index h of a measured reflection. + + Returns: + NumericDescriptor: Miller index h of a measured reflection. + """ return self._index_h @property def index_k(self) -> NumericDescriptor: + """Miller index k of a measured reflection. + + Returns: + NumericDescriptor: Miller index k of a measured reflection. + """ return self._index_k @property def index_l(self) -> NumericDescriptor: + """Miller index l of a measured reflection. + + Returns: + NumericDescriptor: Miller index l of a measured reflection. + """ return self._index_l @property def intensity_meas(self) -> NumericDescriptor: + """The intensity of the reflection derived from the + measurements. + + Returns: + NumericDescriptor: The intensity of the reflection derived from the measurements. + """ return self._intensity_meas @property def intensity_meas_su(self) -> NumericDescriptor: + """Standard uncertainty of the measured intensity. + + Returns: + NumericDescriptor: Standard uncertainty of the measured intensity. + """ return self._intensity_meas_su @property def intensity_calc(self) -> NumericDescriptor: + """The intensity of the reflection calculated from the atom site + data. + + Returns: + NumericDescriptor: The intensity of the reflection calculated from the atom site data. + """ return self._intensity_calc @property def wavelength(self) -> NumericDescriptor: + """The mean wavelength of radiation used to measure this + reflection. + + Returns: + NumericDescriptor: The mean wavelength of radiation used to measure this reflection (Å). + """ return self._wavelength diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index e819bea7..09d6cb20 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -122,26 +122,56 @@ def __init__(self) -> None: @property def point_id(self) -> StringDescriptor: + """Identifier for this data point in the dataset. + + Returns: + StringDescriptor: Identifier for this data point in the dataset. + """ return self._point_id @property def r(self) -> NumericDescriptor: + """Interatomic distance in real space. + + Returns: + NumericDescriptor: Interatomic distance in real space (Å). + """ return self._r @property def g_r_meas(self) -> NumericDescriptor: + """Measured pair distribution function G(r). + + Returns: + NumericDescriptor: Measured pair distribution function G(r). + """ return self._g_r_meas @property def g_r_meas_su(self) -> NumericDescriptor: + """Standard uncertainty of measured G(r). + + Returns: + NumericDescriptor: Standard uncertainty of measured G(r). + """ return self._g_r_meas_su @property def g_r_calc(self) -> NumericDescriptor: + """Calculated pair distribution function G(r). + + Returns: + NumericDescriptor: Calculated pair distribution function G(r). + """ return self._g_r_calc @property def calc_status(self) -> StringDescriptor: + """Status code of the data point in calculation. + + Returns: + StringDescriptor: Status code of the data point in calculation. + """ return self._calc_status diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 0a45a8dd..800e0edc 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -71,27 +71,57 @@ def __init__(self): # ------------------------------------------------------------------ @property - def id(self): + def id(self) -> StringDescriptor: + """Identifier for this excluded region. + + Returns: + StringDescriptor: Identifier for this excluded region. + """ return self._id @id.setter - def id(self, value): + def id(self, value: str) -> None: + """Set the identifier for this excluded region. + + Args: + value: Identifier for this excluded region. + """ self._id.value = value @property def start(self) -> NumericDescriptor: + """Start of the excluded region. + + Returns: + NumericDescriptor: Start of the excluded region. + """ return self._start @start.setter - def start(self, value: float): + def start(self, value: float) -> None: + """Set the start of the excluded region. + + Args: + value: Start of the excluded region. + """ self._start.value = value @property def end(self) -> NumericDescriptor: + """End of the excluded region. + + Returns: + NumericDescriptor: End of the excluded region. + """ return self._end @end.setter - def end(self, value: float): + def end(self, value: float) -> None: + """Set the end of the excluded region. + + Args: + value: End of the excluded region. + """ self._end.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 88693e14..b9a2ceb2 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -112,17 +112,39 @@ def _set_scattering_type(self, value: str) -> None: # ------------------------------------------------------------------ @property - def sample_form(self): + def sample_form(self) -> StringDescriptor: + """Specifies whether the diffraction data corresponds to powder + diffraction or single crystal diffraction. + + Returns: + StringDescriptor: Specifies whether the diffraction data corresponds to powder diffraction or single crystal diffraction. + """ return self._sample_form @property - def beam_mode(self): + def beam_mode(self) -> StringDescriptor: + """Defines whether the measurement is performed with a constant + wavelength (CW) or time-of-flight (TOF) method. + + Returns: + StringDescriptor: Defines whether the measurement is performed with a constant wavelength (CW) or time-of-flight (TOF) method. + """ return self._beam_mode @property - def radiation_probe(self): + def radiation_probe(self) -> StringDescriptor: + """Specifies whether the measurement uses neutrons or X-rays. + + Returns: + StringDescriptor: Specifies whether the measurement uses neutrons or X-rays. + """ return self._radiation_probe @property - def scattering_type(self): + def scattering_type(self) -> StringDescriptor: + """Specifies whether the experiment uses Bragg scattering (for conventional structure refinement) or total scattering (for pair distribution function analysis - PDF). + + Returns: + StringDescriptor: Specifies whether the experiment uses Bragg scattering (for conventional structure refinement) or total scattering (for pair distribution function analysis - PDF). + """ return self._scattering_type diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index b4a81a80..e563a580 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -68,17 +68,37 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def mosaicity(self): + def mosaicity(self) -> Parameter: + """Mosaicity value for extinction correction. + + Returns: + Parameter: Mosaicity value for extinction correction (deg). + """ return self._mosaicity @mosaicity.setter - def mosaicity(self, value): + def mosaicity(self, value: float) -> None: + """Set the mosaicity value for extinction correction. + + Args: + value: Mosaicity value for extinction correction (deg). + """ self._mosaicity.value = value @property - def radius(self): + def radius(self) -> Parameter: + """Crystal radius for extinction correction. + + Returns: + Parameter: Crystal radius for extinction correction (µm). + """ return self._radius @radius.setter - def radius(self, value): + def radius(self, value: float) -> None: + """Set the crystal radius for extinction correction. + + Args: + value: Crystal radius for extinction correction (µm). + """ self._radius.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 0c78f435..41aaa119 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -36,13 +36,21 @@ def __init__(self) -> None: ) @property - def setup_wavelength(self): - """Incident wavelength parameter (Å).""" + def setup_wavelength(self) -> Parameter: + """Incident neutron or X-ray wavelength. + + Returns: + Parameter: Incident neutron or X-ray wavelength (Å). + """ return self._setup_wavelength @setup_wavelength.setter - def setup_wavelength(self, value): - """Set incident wavelength value (Å).""" + def setup_wavelength(self, value: float) -> None: + """Set the incident neutron or X-ray wavelength. + + Args: + value: Incident neutron or X-ray wavelength (Å). + """ self._setup_wavelength.value = value @@ -97,11 +105,19 @@ def __init__(self) -> None: ) @property - def calib_twotheta_offset(self): - """Instrument misalignment two-theta offset (deg).""" + def calib_twotheta_offset(self) -> Parameter: + """Instrument misalignment offset. + + Returns: + Parameter: Instrument misalignment offset (deg). + """ return self._calib_twotheta_offset @calib_twotheta_offset.setter - def calib_twotheta_offset(self, value): - """Set two-theta offset value (deg).""" + def calib_twotheta_offset(self, value: float) -> None: + """Set the instrument misalignment offset. + + Args: + value: Instrument misalignment offset (deg). + """ self._calib_twotheta_offset.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index 33fccab3..7016981d 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -99,41 +99,91 @@ def __init__(self) -> None: ) @property - def setup_twotheta_bank(self): + def setup_twotheta_bank(self) -> Parameter: + """Detector bank position. + + Returns: + Parameter: Detector bank position (deg). + """ return self._setup_twotheta_bank @setup_twotheta_bank.setter - def setup_twotheta_bank(self, value): + def setup_twotheta_bank(self, value: float) -> None: + """Set the detector bank position. + + Args: + value: Detector bank position (deg). + """ self._setup_twotheta_bank.value = value @property - def calib_d_to_tof_offset(self): + def calib_d_to_tof_offset(self) -> Parameter: + """TOF offset. + + Returns: + Parameter: TOF offset (µs). + """ return self._calib_d_to_tof_offset @calib_d_to_tof_offset.setter - def calib_d_to_tof_offset(self, value): + def calib_d_to_tof_offset(self, value: float) -> None: + """Set the tOF offset. + + Args: + value: TOF offset (µs). + """ self._calib_d_to_tof_offset.value = value @property - def calib_d_to_tof_linear(self): + def calib_d_to_tof_linear(self) -> Parameter: + """TOF linear conversion. + + Returns: + Parameter: TOF linear conversion (µs/Å). + """ return self._calib_d_to_tof_linear @calib_d_to_tof_linear.setter - def calib_d_to_tof_linear(self, value): + def calib_d_to_tof_linear(self, value: float) -> None: + """Set the tOF linear conversion. + + Args: + value: TOF linear conversion (µs/Å). + """ self._calib_d_to_tof_linear.value = value @property - def calib_d_to_tof_quad(self): + def calib_d_to_tof_quad(self) -> Parameter: + """TOF quadratic correction. + + Returns: + Parameter: TOF quadratic correction (µs/Ų). + """ return self._calib_d_to_tof_quad @calib_d_to_tof_quad.setter - def calib_d_to_tof_quad(self, value): + def calib_d_to_tof_quad(self, value: float) -> None: + """Set the tOF quadratic correction. + + Args: + value: TOF quadratic correction (µs/Ų). + """ self._calib_d_to_tof_quad.value = value @property - def calib_d_to_tof_recip(self): + def calib_d_to_tof_recip(self) -> Parameter: + """TOF reciprocal velocity correction. + + Returns: + Parameter: TOF reciprocal velocity correction (µs·Å). + """ return self._calib_d_to_tof_recip @calib_d_to_tof_recip.setter - def calib_d_to_tof_recip(self, value): + def calib_d_to_tof_recip(self, value: float) -> None: + """Set the tOF reciprocal velocity correction. + + Args: + value: TOF reciprocal velocity correction (µs·Å). + """ self._calib_d_to_tof_recip.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index 763b6481..fa3888f6 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -63,16 +63,36 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: + """Identifier of the linked crystal. + + Returns: + StringDescriptor: Identifier of the linked crystal. + """ return self._id @id.setter - def id(self, value: str): + def id(self, value: str) -> None: + """Set the identifier of the linked crystal. + + Args: + value: Identifier of the linked crystal. + """ self._id.value = value @property def scale(self) -> Parameter: + """Scale factor of the linked crystal. + + Returns: + Parameter: Scale factor of the linked crystal. + """ return self._scale @scale.setter - def scale(self, value: float): + def scale(self, value: float) -> None: + """Set the scale factor of the linked crystal. + + Args: + value: Scale factor of the linked crystal. + """ self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index c95638d1..e231dd12 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -54,18 +54,38 @@ def __init__(self): @property def id(self) -> StringDescriptor: + """Identifier of the linked phase. + + Returns: + StringDescriptor: Identifier of the linked phase. + """ return self._id @id.setter - def id(self, value: str): + def id(self, value: str) -> None: + """Set the identifier of the linked phase. + + Args: + value: Identifier of the linked phase. + """ self._id.value = value @property def scale(self) -> Parameter: + """Scale factor of the linked phase. + + Returns: + Parameter: Scale factor of the linked phase. + """ return self._scale @scale.setter - def scale(self, value: float): + def scale(self, value: float) -> None: + """Set the scale factor of the linked phase. + + Args: + value: Scale factor of the linked phase. + """ self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 29f629b7..57fb00c3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -78,42 +78,102 @@ def __init__(self): @property def broad_gauss_u(self) -> Parameter: + """Gaussian broadening coefficient (dependent on sample size and + instrument resolution). + + Returns: + Parameter: Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). + """ return self._broad_gauss_u @broad_gauss_u.setter - def broad_gauss_u(self, value): + def broad_gauss_u(self, value: float) -> None: + """Set the gaussian broadening coefficient (dependent on sample + size and instrument resolution). + + Args: + value: Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). + """ self._broad_gauss_u.value = value @property def broad_gauss_v(self) -> Parameter: + """Gaussian broadening coefficient (instrumental broadening + contribution). + + Returns: + Parameter: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + """ return self._broad_gauss_v @broad_gauss_v.setter - def broad_gauss_v(self, value): + def broad_gauss_v(self, value: float) -> None: + """Set the gaussian broadening coefficient (instrumental + broadening contribution). + + Args: + value: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + """ self._broad_gauss_v.value = value @property def broad_gauss_w(self) -> Parameter: + """Gaussian broadening coefficient (instrumental broadening + contribution). + + Returns: + Parameter: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + """ return self._broad_gauss_w @broad_gauss_w.setter - def broad_gauss_w(self, value): + def broad_gauss_w(self, value: float) -> None: + """Set the gaussian broadening coefficient (instrumental + broadening contribution). + + Args: + value: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + """ self._broad_gauss_w.value = value @property def broad_lorentz_x(self) -> Parameter: + """Lorentzian broadening coefficient (dependent on sample strain + effects). + + Returns: + Parameter: Lorentzian broadening coefficient (dependent on sample strain effects) (deg). + """ return self._broad_lorentz_x @broad_lorentz_x.setter - def broad_lorentz_x(self, value): + def broad_lorentz_x(self, value: float) -> None: + """Set the lorentzian broadening coefficient (dependent on + sample strain effects). + + Args: + value: Lorentzian broadening coefficient (dependent on sample strain effects) (deg). + """ self._broad_lorentz_x.value = value @property def broad_lorentz_y(self) -> Parameter: + """Lorentzian broadening coefficient (dependent on + microstructural defects and strain). + + Returns: + Parameter: Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). + """ return self._broad_lorentz_y @broad_lorentz_y.setter - def broad_lorentz_y(self, value): + def broad_lorentz_y(self, value: float) -> None: + """Set the lorentzian broadening coefficient (dependent on + microstructural defects and strain). + + Args: + value: Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). + """ self._broad_lorentz_y.value = value @@ -170,34 +230,74 @@ def __init__(self): @property def asym_empir_1(self) -> Parameter: + """Empirical asymmetry coefficient p1. + + Returns: + Parameter: Empirical asymmetry coefficient p1. + """ return self._asym_empir_1 @asym_empir_1.setter - def asym_empir_1(self, value): + def asym_empir_1(self, value: float) -> None: + """Set the empirical asymmetry coefficient p1. + + Args: + value: Empirical asymmetry coefficient p1. + """ self._asym_empir_1.value = value @property def asym_empir_2(self) -> Parameter: + """Empirical asymmetry coefficient p2. + + Returns: + Parameter: Empirical asymmetry coefficient p2. + """ return self._asym_empir_2 @asym_empir_2.setter - def asym_empir_2(self, value): + def asym_empir_2(self, value: float) -> None: + """Set the empirical asymmetry coefficient p2. + + Args: + value: Empirical asymmetry coefficient p2. + """ self._asym_empir_2.value = value @property def asym_empir_3(self) -> Parameter: + """Empirical asymmetry coefficient p3. + + Returns: + Parameter: Empirical asymmetry coefficient p3. + """ return self._asym_empir_3 @asym_empir_3.setter - def asym_empir_3(self, value): + def asym_empir_3(self, value: float) -> None: + """Set the empirical asymmetry coefficient p3. + + Args: + value: Empirical asymmetry coefficient p3. + """ self._asym_empir_3.value = value @property def asym_empir_4(self) -> Parameter: + """Empirical asymmetry coefficient p4. + + Returns: + Parameter: Empirical asymmetry coefficient p4. + """ return self._asym_empir_4 @asym_empir_4.setter - def asym_empir_4(self, value): + def asym_empir_4(self, value: float) -> None: + """Set the empirical asymmetry coefficient p4. + + Args: + value: Empirical asymmetry coefficient p4. + """ self._asym_empir_4.value = value @@ -233,17 +333,37 @@ def __init__(self): # ------------------------------------------------------------------ @property - def asym_fcj_1(self): + def asym_fcj_1(self) -> Parameter: + """Finger-Cox-Jephcoat asymmetry parameter 1. + + Returns: + Parameter: Finger-Cox-Jephcoat asymmetry parameter 1. + """ return self._asym_fcj_1 @asym_fcj_1.setter - def asym_fcj_1(self, value): + def asym_fcj_1(self, value: float) -> None: + """Set the finger-Cox-Jephcoat asymmetry parameter 1. + + Args: + value: Finger-Cox-Jephcoat asymmetry parameter 1. + """ self._asym_fcj_1.value = value @property - def asym_fcj_2(self): + def asym_fcj_2(self) -> Parameter: + """Finger-Cox-Jephcoat asymmetry parameter 2. + + Returns: + Parameter: Finger-Cox-Jephcoat asymmetry parameter 2. + """ return self._asym_fcj_2 @asym_fcj_2.setter - def asym_fcj_2(self, value): + def asym_fcj_2(self, value: float) -> None: + """Set the finger-Cox-Jephcoat asymmetry parameter 2. + + Args: + value: Finger-Cox-Jephcoat asymmetry parameter 2. + """ self._asym_fcj_2.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index be942a97..be169ed4 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -109,68 +109,159 @@ def __init__(self): # ------------------------------------------------------------------ @property - def broad_gauss_sigma_0(self): + def broad_gauss_sigma_0(self) -> Parameter: + """Gaussian broadening coefficient (instrumental resolution). + + Returns: + Parameter: Gaussian broadening coefficient (instrumental resolution) (µs²). + """ return self._broad_gauss_sigma_0 @broad_gauss_sigma_0.setter - def broad_gauss_sigma_0(self, value): + def broad_gauss_sigma_0(self, value: float) -> None: + """Set the gaussian broadening coefficient (instrumental + resolution). + + Args: + value: Gaussian broadening coefficient (instrumental resolution) (µs²). + """ self._broad_gauss_sigma_0.value = value @property - def broad_gauss_sigma_1(self): + def broad_gauss_sigma_1(self) -> Parameter: + """Gaussian broadening coefficient (dependent on d-spacing). + + Returns: + Parameter: Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). + """ return self._broad_gauss_sigma_1 @broad_gauss_sigma_1.setter - def broad_gauss_sigma_1(self, value): + def broad_gauss_sigma_1(self, value: float) -> None: + """Set the gaussian broadening coefficient (dependent on + d-spacing). + + Args: + value: Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). + """ self._broad_gauss_sigma_1.value = value @property - def broad_gauss_sigma_2(self): + def broad_gauss_sigma_2(self) -> Parameter: + """Gaussian broadening coefficient (instrument-dependent term). + + Returns: + Parameter: Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). + """ return self._broad_gauss_sigma_2 @broad_gauss_sigma_2.setter - def broad_gauss_sigma_2(self, value): - """Set Gaussian sigma_2 parameter.""" + def broad_gauss_sigma_2(self, value: float) -> None: + """Set the gaussian broadening coefficient (instrument-dependent + term). + + Args: + value: Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). + """ self._broad_gauss_sigma_2.value = value @property - def broad_lorentz_gamma_0(self): + def broad_lorentz_gamma_0(self) -> Parameter: + """Lorentzian broadening coefficient (dependent on microstrain + effects). + + Returns: + Parameter: Lorentzian broadening coefficient (dependent on microstrain effects) (µs). + """ return self._broad_lorentz_gamma_0 @broad_lorentz_gamma_0.setter - def broad_lorentz_gamma_0(self, value): + def broad_lorentz_gamma_0(self, value: float) -> None: + """Set the lorentzian broadening coefficient (dependent on + microstrain effects). + + Args: + value: Lorentzian broadening coefficient (dependent on microstrain effects) (µs). + """ self._broad_lorentz_gamma_0.value = value @property - def broad_lorentz_gamma_1(self): + def broad_lorentz_gamma_1(self) -> Parameter: + """Lorentzian broadening coefficient (dependent on d-spacing). + + Returns: + Parameter: Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). + """ return self._broad_lorentz_gamma_1 @broad_lorentz_gamma_1.setter - def broad_lorentz_gamma_1(self, value): + def broad_lorentz_gamma_1(self, value: float) -> None: + """Set the lorentzian broadening coefficient (dependent on + d-spacing). + + Args: + value: Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). + """ self._broad_lorentz_gamma_1.value = value @property - def broad_lorentz_gamma_2(self): + def broad_lorentz_gamma_2(self) -> Parameter: + """Lorentzian broadening coefficient (instrument-dependent + term). + + Returns: + Parameter: Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). + """ return self._broad_lorentz_gamma_2 @broad_lorentz_gamma_2.setter - def broad_lorentz_gamma_2(self, value): + def broad_lorentz_gamma_2(self, value: float) -> None: + """Set the lorentzian broadening coefficient (instrument- + dependent term). + + Args: + value: Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). + """ self._broad_lorentz_gamma_2.value = value @property - def broad_mix_beta_0(self): + def broad_mix_beta_0(self) -> Parameter: + """Mixing parameter. Defines the ratio of Gaussian to Lorentzian + contributions in TOF profiles. + + Returns: + Parameter: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + """ return self._broad_mix_beta_0 @broad_mix_beta_0.setter - def broad_mix_beta_0(self, value): + def broad_mix_beta_0(self, value: float) -> None: + """Set the mixing parameter. Defines the ratio of Gaussian to + Lorentzian contributions in TOF profiles. + + Args: + value: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + """ self._broad_mix_beta_0.value = value @property - def broad_mix_beta_1(self): + def broad_mix_beta_1(self) -> Parameter: + """Mixing parameter. Defines the ratio of Gaussian to Lorentzian + contributions in TOF profiles. + + Returns: + Parameter: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + """ return self._broad_mix_beta_1 @broad_mix_beta_1.setter - def broad_mix_beta_1(self, value): + def broad_mix_beta_1(self, value: float) -> None: + """Set the mixing parameter. Defines the ratio of Gaussian to + Lorentzian contributions in TOF profiles. + + Args: + value: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + """ self._broad_mix_beta_1.value = value @@ -202,17 +293,37 @@ def __init__(self): ) @property - def asym_alpha_0(self): + def asym_alpha_0(self) -> Parameter: + """Ikeda-Carpenter asymmetry parameter α₀. + + Returns: + Parameter: Ikeda-Carpenter asymmetry parameter α₀. + """ return self._asym_alpha_0 @asym_alpha_0.setter - def asym_alpha_0(self, value): + def asym_alpha_0(self, value: float) -> None: + """Set the ikeda-Carpenter asymmetry parameter α₀. + + Args: + value: Ikeda-Carpenter asymmetry parameter α₀. + """ self._asym_alpha_0.value = value @property - def asym_alpha_1(self): + def asym_alpha_1(self) -> Parameter: + """Ikeda-Carpenter asymmetry parameter α₁. + + Returns: + Parameter: Ikeda-Carpenter asymmetry parameter α₁. + """ return self._asym_alpha_1 @asym_alpha_1.setter - def asym_alpha_1(self, value): + def asym_alpha_1(self, value: float) -> None: + """Set the ikeda-Carpenter asymmetry parameter α₁. + + Args: + value: Ikeda-Carpenter asymmetry parameter α₁. + """ self._asym_alpha_1.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 2101ec5e..287bb4c5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -89,49 +89,117 @@ def __init__(self): # ------------------------------------------------------------------ @property - def damp_q(self): + def damp_q(self) -> Parameter: + """Instrumental Q-resolution damping factor (affects high-r PDF + peak amplitude). + + Returns: + Parameter: Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). + """ return self._damp_q @damp_q.setter - def damp_q(self, value): + def damp_q(self, value: float) -> None: + """Set the instrumental Q-resolution damping factor (affects + high-r PDF peak amplitude). + + Args: + value: Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). + """ self._damp_q.value = value @property - def broad_q(self): + def broad_q(self) -> Parameter: + """Quadratic PDF peak broadening coefficient (thermal and model + uncertainty contribution). + + Returns: + Parameter: Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). + """ return self._broad_q @broad_q.setter - def broad_q(self, value): + def broad_q(self, value: float) -> None: + """Set the quadratic PDF peak broadening coefficient (thermal + and model uncertainty contribution). + + Args: + value: Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). + """ self._broad_q.value = value @property def cutoff_q(self) -> Parameter: + """Q-value cutoff applied to model PDF for Fourier transform + (controls real-space resolution). + + Returns: + Parameter: Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). + """ return self._cutoff_q @cutoff_q.setter - def cutoff_q(self, value): + def cutoff_q(self, value: float) -> None: + """Set the q-value cutoff applied to model PDF for Fourier + transform (controls real-space resolution). + + Args: + value: Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). + """ self._cutoff_q.value = value @property def sharp_delta_1(self) -> Parameter: + """PDF peak sharpening coefficient (1/r dependence). + + Returns: + Parameter: PDF peak sharpening coefficient (1/r dependence) (Å). + """ return self._sharp_delta_1 @sharp_delta_1.setter - def sharp_delta_1(self, value): + def sharp_delta_1(self, value: float) -> None: + """Set the pDF peak sharpening coefficient (1/r dependence). + + Args: + value: PDF peak sharpening coefficient (1/r dependence) (Å). + """ self._sharp_delta_1.value = value @property - def sharp_delta_2(self): + def sharp_delta_2(self) -> Parameter: + """PDF peak sharpening coefficient (1/r² dependence). + + Returns: + Parameter: PDF peak sharpening coefficient (1/r² dependence) (Ų). + """ return self._sharp_delta_2 @sharp_delta_2.setter - def sharp_delta_2(self, value): + def sharp_delta_2(self, value: float) -> None: + """Set the pDF peak sharpening coefficient (1/r² dependence). + + Args: + value: PDF peak sharpening coefficient (1/r² dependence) (Ų). + """ self._sharp_delta_2.value = value @property - def damp_particle_diameter(self): + def damp_particle_diameter(self) -> Parameter: + """Particle diameter for spherical envelope damping correction + in PDF. + + Returns: + Parameter: Particle diameter for spherical envelope damping correction in PDF (Å). + """ return self._damp_particle_diameter @damp_particle_diameter.setter - def damp_particle_diameter(self, value): + def damp_particle_diameter(self, value: float) -> None: + """Set the particle diameter for spherical envelope damping + correction in PDF. + + Args: + value: Particle diameter for spherical envelope damping correction in PDF (Å). + """ self._damp_particle_diameter.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index fe688f15..428318d9 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -174,164 +174,177 @@ def _wyckoff_letter_default_value(self) -> str: @property def label(self) -> StringDescriptor: - """Unique label for this atom site. + """Unique identifier for the atom site. Returns: - StringDescriptor: Descriptor holding the site label. + StringDescriptor: Unique identifier for the atom site. """ return self._label @label.setter def label(self, value: str) -> None: - """Set the atom-site label. + """Set the unique identifier for the atom site. Args: - value (str): New label string. + value: Unique identifier for the atom site. """ self._label.value = value @property def type_symbol(self) -> StringDescriptor: - """Chemical element or isotope symbol. + """Chemical symbol of the atom at this site. Returns: - StringDescriptor: Descriptor holding the type symbol. + StringDescriptor: Chemical symbol of the atom at this site. """ return self._type_symbol @type_symbol.setter def type_symbol(self, value: str) -> None: - """Set the chemical element or isotope symbol. + """Set the chemical symbol of the atom at this site. Args: - value (str): New type symbol (must be in the *cryspy* - database). + value: Chemical symbol of the atom at this site. """ self._type_symbol.value = value @property def adp_type(self) -> StringDescriptor: - """Type of atomic displacement parameter (e.g. ``'Biso'``). + """Type of atomic displacement parameter (ADP) used (e.g., Biso, + Uiso, Uani, Bani). Returns: - StringDescriptor: Descriptor holding the ADP type. + StringDescriptor: Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). """ return self._adp_type @adp_type.setter def adp_type(self, value: str) -> None: - """Set the ADP type. + """Set the type of atomic displacement parameter (ADP) used + (e.g., Biso, Uiso, Uani, Bani). Args: - value (str): New ADP type string. + value: Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). """ self._adp_type.value = value @property def wyckoff_letter(self) -> StringDescriptor: - """Wyckoff letter for the symmetry site. + """Wyckoff letter indicating the symmetry of the atom site + within the space group. Returns: - StringDescriptor: Descriptor holding the Wyckoff letter. + StringDescriptor: Wyckoff letter indicating the symmetry of the atom site within the space group. """ return self._wyckoff_letter @wyckoff_letter.setter def wyckoff_letter(self, value: str) -> None: - """Set the Wyckoff letter. + """Set the wyckoff letter indicating the symmetry of the atom + site within the space group. Args: - value (str): New Wyckoff letter. + value: Wyckoff letter indicating the symmetry of the atom site within the space group. """ self._wyckoff_letter.value = value @property def fract_x(self) -> Parameter: - """Fractional *x*-coordinate within the unit cell. + """Fractional x-coordinate of the atom site within the unit + cell. Returns: - Parameter: Descriptor for the *x* coordinate. + Parameter: Fractional x-coordinate of the atom site within the unit cell. """ return self._fract_x @fract_x.setter def fract_x(self, value: float) -> None: - """Set the fractional *x*-coordinate. + """Set the fractional x-coordinate of the atom site within the + unit cell. Args: - value (float): New *x* coordinate. + value: Fractional x-coordinate of the atom site within the unit cell. """ self._fract_x.value = value @property def fract_y(self) -> Parameter: - """Fractional *y*-coordinate within the unit cell. + """Fractional y-coordinate of the atom site within the unit + cell. Returns: - Parameter: Descriptor for the *y* coordinate. + Parameter: Fractional y-coordinate of the atom site within the unit cell. """ return self._fract_y @fract_y.setter def fract_y(self, value: float) -> None: - """Set the fractional *y*-coordinate. + """Set the fractional y-coordinate of the atom site within the + unit cell. Args: - value (float): New *y* coordinate. + value: Fractional y-coordinate of the atom site within the unit cell. """ self._fract_y.value = value @property def fract_z(self) -> Parameter: - """Fractional *z*-coordinate within the unit cell. + """Fractional z-coordinate of the atom site within the unit + cell. Returns: - Parameter: Descriptor for the *z* coordinate. + Parameter: Fractional z-coordinate of the atom site within the unit cell. """ return self._fract_z @fract_z.setter def fract_z(self, value: float) -> None: - """Set the fractional *z*-coordinate. + """Set the fractional z-coordinate of the atom site within the + unit cell. Args: - value (float): New *z* coordinate. + value: Fractional z-coordinate of the atom site within the unit cell. """ self._fract_z.value = value @property def occupancy(self) -> Parameter: - """Site occupancy fraction. + """Occupancy of the atom site, representing the fraction of the + site occupied by the atom type. Returns: - Parameter: Descriptor for the occupancy (0–1). + Parameter: Occupancy of the atom site, representing the fraction of the site occupied by the atom type. """ return self._occupancy @occupancy.setter def occupancy(self, value: float) -> None: - """Set the site occupancy. + """Set the occupancy of the atom site, representing the fraction + of the site occupied by the atom type. Args: - value (float): New occupancy fraction. + value: Occupancy of the atom site, representing the fraction of the site occupied by the atom type. """ self._occupancy.value = value @property def b_iso(self) -> Parameter: - r"""Isotropic atomic displacement parameter (*B*-factor). + """Isotropic atomic displacement parameter (ADP) for the atom + site. Returns: - Parameter: Descriptor for *B*\_iso (Ų). + Parameter: Isotropic atomic displacement parameter (ADP) for the atom site (Ų). """ return self._b_iso @b_iso.setter def b_iso(self, value: float) -> None: - r"""Set the isotropic displacement parameter. + """Set the isotropic atomic displacement parameter (ADP) for the + atom site. Args: - value (float): New *B*\_iso value in Ų. + value: Isotropic atomic displacement parameter (ADP) for the atom site (Ų). """ self._b_iso.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 0a7c4de6..7743606b 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -148,108 +148,108 @@ def _update( @property def length_a(self) -> Parameter: - """Length of the *a* axis. + """Length of the a axis of the unit cell. Returns: - Parameter: Descriptor for lattice parameter *a* (Å). + Parameter: Length of the a axis of the unit cell (Å). """ return self._length_a @length_a.setter def length_a(self, value: float) -> None: - """Set the length of the *a* axis. + """Set the length of the a axis of the unit cell. Args: - value (float): New length in ångströms. + value: Length of the a axis of the unit cell (Å). """ self._length_a.value = value @property def length_b(self) -> Parameter: - """Length of the *b* axis. + """Length of the b axis of the unit cell. Returns: - Parameter: Descriptor for lattice parameter *b* (Å). + Parameter: Length of the b axis of the unit cell (Å). """ return self._length_b @length_b.setter def length_b(self, value: float) -> None: - """Set the length of the *b* axis. + """Set the length of the b axis of the unit cell. Args: - value (float): New length in ångströms. + value: Length of the b axis of the unit cell (Å). """ self._length_b.value = value @property def length_c(self) -> Parameter: - """Length of the *c* axis. + """Length of the c axis of the unit cell. Returns: - Parameter: Descriptor for lattice parameter *c* (Å). + Parameter: Length of the c axis of the unit cell (Å). """ return self._length_c @length_c.setter def length_c(self, value: float) -> None: - """Set the length of the *c* axis. + """Set the length of the c axis of the unit cell. Args: - value (float): New length in ångströms. + value: Length of the c axis of the unit cell (Å). """ self._length_c.value = value @property def angle_alpha(self) -> Parameter: - """Angle between edges *b* and *c*. + """Angle between edges b and c. Returns: - Parameter: Descriptor for angle *α* (degrees). + Parameter: Angle between edges b and c (deg). """ return self._angle_alpha @angle_alpha.setter def angle_alpha(self, value: float) -> None: - """Set the angle between edges *b* and *c*. + """Set the angle between edges b and c. Args: - value (float): New angle in degrees. + value: Angle between edges b and c (deg). """ self._angle_alpha.value = value @property def angle_beta(self) -> Parameter: - """Angle between edges *a* and *c*. + """Angle between edges a and c. Returns: - Parameter: Descriptor for angle *β* (degrees). + Parameter: Angle between edges a and c (deg). """ return self._angle_beta @angle_beta.setter def angle_beta(self, value: float) -> None: - """Set the angle between edges *a* and *c*. + """Set the angle between edges a and c. Args: - value (float): New angle in degrees. + value: Angle between edges a and c (deg). """ self._angle_beta.value = value @property def angle_gamma(self) -> Parameter: - """Angle between edges *a* and *b*. + """Angle between edges a and b. Returns: - Parameter: Descriptor for angle *γ* (degrees). + Parameter: Angle between edges a and b (deg). """ return self._angle_gamma @angle_gamma.setter def angle_gamma(self, value: float) -> None: - """Set the angle between edges *a* and *b*. + """Set the angle between edges a and b. Args: - value (float): New angle in degrees. + value: Angle between edges a and b (deg). """ self._angle_gamma.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 8a0f38a7..e612ee83 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -128,40 +128,37 @@ def _it_coordinate_system_code_default_value(self) -> str: @property def name_h_m(self) -> StringDescriptor: - """Hermann–Mauguin symbol of the space group. + """Hermann-Mauguin symbol of the space group. Returns: - StringDescriptor: Descriptor holding the H-M symbol. + StringDescriptor: Hermann-Mauguin symbol of the space group. """ return self._name_h_m @name_h_m.setter def name_h_m(self, value: str) -> None: - """Set the Hermann–Mauguin symbol and reset the coordinate- - system code. + """Set the hermann-Mauguin symbol of the space group. Args: - value (str): New H-M symbol (must be a recognised short - symbol). + value: Hermann-Mauguin symbol of the space group. """ self._name_h_m.value = value self._reset_it_coordinate_system_code() @property def it_coordinate_system_code(self) -> StringDescriptor: - """International Tables coordinate-system code. + """A qualifier identifying which setting in IT is used. Returns: - StringDescriptor: Descriptor holding the IT code. + StringDescriptor: A qualifier identifying which setting in IT is used. """ return self._it_coordinate_system_code @it_coordinate_system_code.setter def it_coordinate_system_code(self, value: str) -> None: - """Set the IT coordinate-system code. + """Set the a qualifier identifying which setting in IT is used. Args: - value (str): New coordinate-system code (must be allowed for - the current space group). + value: A qualifier identifying which setting in IT is used. """ self._it_coordinate_system_code.value = value From 56eabcd1014d73a50b6a49cd085d67ddd09aa8ad Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 01:53:50 +0100 Subject: [PATCH 14/48] Enhance docstrings by including type annotations for setter arguments --- docs/architecture/architecture.md | 23 ++++++++++--------- pixi.toml | 4 ++-- .../analysis/categories/aliases/default.py | 4 ++-- .../categories/constraints/default.py | 4 ++-- .../analysis/categories/fit_mode/fit_mode.py | 2 +- .../joint_fit_experiments/default.py | 4 ++-- .../categories/background/chebyshev.py | 6 ++--- .../categories/background/line_segment.py | 6 ++--- .../categories/excluded_regions/default.py | 6 ++--- .../experiment/categories/extinction/shelx.py | 4 ++-- .../experiment/categories/instrument/cwl.py | 4 ++-- .../experiment/categories/instrument/tof.py | 10 ++++---- .../categories/linked_crystal/default.py | 4 ++-- .../categories/linked_phases/default.py | 4 ++-- .../experiment/categories/peak/cwl_mixins.py | 22 +++++++++--------- .../experiment/categories/peak/tof_mixins.py | 20 ++++++++-------- .../categories/peak/total_mixins.py | 12 +++++----- .../categories/atom_sites/default.py | 18 +++++++-------- .../structure/categories/cell/default.py | 12 +++++----- .../categories/space_group/default.py | 4 ++-- 20 files changed, 87 insertions(+), 86 deletions(-) diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index 2acbf456..53de1c20 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -1099,26 +1099,27 @@ def length_a(self, value: float) -> None: """Set the length of the a axis of the unit cell. Args: - value: Length of the a axis of the unit cell (Å). + value (float): Length of the a axis of the unit cell (Å). """ self._length_a.value = value ``` **Quick-reference table:** -| Location | Text | -| ------------------ | ------------------------------------------------------- | -| Getter 1st line | `"""{desc}.` | -| Getter `Returns:` | `{Type}: {desc} ({units}).` (or `{Type}: {desc}.`) | -| Setter 1st line | `"""Set the {desc, first letter lowercased}.` | -| Setter `Args:` | `value: {desc} ({units}).` (or `value: {desc}.`) | -| Getter annotation | `-> {Type}` | -| Setter annotation | `value: {ann}` and `-> None` | +| Location | Text | +| ------------------ | ----------------------------------------------------------------- | +| Getter 1st line | `"""{desc}.` | +| Getter `Returns:` | `{Type}: {desc} ({units}).` (or `{Type}: {desc}.`) | +| Setter 1st line | `"""Set the {desc, first letter lowercased}.` | +| Setter `Args:` | `value ({ann}): {desc} ({units}).` (or `value ({ann}): {desc}.`) | +| Getter annotation | `-> {Type}` | +| Setter annotation | `value: {ann}` and `-> None` | **Notes:** -- Do **not** repeat the type in the docstring `Args:` line (e.g. avoid - `value (float):`). The type is already in the function signature. +- Include the type in the docstring `Args:` line (e.g. `value (float):`) + so that `pydoclint` can verify consistency between the function + signature and the docstring. - Avoid markdown emphasis (`*a*`) in docstrings; use plain text to stay in sync with the `description` field. - The CI tool `pixi run param-consistency-check` validates compliance; diff --git a/pixi.toml b/pixi.toml index 28f3532b..dd645320 100644 --- a/pixi.toml +++ b/pixi.toml @@ -111,7 +111,7 @@ py-lint-check = 'ruff check src/ tests/ docs/docs/tutorials/' py-format-check = "ruff format --check src/ tests/ docs/docs/tutorials/" nonpy-format-check = "npx prettier --list-different --config=prettierrc.toml --ignore-unknown ." nonpy-format-check-modified = "python tools/nonpy_prettier_modified.py" -param-consistency-check = 'python tools/param_consistency.py --check' +param-consistency-check = 'python tools/param_consistency.py src/ --check' check = 'pre-commit run --hook-stage manual --all-files' @@ -125,7 +125,7 @@ py-lint-fix = 'ruff check --fix src/ tests/ docs/docs/tutorials/' py-format-fix = "ruff format src/ tests/ docs/docs/tutorials/" nonpy-format-fix = 'npx prettier --write --list-different --config=prettierrc.toml --ignore-unknown .' nonpy-format-fix-modified = "python tools/nonpy_prettier_modified.py --write" -param-consistency-fix = 'python tools/param_consistency.py --fix' +param-consistency-fix = 'python tools/param_consistency.py src/ --fix' success-message-fix = 'echo "✅ All auto-formatting steps completed successfully!"' fix = { depends-on = [ diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 96589832..6079577e 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -73,7 +73,7 @@ def label(self, value: str) -> None: """Set the ... Args: - value: ... + value (str): ... """ self._label.value = value @@ -91,7 +91,7 @@ def param_uid(self, value: str) -> None: """Set the ... Args: - value: ... + value (str): ... """ self._param_uid.value = value diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 9de77d42..d4713d4c 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -70,7 +70,7 @@ def lhs_alias(self, value: str) -> None: """Set the left-hand side of the equation. Args: - value: Left-hand side of the equation. + value (str): Left-hand side of the equation. """ self._lhs_alias.value = value @@ -88,7 +88,7 @@ def rhs_expr(self, value: str) -> None: """Set the right-hand side expression. Args: - value: Right-hand side expression. + value (str): Right-hand side expression. """ self._rhs_expr.value = value diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index 897cb1a9..8dd4631f 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -60,6 +60,6 @@ def mode(self, value: str) -> None: """Set the fitting strategy. Args: - value: Fitting strategy. + value (str): Fitting strategy. """ self._mode.value = value diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index f2086039..0a91b6c5 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -73,7 +73,7 @@ def id(self, value: str) -> None: """Set the experiment identifier. Args: - value: Experiment identifier. + value (str): Experiment identifier. """ self._id.value = value @@ -91,7 +91,7 @@ def weight(self, value: float) -> None: """Set the weight factor. Args: - value: Weight factor. + value (float): Weight factor. """ self._weight.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index c09b2d1b..9e5bb569 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -97,7 +97,7 @@ def id(self, value: str) -> None: """Set the identifier for this background polynomial term. Args: - value: Identifier for this background polynomial term. + value (str): Identifier for this background polynomial term. """ self._id.value = value @@ -115,7 +115,7 @@ def order(self, value: float) -> None: """Set the order used in a Chebyshev polynomial background term. Args: - value: Order used in a Chebyshev polynomial background term. + value (float): Order used in a Chebyshev polynomial background term. """ self._order.value = value @@ -134,7 +134,7 @@ def coef(self, value: float) -> None: term. Args: - value: Coefficient used in a Chebyshev polynomial background term. + value (float): Coefficient used in a Chebyshev polynomial background term. """ self._coef.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index 0237c14b..2beaf794 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -106,7 +106,7 @@ def id(self, value: str) -> None: """Set the identifier for this background line segment. Args: - value: Identifier for this background line segment. + value (str): Identifier for this background line segment. """ self._id.value = value @@ -127,7 +127,7 @@ def x(self, value: float) -> None: diffractogram. Args: - value: X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. + value (float): X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. """ self._x.value = value @@ -147,7 +147,7 @@ def y(self, value: float) -> None: representing the background in a calculated diffractogram. Args: - value: Intensity used to create many straight-line segments representing the background in a calculated diffractogram. + value (float): Intensity used to create many straight-line segments representing the background in a calculated diffractogram. """ self._y.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 800e0edc..7e143e27 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -84,7 +84,7 @@ def id(self, value: str) -> None: """Set the identifier for this excluded region. Args: - value: Identifier for this excluded region. + value (str): Identifier for this excluded region. """ self._id.value = value @@ -102,7 +102,7 @@ def start(self, value: float) -> None: """Set the start of the excluded region. Args: - value: Start of the excluded region. + value (float): Start of the excluded region. """ self._start.value = value @@ -120,7 +120,7 @@ def end(self, value: float) -> None: """Set the end of the excluded region. Args: - value: End of the excluded region. + value (float): End of the excluded region. """ self._end.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index e563a580..c1d5c4ec 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -81,7 +81,7 @@ def mosaicity(self, value: float) -> None: """Set the mosaicity value for extinction correction. Args: - value: Mosaicity value for extinction correction (deg). + value (float): Mosaicity value for extinction correction (deg). """ self._mosaicity.value = value @@ -99,6 +99,6 @@ def radius(self, value: float) -> None: """Set the crystal radius for extinction correction. Args: - value: Crystal radius for extinction correction (µm). + value (float): Crystal radius for extinction correction (µm). """ self._radius.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 41aaa119..6711d107 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -49,7 +49,7 @@ def setup_wavelength(self, value: float) -> None: """Set the incident neutron or X-ray wavelength. Args: - value: Incident neutron or X-ray wavelength (Å). + value (float): Incident neutron or X-ray wavelength (Å). """ self._setup_wavelength.value = value @@ -118,6 +118,6 @@ def calib_twotheta_offset(self, value: float) -> None: """Set the instrument misalignment offset. Args: - value: Instrument misalignment offset (deg). + value (float): Instrument misalignment offset (deg). """ self._calib_twotheta_offset.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index 7016981d..84cce720 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -112,7 +112,7 @@ def setup_twotheta_bank(self, value: float) -> None: """Set the detector bank position. Args: - value: Detector bank position (deg). + value (float): Detector bank position (deg). """ self._setup_twotheta_bank.value = value @@ -130,7 +130,7 @@ def calib_d_to_tof_offset(self, value: float) -> None: """Set the tOF offset. Args: - value: TOF offset (µs). + value (float): TOF offset (µs). """ self._calib_d_to_tof_offset.value = value @@ -148,7 +148,7 @@ def calib_d_to_tof_linear(self, value: float) -> None: """Set the tOF linear conversion. Args: - value: TOF linear conversion (µs/Å). + value (float): TOF linear conversion (µs/Å). """ self._calib_d_to_tof_linear.value = value @@ -166,7 +166,7 @@ def calib_d_to_tof_quad(self, value: float) -> None: """Set the tOF quadratic correction. Args: - value: TOF quadratic correction (µs/Ų). + value (float): TOF quadratic correction (µs/Ų). """ self._calib_d_to_tof_quad.value = value @@ -184,6 +184,6 @@ def calib_d_to_tof_recip(self, value: float) -> None: """Set the tOF reciprocal velocity correction. Args: - value: TOF reciprocal velocity correction (µs·Å). + value (float): TOF reciprocal velocity correction (µs·Å). """ self._calib_d_to_tof_recip.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index fa3888f6..28d2855a 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -75,7 +75,7 @@ def id(self, value: str) -> None: """Set the identifier of the linked crystal. Args: - value: Identifier of the linked crystal. + value (str): Identifier of the linked crystal. """ self._id.value = value @@ -93,6 +93,6 @@ def scale(self, value: float) -> None: """Set the scale factor of the linked crystal. Args: - value: Scale factor of the linked crystal. + value (float): Scale factor of the linked crystal. """ self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index e231dd12..d07db89e 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -66,7 +66,7 @@ def id(self, value: str) -> None: """Set the identifier of the linked phase. Args: - value: Identifier of the linked phase. + value (str): Identifier of the linked phase. """ self._id.value = value @@ -84,7 +84,7 @@ def scale(self, value: float) -> None: """Set the scale factor of the linked phase. Args: - value: Scale factor of the linked phase. + value (float): Scale factor of the linked phase. """ self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 57fb00c3..4a1f3179 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -92,7 +92,7 @@ def broad_gauss_u(self, value: float) -> None: size and instrument resolution). Args: - value: Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). + value (float): Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). """ self._broad_gauss_u.value = value @@ -112,7 +112,7 @@ def broad_gauss_v(self, value: float) -> None: broadening contribution). Args: - value: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + value (float): Gaussian broadening coefficient (instrumental broadening contribution) (deg²). """ self._broad_gauss_v.value = value @@ -132,7 +132,7 @@ def broad_gauss_w(self, value: float) -> None: broadening contribution). Args: - value: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + value (float): Gaussian broadening coefficient (instrumental broadening contribution) (deg²). """ self._broad_gauss_w.value = value @@ -152,7 +152,7 @@ def broad_lorentz_x(self, value: float) -> None: sample strain effects). Args: - value: Lorentzian broadening coefficient (dependent on sample strain effects) (deg). + value (float): Lorentzian broadening coefficient (dependent on sample strain effects) (deg). """ self._broad_lorentz_x.value = value @@ -172,7 +172,7 @@ def broad_lorentz_y(self, value: float) -> None: microstructural defects and strain). Args: - value: Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). + value (float): Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). """ self._broad_lorentz_y.value = value @@ -242,7 +242,7 @@ def asym_empir_1(self, value: float) -> None: """Set the empirical asymmetry coefficient p1. Args: - value: Empirical asymmetry coefficient p1. + value (float): Empirical asymmetry coefficient p1. """ self._asym_empir_1.value = value @@ -260,7 +260,7 @@ def asym_empir_2(self, value: float) -> None: """Set the empirical asymmetry coefficient p2. Args: - value: Empirical asymmetry coefficient p2. + value (float): Empirical asymmetry coefficient p2. """ self._asym_empir_2.value = value @@ -278,7 +278,7 @@ def asym_empir_3(self, value: float) -> None: """Set the empirical asymmetry coefficient p3. Args: - value: Empirical asymmetry coefficient p3. + value (float): Empirical asymmetry coefficient p3. """ self._asym_empir_3.value = value @@ -296,7 +296,7 @@ def asym_empir_4(self, value: float) -> None: """Set the empirical asymmetry coefficient p4. Args: - value: Empirical asymmetry coefficient p4. + value (float): Empirical asymmetry coefficient p4. """ self._asym_empir_4.value = value @@ -346,7 +346,7 @@ def asym_fcj_1(self, value: float) -> None: """Set the finger-Cox-Jephcoat asymmetry parameter 1. Args: - value: Finger-Cox-Jephcoat asymmetry parameter 1. + value (float): Finger-Cox-Jephcoat asymmetry parameter 1. """ self._asym_fcj_1.value = value @@ -364,6 +364,6 @@ def asym_fcj_2(self, value: float) -> None: """Set the finger-Cox-Jephcoat asymmetry parameter 2. Args: - value: Finger-Cox-Jephcoat asymmetry parameter 2. + value (float): Finger-Cox-Jephcoat asymmetry parameter 2. """ self._asym_fcj_2.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index be169ed4..b872ae99 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -123,7 +123,7 @@ def broad_gauss_sigma_0(self, value: float) -> None: resolution). Args: - value: Gaussian broadening coefficient (instrumental resolution) (µs²). + value (float): Gaussian broadening coefficient (instrumental resolution) (µs²). """ self._broad_gauss_sigma_0.value = value @@ -142,7 +142,7 @@ def broad_gauss_sigma_1(self, value: float) -> None: d-spacing). Args: - value: Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). + value (float): Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). """ self._broad_gauss_sigma_1.value = value @@ -161,7 +161,7 @@ def broad_gauss_sigma_2(self, value: float) -> None: term). Args: - value: Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). + value (float): Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). """ self._broad_gauss_sigma_2.value = value @@ -181,7 +181,7 @@ def broad_lorentz_gamma_0(self, value: float) -> None: microstrain effects). Args: - value: Lorentzian broadening coefficient (dependent on microstrain effects) (µs). + value (float): Lorentzian broadening coefficient (dependent on microstrain effects) (µs). """ self._broad_lorentz_gamma_0.value = value @@ -200,7 +200,7 @@ def broad_lorentz_gamma_1(self, value: float) -> None: d-spacing). Args: - value: Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). + value (float): Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). """ self._broad_lorentz_gamma_1.value = value @@ -220,7 +220,7 @@ def broad_lorentz_gamma_2(self, value: float) -> None: dependent term). Args: - value: Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). + value (float): Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). """ self._broad_lorentz_gamma_2.value = value @@ -240,7 +240,7 @@ def broad_mix_beta_0(self, value: float) -> None: Lorentzian contributions in TOF profiles. Args: - value: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + value (float): Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). """ self._broad_mix_beta_0.value = value @@ -260,7 +260,7 @@ def broad_mix_beta_1(self, value: float) -> None: Lorentzian contributions in TOF profiles. Args: - value: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + value (float): Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). """ self._broad_mix_beta_1.value = value @@ -306,7 +306,7 @@ def asym_alpha_0(self, value: float) -> None: """Set the ikeda-Carpenter asymmetry parameter α₀. Args: - value: Ikeda-Carpenter asymmetry parameter α₀. + value (float): Ikeda-Carpenter asymmetry parameter α₀. """ self._asym_alpha_0.value = value @@ -324,6 +324,6 @@ def asym_alpha_1(self, value: float) -> None: """Set the ikeda-Carpenter asymmetry parameter α₁. Args: - value: Ikeda-Carpenter asymmetry parameter α₁. + value (float): Ikeda-Carpenter asymmetry parameter α₁. """ self._asym_alpha_1.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 287bb4c5..67a052b2 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -104,7 +104,7 @@ def damp_q(self, value: float) -> None: high-r PDF peak amplitude). Args: - value: Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). + value (float): Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). """ self._damp_q.value = value @@ -124,7 +124,7 @@ def broad_q(self, value: float) -> None: and model uncertainty contribution). Args: - value: Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). + value (float): Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). """ self._broad_q.value = value @@ -144,7 +144,7 @@ def cutoff_q(self, value: float) -> None: transform (controls real-space resolution). Args: - value: Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). + value (float): Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). """ self._cutoff_q.value = value @@ -162,7 +162,7 @@ def sharp_delta_1(self, value: float) -> None: """Set the pDF peak sharpening coefficient (1/r dependence). Args: - value: PDF peak sharpening coefficient (1/r dependence) (Å). + value (float): PDF peak sharpening coefficient (1/r dependence) (Å). """ self._sharp_delta_1.value = value @@ -180,7 +180,7 @@ def sharp_delta_2(self, value: float) -> None: """Set the pDF peak sharpening coefficient (1/r² dependence). Args: - value: PDF peak sharpening coefficient (1/r² dependence) (Ų). + value (float): PDF peak sharpening coefficient (1/r² dependence) (Ų). """ self._sharp_delta_2.value = value @@ -200,6 +200,6 @@ def damp_particle_diameter(self, value: float) -> None: correction in PDF. Args: - value: Particle diameter for spherical envelope damping correction in PDF (Å). + value (float): Particle diameter for spherical envelope damping correction in PDF (Å). """ self._damp_particle_diameter.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index 428318d9..b17960dc 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -186,7 +186,7 @@ def label(self, value: str) -> None: """Set the unique identifier for the atom site. Args: - value: Unique identifier for the atom site. + value (str): Unique identifier for the atom site. """ self._label.value = value @@ -204,7 +204,7 @@ def type_symbol(self, value: str) -> None: """Set the chemical symbol of the atom at this site. Args: - value: Chemical symbol of the atom at this site. + value (str): Chemical symbol of the atom at this site. """ self._type_symbol.value = value @@ -224,7 +224,7 @@ def adp_type(self, value: str) -> None: (e.g., Biso, Uiso, Uani, Bani). Args: - value: Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). + value (str): Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). """ self._adp_type.value = value @@ -244,7 +244,7 @@ def wyckoff_letter(self, value: str) -> None: site within the space group. Args: - value: Wyckoff letter indicating the symmetry of the atom site within the space group. + value (str): Wyckoff letter indicating the symmetry of the atom site within the space group. """ self._wyckoff_letter.value = value @@ -264,7 +264,7 @@ def fract_x(self, value: float) -> None: unit cell. Args: - value: Fractional x-coordinate of the atom site within the unit cell. + value (float): Fractional x-coordinate of the atom site within the unit cell. """ self._fract_x.value = value @@ -284,7 +284,7 @@ def fract_y(self, value: float) -> None: unit cell. Args: - value: Fractional y-coordinate of the atom site within the unit cell. + value (float): Fractional y-coordinate of the atom site within the unit cell. """ self._fract_y.value = value @@ -304,7 +304,7 @@ def fract_z(self, value: float) -> None: unit cell. Args: - value: Fractional z-coordinate of the atom site within the unit cell. + value (float): Fractional z-coordinate of the atom site within the unit cell. """ self._fract_z.value = value @@ -324,7 +324,7 @@ def occupancy(self, value: float) -> None: of the site occupied by the atom type. Args: - value: Occupancy of the atom site, representing the fraction of the site occupied by the atom type. + value (float): Occupancy of the atom site, representing the fraction of the site occupied by the atom type. """ self._occupancy.value = value @@ -344,7 +344,7 @@ def b_iso(self, value: float) -> None: atom site. Args: - value: Isotropic atomic displacement parameter (ADP) for the atom site (Ų). + value (float): Isotropic atomic displacement parameter (ADP) for the atom site (Ų). """ self._b_iso.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 7743606b..f8988403 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -160,7 +160,7 @@ def length_a(self, value: float) -> None: """Set the length of the a axis of the unit cell. Args: - value: Length of the a axis of the unit cell (Å). + value (float): Length of the a axis of the unit cell (Å). """ self._length_a.value = value @@ -178,7 +178,7 @@ def length_b(self, value: float) -> None: """Set the length of the b axis of the unit cell. Args: - value: Length of the b axis of the unit cell (Å). + value (float): Length of the b axis of the unit cell (Å). """ self._length_b.value = value @@ -196,7 +196,7 @@ def length_c(self, value: float) -> None: """Set the length of the c axis of the unit cell. Args: - value: Length of the c axis of the unit cell (Å). + value (float): Length of the c axis of the unit cell (Å). """ self._length_c.value = value @@ -214,7 +214,7 @@ def angle_alpha(self, value: float) -> None: """Set the angle between edges b and c. Args: - value: Angle between edges b and c (deg). + value (float): Angle between edges b and c (deg). """ self._angle_alpha.value = value @@ -232,7 +232,7 @@ def angle_beta(self, value: float) -> None: """Set the angle between edges a and c. Args: - value: Angle between edges a and c (deg). + value (float): Angle between edges a and c (deg). """ self._angle_beta.value = value @@ -250,6 +250,6 @@ def angle_gamma(self, value: float) -> None: """Set the angle between edges a and b. Args: - value: Angle between edges a and b (deg). + value (float): Angle between edges a and b (deg). """ self._angle_gamma.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index e612ee83..11fe544b 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -140,7 +140,7 @@ def name_h_m(self, value: str) -> None: """Set the hermann-Mauguin symbol of the space group. Args: - value: Hermann-Mauguin symbol of the space group. + value (str): Hermann-Mauguin symbol of the space group. """ self._name_h_m.value = value self._reset_it_coordinate_system_code() @@ -159,6 +159,6 @@ def it_coordinate_system_code(self, value: str) -> None: """Set the a qualifier identifying which setting in IT is used. Args: - value: A qualifier identifying which setting in IT is used. + value (str): A qualifier identifying which setting in IT is used. """ self._it_coordinate_system_code.value = value From a077ef4dcd6717e050e8209e66c2a5a47104cae0 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 02:12:22 +0100 Subject: [PATCH 15/48] Ignore E501 lint rule in favor of formatter and W505 --- docs/architecture/architecture.md | 28 +- pixi.lock | 4 +- pyproject.toml | 14 +- tools/param_consistency.py | 595 ++++++++++++++++++++++++++++++ 4 files changed, 620 insertions(+), 21 deletions(-) create mode 100644 tools/param_consistency.py diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index 53de1c20..f015974c 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -1061,12 +1061,12 @@ derived from it. **Definitions:** -| Symbol | Meaning | -| ------------ | --------------------------------------------------------------------- | -| `{desc}` | `description` string without trailing period | -| `{units}` | `units` string; omit the `({units})` parenthetical when absent/empty | -| `{Type}` | Descriptor class name: `Parameter`, `NumericDescriptor`, or `StringDescriptor` | -| `{ann}` | Setter value annotation: `float` for numeric descriptors, `str` for string descriptors | +| Symbol | Meaning | +| --------- | -------------------------------------------------------------------------------------- | +| `{desc}` | `description` string without trailing period | +| `{units}` | `units` string; omit the `({units})` parenthetical when absent/empty | +| `{Type}` | Descriptor class name: `Parameter`, `NumericDescriptor`, or `StringDescriptor` | +| `{ann}` | Setter value annotation: `float` for numeric descriptors, `str` for string descriptors | **Template:** @@ -1106,14 +1106,14 @@ def length_a(self, value: float) -> None: **Quick-reference table:** -| Location | Text | -| ------------------ | ----------------------------------------------------------------- | -| Getter 1st line | `"""{desc}.` | -| Getter `Returns:` | `{Type}: {desc} ({units}).` (or `{Type}: {desc}.`) | -| Setter 1st line | `"""Set the {desc, first letter lowercased}.` | -| Setter `Args:` | `value ({ann}): {desc} ({units}).` (or `value ({ann}): {desc}.`) | -| Getter annotation | `-> {Type}` | -| Setter annotation | `value: {ann}` and `-> None` | +| Location | Text | +| ----------------- | ---------------------------------------------------------------- | +| Getter 1st line | `"""{desc}.` | +| Getter `Returns:` | `{Type}: {desc} ({units}).` (or `{Type}: {desc}.`) | +| Setter 1st line | `"""Set the {desc, first letter lowercased}.` | +| Setter `Args:` | `value ({ann}): {desc} ({units}).` (or `value ({ann}): {desc}.`) | +| Getter annotation | `-> {Type}` | +| Setter annotation | `value: {ann}` and `-> None` | **Notes:** diff --git a/pixi.lock b/pixi.lock index 6afb88dc..4c1e1ef4 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4883,8 +4883,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty9 - sha256: 91dc01dcc37a9ea9fbbb3aeb46f055f2caf4a4b0ad81f0b2d320988a0a9a55c9 + version: 0.10.2+devdirty17 + sha256: 4c0c086e8e51b822c55b1aaeb5353f55a8bdc1e36217e8c9fff0ed5191a8447d requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index 473eeece..60804b73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -278,6 +278,10 @@ ignore = [ # The following covered by [tool.pydoclint] section below 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc + # E501 applies max-line-length to ALL lines including docstrings. + # We ignore it because the ruff formatter already enforces line-length + # for code, and W505 enforces max-doc-length for docstrings. + 'E501', # https://docs.astral.sh/ruff/rules/line-too-long/ # Temporary: 'DTZ005', @@ -333,16 +337,16 @@ max-complexity = 10 # https://peps.python.org/pep-0008/#maximum-line-length # Use 99 characters as the project-wide maximum for regular code lines. max-line-length = 99 -# allow longer lines so that parameter declarations such as -# `name (Type | Type | None):` remain on a single line. Splitting these -# lines can prevent tools such as MkDocs and IDEs from correctly -# parsing and rendering parameter documentation. +# Allow longer docstring lines so that parameter declarations such as +# `name (Type | Type | None): Description text (units).` remain on a +# single line. Splitting these lines can prevent tools such as MkDocs +# and IDEs from correctly parsing and rendering parameter documentation. # The descriptive text itself is wrapped more strictly by # `docformatter` (see the configuration in [tool.docformatter] below) # whenever it is treated as normal paragraph text. # The line length for code snippets in docstrings is also more strict, # as defined in the [tool.ruff.format] section above. -max-doc-length = 99 +max-doc-length = 200 ############################# # Configuration for pydoclint diff --git a/tools/param_consistency.py b/tools/param_consistency.py new file mode 100644 index 00000000..6d74bf33 --- /dev/null +++ b/tools/param_consistency.py @@ -0,0 +1,595 @@ +"""Check and fix consistency between Parameter/Descriptor definitions +and their public property docstrings and type annotations. + +Usage: + python param_consistency.py --check # validate (exit code 0/1) + python param_consistency.py --fix # auto-fix docstrings and type hints + python param_consistency.py src/mypackage/ --check # scan only a specific directory + +Template (see docs/architecture/architecture.md §9.8 for the full spec) +----------------------------------------------------------------------- +Given ``description='Length of the a axis of the unit cell.'``, +``units='Å'``, and type ``Parameter``: + +Getter:: + + @property + def length_a(self) -> Parameter: + \"""Length of the a axis of the unit cell. + + Returns: + Parameter: Length of the a axis of the unit cell (Å). + \""" + return self._length_a + +Setter:: + + @length_a.setter + def length_a(self, value: float) -> None: + \"""Set the length of the a axis of the unit cell. + + Args: + value (float): Length of the a axis of the unit cell (Å). + \""" + self._length_a.value = value + +Rules: +- ``{desc}`` = description without trailing period (single source of truth). +- ``{units}`` = units string; omit ``({units})`` when absent or empty. +- Getter return annotation: the descriptor class name. +- Setter value annotation: ``float`` for numeric, ``str`` for string. +- Setter return annotation: ``None``. +- Setter ``Args`` uses the **actual parameter name** (conventionally + ``value``). + +Exit code 0 when all checks pass (or fix succeeds), 1 otherwise. +""" + +from __future__ import annotations + +import argparse +import ast +import sys +from dataclasses import dataclass +from dataclasses import field +from pathlib import Path + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- + +_SRC_ROOT = Path(__file__).resolve().parents[1] / 'src' / 'easydiffraction' + +_DESCRIPTOR_TYPES = frozenset({'Parameter', 'NumericDescriptor', 'StringDescriptor'}) +_NUMERIC_TYPES = frozenset({'Parameter', 'NumericDescriptor'}) + +# Canonical setter value annotation per descriptor family. +_SETTER_ANN: dict[str, str] = { + 'Parameter': 'float', + 'NumericDescriptor': 'float', + 'StringDescriptor': 'str', +} + + +# --------------------------------------------------------------------------- +# Data structures +# --------------------------------------------------------------------------- + + +@dataclass +class DescriptorInfo: + """Descriptor definition extracted from ``__init__``.""" + + attr_name: str # e.g. '_length_a' + prop_name: str # e.g. 'length_a' + type_name: str # 'Parameter' | 'NumericDescriptor' | 'StringDescriptor' + description: str # e.g. 'Length of the a axis of the unit cell.' + units: str | None # e.g. 'Å', or None / '' for unitless + + +@dataclass +class PropertyInfo: + """Property getter / setter AST nodes.""" + + name: str + getter: ast.FunctionDef + setter: ast.FunctionDef | None = None + + +@dataclass +class Edit: + """A source-level edit: replace ``lines[start:end]`` with *new_text*. + + When ``start == end`` the edit is an insertion before that line. + """ + + start: int # 0-based inclusive + end: int # 0-based exclusive + new_text: str + + +@dataclass +class FileResult: + """Analysis result for one source file.""" + + path: Path + issues: list[str] = field(default_factory=list) + edits: list[Edit] = field(default_factory=list) + + +# --------------------------------------------------------------------------- +# Template helpers +# --------------------------------------------------------------------------- + + +def _strip_dot(s: str) -> str: + """Remove a single trailing period and surrounding whitespace.""" + s = s.rstrip() + if s.endswith('.'): + s = s[:-1].rstrip() + return s + + +def _getter_docstring( + desc: str, + units: str | None, + type_name: str, + indent: str, +) -> str: + """Build the expected getter docstring.""" + d = _strip_dot(desc) + ret = f'{d} ({units}).' if units else f'{d}.' + return ( + f'{indent}"""{d}.\n' + f'\n' + f'{indent}Returns:\n' + f'{indent} {type_name}: {ret}\n' + f'{indent}"""\n' + ) + + +def _setter_docstring( + desc: str, + units: str | None, + param_name: str, + value_type: str, + indent: str, +) -> str: + """Build the expected setter docstring.""" + d = _strip_dot(desc) + lower = d[0].lower() + d[1:] + arg = f'{d} ({units}).' if units else f'{d}.' + return ( + f'{indent}"""Set the {lower}.\n' + f'\n' + f'{indent}Args:\n' + f'{indent} {param_name} ({value_type}): {arg}\n' + f'{indent}"""\n' + ) + + +def _normalize_docstring_src(text: str) -> str: + """Normalize docstring source for comparison. + + Handles formatting transformations that must not cause false + negatives: + + * **Summary wrapping** — ``docformatter`` may wrap long summary + paragraphs; continuation lines are collapsed into one line. + * **First-character capitalisation** — ``docformatter`` capitalises + the first letter of the summary; comparison is case-insensitive. + * **Hyphen word-breaks** — wrapping at hyphens produces + ``"foo-\\nbar"``; the dangling space is collapsed. + * **Section continuation lines** — ``docformatter`` may wrap long + ``Returns:`` / ``Args:`` content lines; continuation lines are + rejoined so that wrapped and single-line forms compare equal. + """ + parts = text.split('\n\n', 1) + # -- Summary paragraph -- + summary = ' '.join(parts[0].split()).lower() + summary = summary.replace('- ', '-') + summary = summary.replace('""" ', '"""') + if len(parts) <= 1: + return summary + + # -- Structured section (Returns / Args) -- + # Join continuation lines back into their parent line so that + # wrapping differences do not cause false negatives. + section_lines = parts[1].splitlines() + merged: list[str] = [] + for raw in section_lines: + stripped = raw.strip() + # A continuation line is non-empty, doesn't start a recognised + # keyword or the closing triple-quote, and appears right after + # a content line (not a blank or section header). + is_continuation = ( + stripped + and merged + and merged[-1].strip() + and not stripped.startswith(('Returns:', 'Args:', '"""')) + and not merged[-1].strip().endswith(':') + ) + if is_continuation: + merged[-1] = merged[-1].rstrip() + ' ' + stripped + else: + merged.append(raw) + return summary + '\n\n' + '\n'.join(merged) + + +# --------------------------------------------------------------------------- +# AST helpers +# --------------------------------------------------------------------------- + + +def _call_name(node: ast.Call) -> str | None: + """Return the simple name of a Call's function.""" + if isinstance(node.func, ast.Name): + return node.func.id + if isinstance(node.func, ast.Attribute): + return node.func.attr + return None + + +def _kwarg_str(call: ast.Call, name: str) -> str | None: + """Extract a string-valued keyword argument from *call*.""" + for kw in call.keywords: + if ( + kw.arg == name + and isinstance(kw.value, ast.Constant) + and isinstance(kw.value.value, str) + ): + return kw.value.value + return None + + +def _ann_str(ann: ast.expr | None) -> str | None: + """Return the annotation as a source-level string.""" + if ann is None: + return None + if isinstance(ann, ast.Name): + return ann.id + if isinstance(ann, ast.Constant) and isinstance(ann.value, str): + return ann.value # forward reference + return ast.unparse(ann) + + +def _body_indent(func: ast.FunctionDef, lines: list[str]) -> str: + """Compute the indentation string for the function body.""" + def_line = lines[func.lineno - 1] + return ' ' * (len(def_line) - len(def_line.lstrip()) + 4) + + +def _def_line_range(func: ast.FunctionDef, lines: list[str]) -> tuple[int, int]: + """Return 0-based ``[start, end)`` of the ``def`` statement.""" + start = func.lineno - 1 + for i in range(start, min(start + 10, len(lines))): + if lines[i].rstrip().endswith(':'): + return start, i + 1 + if func.body and i + 1 >= func.body[0].lineno: + break + return start, start + 1 + + +def _docstring_range(func: ast.FunctionDef) -> tuple[str | None, int, int]: + """Return ``(text, start_0, end_exclusive_0)`` of the docstring.""" + if not func.body: + return None, -1, -1 + first = func.body[0] + if ( + isinstance(first, ast.Expr) + and isinstance(first.value, ast.Constant) + and isinstance(first.value.value, str) + ): + # end_lineno is 1-based inclusive → 0-based exclusive is the same int + return first.value.value, first.lineno - 1, first.end_lineno + return None, -1, -1 + + +# --------------------------------------------------------------------------- +# Extraction +# --------------------------------------------------------------------------- + + +def _extract_descriptors(cls: ast.ClassDef) -> dict[str, DescriptorInfo]: + """Find ``self._xxx = DescriptorType(...)`` assignments in ``__init__``.""" + result: dict[str, DescriptorInfo] = {} + + init = next( + (n for n in cls.body if isinstance(n, ast.FunctionDef) and n.name == '__init__'), + None, + ) + if init is None: + return result + + for stmt in ast.walk(init): + # Handle both ast.Assign and ast.AnnAssign + if isinstance(stmt, ast.Assign) and len(stmt.targets) == 1: + target = stmt.targets[0] + value = stmt.value + elif isinstance(stmt, ast.AnnAssign) and stmt.value is not None: + target = stmt.target + value = stmt.value + else: + continue + + # Target must be self._xxx + if not ( + isinstance(target, ast.Attribute) + and isinstance(target.value, ast.Name) + and target.value.id == 'self' + and target.attr.startswith('_') + ): + continue + + if not isinstance(value, ast.Call): + continue + + name = _call_name(value) + if name not in _DESCRIPTOR_TYPES: + continue + + desc_str = _kwarg_str(value, 'description') + if not desc_str or not _strip_dot(desc_str): + continue + + units = _kwarg_str(value, 'units') or None + prop = target.attr.lstrip('_') + result[prop] = DescriptorInfo(target.attr, prop, name, desc_str, units) + + return result + + +def _extract_properties(cls: ast.ClassDef) -> dict[str, PropertyInfo]: + """Find property getters and their setters in *cls*.""" + result: dict[str, PropertyInfo] = {} + + for item in cls.body: + if not isinstance(item, ast.FunctionDef): + continue + for dec in item.decorator_list: + # @property + if isinstance(dec, ast.Name) and dec.id == 'property': + result[item.name] = PropertyInfo(item.name, item) + break + # @xxx.setter + if ( + isinstance(dec, ast.Attribute) + and dec.attr == 'setter' + and isinstance(dec.value, ast.Name) + and dec.value.id in result + ): + result[dec.value.id].setter = item + break + + return result + + +# --------------------------------------------------------------------------- +# Analysis (shared by --check and --fix) +# --------------------------------------------------------------------------- + + +def _analyze_file(path: Path) -> FileResult: + """Analyze one source file and return issues and proposed edits.""" + result = FileResult(path) + try: + source = path.read_text(encoding='utf-8') + except Exception: # noqa: BLE001 + return result + + lines = source.splitlines(keepends=True) + + try: + tree = ast.parse(source, filename=str(path)) + except SyntaxError: + return result + + for node in tree.body: + if not isinstance(node, ast.ClassDef): + continue + + descriptors = _extract_descriptors(node) + properties = _extract_properties(node) + + for prop_name, prop in properties.items(): + if prop_name not in descriptors: + continue + desc = descriptors[prop_name] + _analyze_property(node.name, prop, desc, lines, result) + + return result + + +def _analyze_property( + cls_name: str, + prop: PropertyInfo, + desc: DescriptorInfo, + lines: list[str], + result: FileResult, +) -> None: + """Check a single property against the template, accumulating issues and edits.""" + loc = f'{cls_name}.{prop.name}' + indent = _body_indent(prop.getter, lines) + + # --- Getter return annotation --- + actual_ret = _ann_str(prop.getter.returns) + expected_ret = desc.type_name + if actual_ret != expected_ret: + result.issues.append( + f'{loc}: getter annotation -> {actual_ret} (expected {expected_ret})' + ) + ds, de = _def_line_range(prop.getter, lines) + def_indent = lines[ds][: len(lines[ds]) - len(lines[ds].lstrip())] + new_def = f'{def_indent}def {prop.name}(self) -> {expected_ret}:\n' + result.edits.append(Edit(ds, de, new_def)) + + # --- Getter docstring --- + expected_doc = _getter_docstring(desc.description, desc.units, desc.type_name, indent) + actual_doc_text, doc_s, doc_e = _docstring_range(prop.getter) + + if actual_doc_text is None: + result.issues.append(f'{loc}: getter missing docstring') + _, def_end = _def_line_range(prop.getter, lines) + result.edits.append(Edit(def_end, def_end, expected_doc)) + else: + actual_src = ''.join(lines[doc_s:doc_e]) + if _normalize_docstring_src(actual_src) != _normalize_docstring_src(expected_doc): + result.issues.append(f'{loc}: getter docstring does not match template') + result.edits.append(Edit(doc_s, doc_e, expected_doc)) + + # --- Setter --- + if prop.setter is None: + return + + setter_args = prop.setter.args.args + setter_param = setter_args[1].arg if len(setter_args) >= 2 else 'value' + expected_ann = _SETTER_ANN[desc.type_name] + + # Setter def-line annotations (value type + return type) + actual_val_ann = None + if len(setter_args) >= 2 and setter_args[1].annotation: + actual_val_ann = _ann_str(setter_args[1].annotation) + + actual_ret_ann = _ann_str(prop.setter.returns) + + if actual_val_ann != expected_ann or actual_ret_ann != 'None': + parts: list[str] = [] + if actual_val_ann != expected_ann: + parts.append(f'value: {actual_val_ann} (expected {expected_ann})') + if actual_ret_ann != 'None': + parts.append(f'return: {actual_ret_ann} (expected None)') + result.issues.append(f'{loc}: setter annotation — {", ".join(parts)}') + + ds, de = _def_line_range(prop.setter, lines) + def_indent = lines[ds][: len(lines[ds]) - len(lines[ds].lstrip())] + new_def = f'{def_indent}def {prop.name}(self, {setter_param}: {expected_ann}) -> None:\n' + result.edits.append(Edit(ds, de, new_def)) + + # Setter docstring + expected_setter_doc = _setter_docstring( + desc.description, desc.units, setter_param, expected_ann, indent + ) + actual_setter_doc, sd_s, sd_e = _docstring_range(prop.setter) + + if actual_setter_doc is None: + result.issues.append(f'{loc}: setter missing docstring') + _, def_end = _def_line_range(prop.setter, lines) + result.edits.append(Edit(def_end, def_end, expected_setter_doc)) + else: + actual_setter_src = ''.join(lines[sd_s:sd_e]) + if _normalize_docstring_src(actual_setter_src) != _normalize_docstring_src( + expected_setter_doc + ): + result.issues.append(f'{loc}: setter docstring does not match template') + result.edits.append(Edit(sd_s, sd_e, expected_setter_doc)) + + +# --------------------------------------------------------------------------- +# Apply edits +# --------------------------------------------------------------------------- + + +def _apply_edits(lines: list[str], edits: list[Edit]) -> list[str]: + """Apply edits bottom-up to preserve line numbers.""" + sorted_edits = sorted(edits, key=lambda e: e.start, reverse=True) + result = list(lines) + for edit in sorted_edits: + new_lines = edit.new_text.splitlines(keepends=True) + result[edit.start : edit.end] = new_lines + return result + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + + +def _collect_py_files(paths: list[str]) -> list[Path]: + """Resolve *paths* to a sorted list of ``.py`` files. + + Each entry can be a directory (recursively globbed) or a single + ``.py`` file. When *paths* is empty, defaults to ``_SRC_ROOT``. + """ + if not paths: + return sorted(_SRC_ROOT.rglob('*.py')) + + result: list[Path] = [] + for raw in paths: + p = Path(raw).resolve() + if p.is_dir(): + result.extend(p.rglob('*.py')) + elif p.is_file() and p.suffix == '.py': + result.append(p) + return sorted(set(result)) + + +def main() -> int: + """Run param-consistency check or fix.""" + parser = argparse.ArgumentParser( + description='Parameter / property consistency: docstrings and type hints.', + ) + parser.add_argument( + 'paths', + nargs='*', + help='Directories or .py files to scan (default: src/easydiffraction/)', + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + '--check', + action='store_true', + help='Validate consistency (default)', + ) + group.add_argument( + '--fix', + action='store_true', + help='Auto-fix docstrings and type hints in-place', + ) + args = parser.parse_args() + + py_files = _collect_py_files(args.paths) + repo_root = Path(__file__).resolve().parents[1] + total_issues = 0 + total_fixed = 0 + files_touched = 0 + + for path in py_files: + result = _analyze_file(path) + if not result.issues: + continue + + try: + rel = path.relative_to(repo_root) + except ValueError: + rel = path + + if args.fix: + source_lines = path.read_text(encoding='utf-8').splitlines(keepends=True) + fixed_lines = _apply_edits(source_lines, result.edits) + path.write_text(''.join(fixed_lines), encoding='utf-8') + count = len(result.issues) + total_fixed += count + files_touched += 1 + print(f'📝 {rel}: fixed {count} issue(s)') + else: + for issue in result.issues: + print(f' ❌ {rel}: {issue}') + total_issues += len(result.issues) + + # Summary + print() + if args.fix: + print(f'✅ Fixed {total_fixed} issue(s) in {files_touched} file(s).') + return 0 + if total_issues: + print(f'❌ {total_issues} consistency issue(s) found.') + return 1 + print('✅ All properties match the template.') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) + + + + From 5179138988e8557d6502048be4c58f398dff00a8 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 09:54:29 +0100 Subject: [PATCH 16/48] Update line length settings in pyproject.toml for consistency and clarity --- pyproject.toml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 60804b73..661a627a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -198,7 +198,7 @@ exclude = [ 'src/easydiffraction/utils/_vendored/jupyter_dark_detect', ] indent-width = 4 -line-length = 99 +line-length = 99 # See also `max-line-length` in [tool.ruff.lint.pycodestyle] preview = true # Enable new rules that are not yet stable, like DOC # Formatting options for Ruff @@ -281,7 +281,7 @@ ignore = [ # E501 applies max-line-length to ALL lines including docstrings. # We ignore it because the ruff formatter already enforces line-length # for code, and W505 enforces max-doc-length for docstrings. - 'E501', # https://docs.astral.sh/ruff/rules/line-too-long/ + #'E501', # https://docs.astral.sh/ruff/rules/line-too-long/ # Temporary: 'DTZ005', @@ -308,10 +308,12 @@ ignore = [ 'E501', 'E741', 'F841', + 'I001', 'N801', 'N805', 'N812', 'SIM117', + 'W505', ] 'docs/**' = [ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ @@ -336,17 +338,10 @@ max-complexity = 10 # PEP 8 line length guidance: # https://peps.python.org/pep-0008/#maximum-line-length # Use 99 characters as the project-wide maximum for regular code lines. -max-line-length = 99 -# Allow longer docstring lines so that parameter declarations such as -# `name (Type | Type | None): Description text (units).` remain on a -# single line. Splitting these lines can prevent tools such as MkDocs -# and IDEs from correctly parsing and rendering parameter documentation. -# The descriptive text itself is wrapped more strictly by -# `docformatter` (see the configuration in [tool.docformatter] below) -# whenever it is treated as normal paragraph text. -# The line length for code snippets in docstrings is also more strict, -# as defined in the [tool.ruff.format] section above. -max-doc-length = 200 +# Use 72 characters for docstrings. +max-line-length = 99 # See also `line-length` in [tool.ruff] +max-doc-length = 72 + ############################# # Configuration for pydoclint From e20b14680ec627a377b6d7d54dc7519d09f09f62 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 10:21:15 +0100 Subject: [PATCH 17/48] Simplify property docstring template and checker --- docs/architecture/architecture.md | 59 +-- pixi.lock | 4 +- .../analysis/categories/aliases/default.py | 20 +- .../categories/constraints/default.py | 20 +- .../analysis/categories/fit_mode/fit_mode.py | 10 +- .../joint_fit_experiments/default.py | 20 +- .../categories/background/chebyshev.py | 31 +- .../categories/background/line_segment.py | 39 +- .../experiment/categories/data/bragg_pd.py | 51 +- .../experiment/categories/data/bragg_sc.py | 54 +- .../experiment/categories/data/total_pd.py | 26 +- .../categories/excluded_regions/default.py | 30 +- .../categories/experiment_type/default.py | 22 +- .../experiment/categories/extinction/shelx.py | 24 +- .../experiment/categories/instrument/cwl.py | 24 +- .../experiment/categories/instrument/tof.py | 60 +-- .../categories/linked_crystal/default.py | 20 +- .../categories/linked_phases/default.py | 20 +- .../experiment/categories/peak/cwl_mixins.py | 130 ++--- .../experiment/categories/peak/tof_mixins.py | 128 ++--- .../categories/peak/total_mixins.py | 80 +-- .../categories/atom_sites/default.py | 118 ++--- .../structure/categories/cell/default.py | 77 +-- .../categories/space_group/default.py | 20 +- tools/param_consistency.py | 485 ++++++++++-------- 25 files changed, 647 insertions(+), 925 deletions(-) diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index f015974c..4c7c5498 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -1068,58 +1068,51 @@ derived from it. | `{Type}` | Descriptor class name: `Parameter`, `NumericDescriptor`, or `StringDescriptor` | | `{ann}` | Setter value annotation: `float` for numeric descriptors, `str` for string descriptors | -**Template:** +**Template — writable property:** ```python -# ── Private attribute (in __init__) ────────────────────────────── -self._length_a = Parameter( - name='length_a', - description='Length of the a axis of the unit cell.', - units='Å', - value_spec=AttributeSpec( - default=10.0, - validator=RangeValidator(ge=0, le=1000), - ), - cif_handler=CifHandler(names=['_cell.length_a']), -) - -# ── Getter ─────────────────────────────────────────────────────── @property def length_a(self) -> Parameter: - """Length of the a axis of the unit cell. + """Length of the a axis of the unit cell (Å). - Returns: - Parameter: Length of the a axis of the unit cell (Å). + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._length_a -# ── Setter ─────────────────────────────────────────────────────── @length_a.setter def length_a(self, value: float) -> None: - """Set the length of the a axis of the unit cell. + self._length_a.value = value +``` + +**Template — read-only property:** - Args: - value (float): Length of the a axis of the unit cell (Å). +```python +@property +def length_a(self) -> Parameter: + """Length of the a axis of the unit cell (Å). + + Reading this property returns the underlying ``Parameter`` + object. """ - self._length_a.value = value + return self._length_a ``` **Quick-reference table:** -| Location | Text | -| ----------------- | ---------------------------------------------------------------- | -| Getter 1st line | `"""{desc}.` | -| Getter `Returns:` | `{Type}: {desc} ({units}).` (or `{Type}: {desc}.`) | -| Setter 1st line | `"""Set the {desc, first letter lowercased}.` | -| Setter `Args:` | `value ({ann}): {desc} ({units}).` (or `value ({ann}): {desc}.`) | -| Getter annotation | `-> {Type}` | -| Setter annotation | `value: {ann}` and `-> None` | +| Element | Text | +| ---------------------- | ------------------------------------------------------------------------------------------- | +| Getter summary line | `"""{desc} ({units}).` (or `"""{desc}.` when unitless) | +| Getter body (writable) | `Reading this property returns the underlying ``{Type}`` object. Assigning to it updates the parameter value.` | +| Getter body (readonly) | `Reading this property returns the underlying ``{Type}`` object.` | +| Setter docstring | *(none — not rendered by griffe / MkDocs)* | +| Getter annotation | `-> {Type}` | +| Setter annotation | `value: {ann}` and `-> None` | **Notes:** -- Include the type in the docstring `Args:` line (e.g. `value (float):`) - so that `pydoclint` can verify consistency between the function - signature and the docstring. +- Getter docstrings have **no** `Args:` or `Returns:` sections. +- Setters have **no** docstring. - Avoid markdown emphasis (`*a*`) in docstrings; use plain text to stay in sync with the `description` field. - The CI tool `pixi run param-consistency-check` validates compliance; diff --git a/pixi.lock b/pixi.lock index 4c1e1ef4..3f7ce83c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4883,8 +4883,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty17 - sha256: 4c0c086e8e51b822c55b1aaeb5353f55a8bdc1e36217e8c9fff0ed5191a8447d + version: 0.10.2+devdirty18 + sha256: d28acf83fdc1dd4d99d00fae4b1607f20fb326757dd1852dd96e2eb681cbe44d requires_dist: - asciichartpy - asteval diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 6079577e..d8667cfb 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -63,36 +63,28 @@ def __init__(self) -> None: def label(self) -> StringDescriptor: """... - Returns: - StringDescriptor: ... + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._label @label.setter def label(self, value: str) -> None: - """Set the ... - - Args: - value (str): ... - """ self._label.value = value @property def param_uid(self) -> StringDescriptor: """... - Returns: - StringDescriptor: ... + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._param_uid @param_uid.setter def param_uid(self, value: str) -> None: - """Set the ... - - Args: - value (str): ... - """ self._param_uid.value = value diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index d4713d4c..4a14aa33 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -60,36 +60,28 @@ def __init__(self) -> None: def lhs_alias(self) -> StringDescriptor: """Left-hand side of the equation. - Returns: - StringDescriptor: Left-hand side of the equation. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._lhs_alias @lhs_alias.setter def lhs_alias(self, value: str) -> None: - """Set the left-hand side of the equation. - - Args: - value (str): Left-hand side of the equation. - """ self._lhs_alias.value = value @property def rhs_expr(self) -> StringDescriptor: """Right-hand side expression. - Returns: - StringDescriptor: Right-hand side expression. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._rhs_expr @rhs_expr.setter def rhs_expr(self, value: str) -> None: - """Set the right-hand side expression. - - Args: - value (str): Right-hand side expression. - """ self._rhs_expr.value = value diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index 8dd4631f..ecd31415 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -50,16 +50,12 @@ def __init__(self) -> None: def mode(self) -> StringDescriptor: """Fitting strategy. - Returns: - StringDescriptor: Fitting strategy. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._mode @mode.setter def mode(self, value: str) -> None: - """Set the fitting strategy. - - Args: - value (str): Fitting strategy. - """ self._mode.value = value diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index 0a91b6c5..03916a4e 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -63,36 +63,28 @@ def __init__(self) -> None: def id(self) -> StringDescriptor: """Experiment identifier. - Returns: - StringDescriptor: Experiment identifier. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._id @id.setter def id(self, value: str) -> None: - """Set the experiment identifier. - - Args: - value (str): Experiment identifier. - """ self._id.value = value @property def weight(self) -> NumericDescriptor: """Weight factor. - Returns: - NumericDescriptor: Weight factor. + Reading this property returns the underlying + ``NumericDescriptor` object. + Assigning to it updates the parameter value. """ return self._weight @weight.setter def weight(self, value: float) -> None: - """Set the weight factor. - - Args: - value (float): Weight factor. - """ self._weight.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 9e5bb569..66d803e0 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -87,55 +87,42 @@ def __init__(self) -> None: def id(self) -> StringDescriptor: """Identifier for this background polynomial term. - Returns: - StringDescriptor: Identifier for this background polynomial term. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._id @id.setter def id(self, value: str) -> None: - """Set the identifier for this background polynomial term. - - Args: - value (str): Identifier for this background polynomial term. - """ self._id.value = value @property def order(self) -> NumericDescriptor: """Order used in a Chebyshev polynomial background term. - Returns: - NumericDescriptor: Order used in a Chebyshev polynomial background term. + Reading this property returns the underlying + ``NumericDescriptor` object. + Assigning to it updates the parameter value. """ return self._order @order.setter def order(self, value: float) -> None: - """Set the order used in a Chebyshev polynomial background term. - - Args: - value (float): Order used in a Chebyshev polynomial background term. - """ self._order.value = value @property def coef(self) -> Parameter: """Coefficient used in a Chebyshev polynomial background term. - Returns: - Parameter: Coefficient used in a Chebyshev polynomial background term. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._coef @coef.setter def coef(self, value: float) -> None: - """Set the coefficient used in a Chebyshev polynomial background - term. - - Args: - value (float): Coefficient used in a Chebyshev polynomial background term. - """ self._coef.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index 2beaf794..f4fc2f6a 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -96,59 +96,42 @@ def __init__(self) -> None: def id(self) -> StringDescriptor: """Identifier for this background line segment. - Returns: - StringDescriptor: Identifier for this background line segment. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._id @id.setter def id(self, value: str) -> None: - """Set the identifier for this background line segment. - - Args: - value (str): Identifier for this background line segment. - """ self._id.value = value @property def x(self) -> NumericDescriptor: - """X-coordinates used to create many straight-line segments - representing the background in a calculated diffractogram. + """X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. - Returns: - NumericDescriptor: X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. + Reading this property returns the underlying + ``NumericDescriptor` object. + Assigning to it updates the parameter value. """ return self._x @x.setter def x(self, value: float) -> None: - """Set the x-coordinates used to create many straight-line - segments representing the background in a calculated - diffractogram. - - Args: - value (float): X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. - """ self._x.value = value @property def y(self) -> Parameter: - """Intensity used to create many straight-line segments - representing the background in a calculated diffractogram. + """Intensity used to create many straight-line segments representing the background in a calculated diffractogram. - Returns: - Parameter: Intensity used to create many straight-line segments representing the background in a calculated diffractogram. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._y @y.setter def y(self, value: float) -> None: - """Set the intensity used to create many straight-line segments - representing the background in a calculated diffractogram. - - Args: - value (float): Intensity used to create many straight-line segments representing the background in a calculated diffractogram. - """ self._y.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 59c2ed72..56a63c2d 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -125,47 +125,44 @@ def __init__(self): def point_id(self) -> StringDescriptor: """Identifier for this data point in the dataset. - Returns: - StringDescriptor: Identifier for this data point in the dataset. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._point_id @property def d_spacing(self) -> NumericDescriptor: - """D-spacing value corresponding to this data point. + """d-spacing value corresponding to this data point. - Returns: - NumericDescriptor: d-spacing value corresponding to this data point. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._d_spacing @property def intensity_meas(self) -> NumericDescriptor: - """Intensity recorded at each measurement point as a function of - angle/time. + """Intensity recorded at each measurement point as a function of angle/time. - Returns: - NumericDescriptor: Intensity recorded at each measurement point as a function of angle/time. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._intensity_meas @property def intensity_meas_su(self) -> NumericDescriptor: - """Standard uncertainty of the measured intensity at this data - point. + """Standard uncertainty of the measured intensity at this data point. - Returns: - NumericDescriptor: Standard uncertainty of the measured intensity at this data point. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._intensity_meas_su @property def intensity_calc(self) -> NumericDescriptor: - """Intensity value for a computed diffractogram at this data - point. + """Intensity value for a computed diffractogram at this data point. - Returns: - NumericDescriptor: Intensity value for a computed diffractogram at this data point. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._intensity_calc @@ -173,8 +170,8 @@ def intensity_calc(self) -> NumericDescriptor: def intensity_bkg(self) -> NumericDescriptor: """Intensity value for a computed background at this data point. - Returns: - NumericDescriptor: Intensity value for a computed background at this data point. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._intensity_bkg @@ -182,8 +179,8 @@ def intensity_bkg(self) -> NumericDescriptor: def calc_status(self) -> StringDescriptor: """Status code of the data point in the calculation process. - Returns: - StringDescriptor: Status code of the data point in the calculation process. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._calc_status @@ -218,10 +215,10 @@ def __init__(self): @property def two_theta(self) -> NumericDescriptor: - """Measured 2θ diffraction angle. + """Measured 2θ diffraction angle (deg). - Returns: - NumericDescriptor: Measured 2θ diffraction angle (deg). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._two_theta @@ -249,10 +246,10 @@ def __init__(self): @property def time_of_flight(self) -> NumericDescriptor: - """Measured time for time-of-flight neutron measurement. + """Measured time for time-of-flight neutron measurement (µs). - Returns: - NumericDescriptor: Measured time for time-of-flight neutron measurement (µs). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._time_of_flight diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 3b074e45..3e2c2ac6 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -141,27 +141,26 @@ def __init__(self) -> None: def id(self) -> StringDescriptor: """Identifier of the reflection. - Returns: - StringDescriptor: Identifier of the reflection. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._id @property def d_spacing(self) -> NumericDescriptor: - """The distance between lattice planes in the crystal for this - reflection. + """The distance between lattice planes in the crystal for this reflection (Å). - Returns: - NumericDescriptor: The distance between lattice planes in the crystal for this reflection (Å). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._d_spacing @property def sin_theta_over_lambda(self) -> NumericDescriptor: - """The sin(θ)/λ value for this reflection. + """The sin(θ)/λ value for this reflection (Å⁻¹). - Returns: - NumericDescriptor: The sin(θ)/λ value for this reflection (Å⁻¹). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._sin_theta_over_lambda @@ -169,8 +168,8 @@ def sin_theta_over_lambda(self) -> NumericDescriptor: def index_h(self) -> NumericDescriptor: """Miller index h of a measured reflection. - Returns: - NumericDescriptor: Miller index h of a measured reflection. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._index_h @@ -178,8 +177,8 @@ def index_h(self) -> NumericDescriptor: def index_k(self) -> NumericDescriptor: """Miller index k of a measured reflection. - Returns: - NumericDescriptor: Miller index k of a measured reflection. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._index_k @@ -187,18 +186,17 @@ def index_k(self) -> NumericDescriptor: def index_l(self) -> NumericDescriptor: """Miller index l of a measured reflection. - Returns: - NumericDescriptor: Miller index l of a measured reflection. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._index_l @property def intensity_meas(self) -> NumericDescriptor: - """The intensity of the reflection derived from the - measurements. + """The intensity of the reflection derived from the measurements. - Returns: - NumericDescriptor: The intensity of the reflection derived from the measurements. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._intensity_meas @@ -206,28 +204,26 @@ def intensity_meas(self) -> NumericDescriptor: def intensity_meas_su(self) -> NumericDescriptor: """Standard uncertainty of the measured intensity. - Returns: - NumericDescriptor: Standard uncertainty of the measured intensity. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._intensity_meas_su @property def intensity_calc(self) -> NumericDescriptor: - """The intensity of the reflection calculated from the atom site - data. + """The intensity of the reflection calculated from the atom site data. - Returns: - NumericDescriptor: The intensity of the reflection calculated from the atom site data. + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._intensity_calc @property def wavelength(self) -> NumericDescriptor: - """The mean wavelength of radiation used to measure this - reflection. + """The mean wavelength of radiation used to measure this reflection (Å). - Returns: - NumericDescriptor: The mean wavelength of radiation used to measure this reflection (Å). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._wavelength diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index 09d6cb20..7ecffbca 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -124,17 +124,17 @@ def __init__(self) -> None: def point_id(self) -> StringDescriptor: """Identifier for this data point in the dataset. - Returns: - StringDescriptor: Identifier for this data point in the dataset. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._point_id @property def r(self) -> NumericDescriptor: - """Interatomic distance in real space. + """Interatomic distance in real space (Å). - Returns: - NumericDescriptor: Interatomic distance in real space (Å). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._r @@ -142,8 +142,8 @@ def r(self) -> NumericDescriptor: def g_r_meas(self) -> NumericDescriptor: """Measured pair distribution function G(r). - Returns: - NumericDescriptor: Measured pair distribution function G(r). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._g_r_meas @@ -151,8 +151,8 @@ def g_r_meas(self) -> NumericDescriptor: def g_r_meas_su(self) -> NumericDescriptor: """Standard uncertainty of measured G(r). - Returns: - NumericDescriptor: Standard uncertainty of measured G(r). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._g_r_meas_su @@ -160,8 +160,8 @@ def g_r_meas_su(self) -> NumericDescriptor: def g_r_calc(self) -> NumericDescriptor: """Calculated pair distribution function G(r). - Returns: - NumericDescriptor: Calculated pair distribution function G(r). + Reading this property returns the underlying + ``NumericDescriptor` object. """ return self._g_r_calc @@ -169,8 +169,8 @@ def g_r_calc(self) -> NumericDescriptor: def calc_status(self) -> StringDescriptor: """Status code of the data point in calculation. - Returns: - StringDescriptor: Status code of the data point in calculation. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._calc_status diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 7e143e27..839f1c08 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -74,54 +74,42 @@ def __init__(self): def id(self) -> StringDescriptor: """Identifier for this excluded region. - Returns: - StringDescriptor: Identifier for this excluded region. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._id @id.setter def id(self, value: str) -> None: - """Set the identifier for this excluded region. - - Args: - value (str): Identifier for this excluded region. - """ self._id.value = value @property def start(self) -> NumericDescriptor: """Start of the excluded region. - Returns: - NumericDescriptor: Start of the excluded region. + Reading this property returns the underlying + ``NumericDescriptor` object. + Assigning to it updates the parameter value. """ return self._start @start.setter def start(self, value: float) -> None: - """Set the start of the excluded region. - - Args: - value (float): Start of the excluded region. - """ self._start.value = value @property def end(self) -> NumericDescriptor: """End of the excluded region. - Returns: - NumericDescriptor: End of the excluded region. + Reading this property returns the underlying + ``NumericDescriptor` object. + Assigning to it updates the parameter value. """ return self._end @end.setter def end(self, value: float) -> None: - """Set the end of the excluded region. - - Args: - value (float): End of the excluded region. - """ self._end.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index b9a2ceb2..3ebbf94c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -113,21 +113,19 @@ def _set_scattering_type(self, value: str) -> None: @property def sample_form(self) -> StringDescriptor: - """Specifies whether the diffraction data corresponds to powder - diffraction or single crystal diffraction. + """Specifies whether the diffraction data corresponds to powder diffraction or single crystal diffraction. - Returns: - StringDescriptor: Specifies whether the diffraction data corresponds to powder diffraction or single crystal diffraction. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._sample_form @property def beam_mode(self) -> StringDescriptor: - """Defines whether the measurement is performed with a constant - wavelength (CW) or time-of-flight (TOF) method. + """Defines whether the measurement is performed with a constant wavelength (CW) or time-of-flight (TOF) method. - Returns: - StringDescriptor: Defines whether the measurement is performed with a constant wavelength (CW) or time-of-flight (TOF) method. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._beam_mode @@ -135,8 +133,8 @@ def beam_mode(self) -> StringDescriptor: def radiation_probe(self) -> StringDescriptor: """Specifies whether the measurement uses neutrons or X-rays. - Returns: - StringDescriptor: Specifies whether the measurement uses neutrons or X-rays. + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._radiation_probe @@ -144,7 +142,7 @@ def radiation_probe(self) -> StringDescriptor: def scattering_type(self) -> StringDescriptor: """Specifies whether the experiment uses Bragg scattering (for conventional structure refinement) or total scattering (for pair distribution function analysis - PDF). - Returns: - StringDescriptor: Specifies whether the experiment uses Bragg scattering (for conventional structure refinement) or total scattering (for pair distribution function analysis - PDF). + Reading this property returns the underlying + ``StringDescriptor` object. """ return self._scattering_type diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index c1d5c4ec..7d8f45bc 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -69,36 +69,28 @@ def __init__(self) -> None: @property def mosaicity(self) -> Parameter: - """Mosaicity value for extinction correction. + """Mosaicity value for extinction correction (deg). - Returns: - Parameter: Mosaicity value for extinction correction (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._mosaicity @mosaicity.setter def mosaicity(self, value: float) -> None: - """Set the mosaicity value for extinction correction. - - Args: - value (float): Mosaicity value for extinction correction (deg). - """ self._mosaicity.value = value @property def radius(self) -> Parameter: - """Crystal radius for extinction correction. + """Crystal radius for extinction correction (µm). - Returns: - Parameter: Crystal radius for extinction correction (µm). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._radius @radius.setter def radius(self, value: float) -> None: - """Set the crystal radius for extinction correction. - - Args: - value (float): Crystal radius for extinction correction (µm). - """ self._radius.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 6711d107..df527fda 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -37,20 +37,16 @@ def __init__(self) -> None: @property def setup_wavelength(self) -> Parameter: - """Incident neutron or X-ray wavelength. + """Incident neutron or X-ray wavelength (Å). - Returns: - Parameter: Incident neutron or X-ray wavelength (Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._setup_wavelength @setup_wavelength.setter def setup_wavelength(self, value: float) -> None: - """Set the incident neutron or X-ray wavelength. - - Args: - value (float): Incident neutron or X-ray wavelength (Å). - """ self._setup_wavelength.value = value @@ -106,18 +102,14 @@ def __init__(self) -> None: @property def calib_twotheta_offset(self) -> Parameter: - """Instrument misalignment offset. + """Instrument misalignment offset (deg). - Returns: - Parameter: Instrument misalignment offset (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._calib_twotheta_offset @calib_twotheta_offset.setter def calib_twotheta_offset(self, value: float) -> None: - """Set the instrument misalignment offset. - - Args: - value (float): Instrument misalignment offset (deg). - """ self._calib_twotheta_offset.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index 84cce720..ce9563ae 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -100,90 +100,70 @@ def __init__(self) -> None: @property def setup_twotheta_bank(self) -> Parameter: - """Detector bank position. + """Detector bank position (deg). - Returns: - Parameter: Detector bank position (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._setup_twotheta_bank @setup_twotheta_bank.setter def setup_twotheta_bank(self, value: float) -> None: - """Set the detector bank position. - - Args: - value (float): Detector bank position (deg). - """ self._setup_twotheta_bank.value = value @property def calib_d_to_tof_offset(self) -> Parameter: - """TOF offset. + """TOF offset (µs). - Returns: - Parameter: TOF offset (µs). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._calib_d_to_tof_offset @calib_d_to_tof_offset.setter def calib_d_to_tof_offset(self, value: float) -> None: - """Set the tOF offset. - - Args: - value (float): TOF offset (µs). - """ self._calib_d_to_tof_offset.value = value @property def calib_d_to_tof_linear(self) -> Parameter: - """TOF linear conversion. + """TOF linear conversion (µs/Å). - Returns: - Parameter: TOF linear conversion (µs/Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._calib_d_to_tof_linear @calib_d_to_tof_linear.setter def calib_d_to_tof_linear(self, value: float) -> None: - """Set the tOF linear conversion. - - Args: - value (float): TOF linear conversion (µs/Å). - """ self._calib_d_to_tof_linear.value = value @property def calib_d_to_tof_quad(self) -> Parameter: - """TOF quadratic correction. + """TOF quadratic correction (µs/Ų). - Returns: - Parameter: TOF quadratic correction (µs/Ų). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._calib_d_to_tof_quad @calib_d_to_tof_quad.setter def calib_d_to_tof_quad(self, value: float) -> None: - """Set the tOF quadratic correction. - - Args: - value (float): TOF quadratic correction (µs/Ų). - """ self._calib_d_to_tof_quad.value = value @property def calib_d_to_tof_recip(self) -> Parameter: - """TOF reciprocal velocity correction. + """TOF reciprocal velocity correction (µs·Å). - Returns: - Parameter: TOF reciprocal velocity correction (µs·Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._calib_d_to_tof_recip @calib_d_to_tof_recip.setter def calib_d_to_tof_recip(self, value: float) -> None: - """Set the tOF reciprocal velocity correction. - - Args: - value (float): TOF reciprocal velocity correction (µs·Å). - """ self._calib_d_to_tof_recip.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index 28d2855a..7419671f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -65,34 +65,26 @@ def __init__(self) -> None: def id(self) -> StringDescriptor: """Identifier of the linked crystal. - Returns: - StringDescriptor: Identifier of the linked crystal. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._id @id.setter def id(self, value: str) -> None: - """Set the identifier of the linked crystal. - - Args: - value (str): Identifier of the linked crystal. - """ self._id.value = value @property def scale(self) -> Parameter: """Scale factor of the linked crystal. - Returns: - Parameter: Scale factor of the linked crystal. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._scale @scale.setter def scale(self, value: float) -> None: - """Set the scale factor of the linked crystal. - - Args: - value (float): Scale factor of the linked crystal. - """ self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index d07db89e..60723ba9 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -56,36 +56,28 @@ def __init__(self): def id(self) -> StringDescriptor: """Identifier of the linked phase. - Returns: - StringDescriptor: Identifier of the linked phase. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._id @id.setter def id(self, value: str) -> None: - """Set the identifier of the linked phase. - - Args: - value (str): Identifier of the linked phase. - """ self._id.value = value @property def scale(self) -> Parameter: """Scale factor of the linked phase. - Returns: - Parameter: Scale factor of the linked phase. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._scale @scale.setter def scale(self, value: float) -> None: - """Set the scale factor of the linked phase. - - Args: - value (float): Scale factor of the linked phase. - """ self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 4a1f3179..6ec1c8ee 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -78,102 +78,72 @@ def __init__(self): @property def broad_gauss_u(self) -> Parameter: - """Gaussian broadening coefficient (dependent on sample size and - instrument resolution). + """Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). - Returns: - Parameter: Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_gauss_u @broad_gauss_u.setter def broad_gauss_u(self, value: float) -> None: - """Set the gaussian broadening coefficient (dependent on sample - size and instrument resolution). - - Args: - value (float): Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). - """ self._broad_gauss_u.value = value @property def broad_gauss_v(self) -> Parameter: - """Gaussian broadening coefficient (instrumental broadening - contribution). + """Gaussian broadening coefficient (instrumental broadening contribution) (deg²). - Returns: - Parameter: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_gauss_v @broad_gauss_v.setter def broad_gauss_v(self, value: float) -> None: - """Set the gaussian broadening coefficient (instrumental - broadening contribution). - - Args: - value (float): Gaussian broadening coefficient (instrumental broadening contribution) (deg²). - """ self._broad_gauss_v.value = value @property def broad_gauss_w(self) -> Parameter: - """Gaussian broadening coefficient (instrumental broadening - contribution). + """Gaussian broadening coefficient (instrumental broadening contribution) (deg²). - Returns: - Parameter: Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_gauss_w @broad_gauss_w.setter def broad_gauss_w(self, value: float) -> None: - """Set the gaussian broadening coefficient (instrumental - broadening contribution). - - Args: - value (float): Gaussian broadening coefficient (instrumental broadening contribution) (deg²). - """ self._broad_gauss_w.value = value @property def broad_lorentz_x(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on sample strain - effects). + """Lorentzian broadening coefficient (dependent on sample strain effects) (deg). - Returns: - Parameter: Lorentzian broadening coefficient (dependent on sample strain effects) (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_lorentz_x @broad_lorentz_x.setter def broad_lorentz_x(self, value: float) -> None: - """Set the lorentzian broadening coefficient (dependent on - sample strain effects). - - Args: - value (float): Lorentzian broadening coefficient (dependent on sample strain effects) (deg). - """ self._broad_lorentz_x.value = value @property def broad_lorentz_y(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on - microstructural defects and strain). + """Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). - Returns: - Parameter: Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_lorentz_y @broad_lorentz_y.setter def broad_lorentz_y(self, value: float) -> None: - """Set the lorentzian broadening coefficient (dependent on - microstructural defects and strain). - - Args: - value (float): Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). - """ self._broad_lorentz_y.value = value @@ -232,72 +202,56 @@ def __init__(self): def asym_empir_1(self) -> Parameter: """Empirical asymmetry coefficient p1. - Returns: - Parameter: Empirical asymmetry coefficient p1. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_empir_1 @asym_empir_1.setter def asym_empir_1(self, value: float) -> None: - """Set the empirical asymmetry coefficient p1. - - Args: - value (float): Empirical asymmetry coefficient p1. - """ self._asym_empir_1.value = value @property def asym_empir_2(self) -> Parameter: """Empirical asymmetry coefficient p2. - Returns: - Parameter: Empirical asymmetry coefficient p2. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_empir_2 @asym_empir_2.setter def asym_empir_2(self, value: float) -> None: - """Set the empirical asymmetry coefficient p2. - - Args: - value (float): Empirical asymmetry coefficient p2. - """ self._asym_empir_2.value = value @property def asym_empir_3(self) -> Parameter: """Empirical asymmetry coefficient p3. - Returns: - Parameter: Empirical asymmetry coefficient p3. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_empir_3 @asym_empir_3.setter def asym_empir_3(self, value: float) -> None: - """Set the empirical asymmetry coefficient p3. - - Args: - value (float): Empirical asymmetry coefficient p3. - """ self._asym_empir_3.value = value @property def asym_empir_4(self) -> Parameter: """Empirical asymmetry coefficient p4. - Returns: - Parameter: Empirical asymmetry coefficient p4. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_empir_4 @asym_empir_4.setter def asym_empir_4(self, value: float) -> None: - """Set the empirical asymmetry coefficient p4. - - Args: - value (float): Empirical asymmetry coefficient p4. - """ self._asym_empir_4.value = value @@ -336,34 +290,26 @@ def __init__(self): def asym_fcj_1(self) -> Parameter: """Finger-Cox-Jephcoat asymmetry parameter 1. - Returns: - Parameter: Finger-Cox-Jephcoat asymmetry parameter 1. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_fcj_1 @asym_fcj_1.setter def asym_fcj_1(self, value: float) -> None: - """Set the finger-Cox-Jephcoat asymmetry parameter 1. - - Args: - value (float): Finger-Cox-Jephcoat asymmetry parameter 1. - """ self._asym_fcj_1.value = value @property def asym_fcj_2(self) -> Parameter: """Finger-Cox-Jephcoat asymmetry parameter 2. - Returns: - Parameter: Finger-Cox-Jephcoat asymmetry parameter 2. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_fcj_2 @asym_fcj_2.setter def asym_fcj_2(self, value: float) -> None: - """Set the finger-Cox-Jephcoat asymmetry parameter 2. - - Args: - value (float): Finger-Cox-Jephcoat asymmetry parameter 2. - """ self._asym_fcj_2.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index b872ae99..1e7468e4 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -110,158 +110,114 @@ def __init__(self): @property def broad_gauss_sigma_0(self) -> Parameter: - """Gaussian broadening coefficient (instrumental resolution). + """Gaussian broadening coefficient (instrumental resolution) (µs²). - Returns: - Parameter: Gaussian broadening coefficient (instrumental resolution) (µs²). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_gauss_sigma_0 @broad_gauss_sigma_0.setter def broad_gauss_sigma_0(self, value: float) -> None: - """Set the gaussian broadening coefficient (instrumental - resolution). - - Args: - value (float): Gaussian broadening coefficient (instrumental resolution) (µs²). - """ self._broad_gauss_sigma_0.value = value @property def broad_gauss_sigma_1(self) -> Parameter: - """Gaussian broadening coefficient (dependent on d-spacing). + """Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). - Returns: - Parameter: Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_gauss_sigma_1 @broad_gauss_sigma_1.setter def broad_gauss_sigma_1(self, value: float) -> None: - """Set the gaussian broadening coefficient (dependent on - d-spacing). - - Args: - value (float): Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). - """ self._broad_gauss_sigma_1.value = value @property def broad_gauss_sigma_2(self) -> Parameter: - """Gaussian broadening coefficient (instrument-dependent term). + """Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). - Returns: - Parameter: Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_gauss_sigma_2 @broad_gauss_sigma_2.setter def broad_gauss_sigma_2(self, value: float) -> None: - """Set the gaussian broadening coefficient (instrument-dependent - term). - - Args: - value (float): Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). - """ self._broad_gauss_sigma_2.value = value @property def broad_lorentz_gamma_0(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on microstrain - effects). + """Lorentzian broadening coefficient (dependent on microstrain effects) (µs). - Returns: - Parameter: Lorentzian broadening coefficient (dependent on microstrain effects) (µs). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_lorentz_gamma_0 @broad_lorentz_gamma_0.setter def broad_lorentz_gamma_0(self, value: float) -> None: - """Set the lorentzian broadening coefficient (dependent on - microstrain effects). - - Args: - value (float): Lorentzian broadening coefficient (dependent on microstrain effects) (µs). - """ self._broad_lorentz_gamma_0.value = value @property def broad_lorentz_gamma_1(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on d-spacing). + """Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). - Returns: - Parameter: Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_lorentz_gamma_1 @broad_lorentz_gamma_1.setter def broad_lorentz_gamma_1(self, value: float) -> None: - """Set the lorentzian broadening coefficient (dependent on - d-spacing). - - Args: - value (float): Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). - """ self._broad_lorentz_gamma_1.value = value @property def broad_lorentz_gamma_2(self) -> Parameter: - """Lorentzian broadening coefficient (instrument-dependent - term). + """Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). - Returns: - Parameter: Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_lorentz_gamma_2 @broad_lorentz_gamma_2.setter def broad_lorentz_gamma_2(self, value: float) -> None: - """Set the lorentzian broadening coefficient (instrument- - dependent term). - - Args: - value (float): Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). - """ self._broad_lorentz_gamma_2.value = value @property def broad_mix_beta_0(self) -> Parameter: - """Mixing parameter. Defines the ratio of Gaussian to Lorentzian - contributions in TOF profiles. + """Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). - Returns: - Parameter: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_mix_beta_0 @broad_mix_beta_0.setter def broad_mix_beta_0(self, value: float) -> None: - """Set the mixing parameter. Defines the ratio of Gaussian to - Lorentzian contributions in TOF profiles. - - Args: - value (float): Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). - """ self._broad_mix_beta_0.value = value @property def broad_mix_beta_1(self) -> Parameter: - """Mixing parameter. Defines the ratio of Gaussian to Lorentzian - contributions in TOF profiles. + """Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). - Returns: - Parameter: Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_mix_beta_1 @broad_mix_beta_1.setter def broad_mix_beta_1(self, value: float) -> None: - """Set the mixing parameter. Defines the ratio of Gaussian to - Lorentzian contributions in TOF profiles. - - Args: - value (float): Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). - """ self._broad_mix_beta_1.value = value @@ -296,34 +252,26 @@ def __init__(self): def asym_alpha_0(self) -> Parameter: """Ikeda-Carpenter asymmetry parameter α₀. - Returns: - Parameter: Ikeda-Carpenter asymmetry parameter α₀. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_alpha_0 @asym_alpha_0.setter def asym_alpha_0(self, value: float) -> None: - """Set the ikeda-Carpenter asymmetry parameter α₀. - - Args: - value (float): Ikeda-Carpenter asymmetry parameter α₀. - """ self._asym_alpha_0.value = value @property def asym_alpha_1(self) -> Parameter: """Ikeda-Carpenter asymmetry parameter α₁. - Returns: - Parameter: Ikeda-Carpenter asymmetry parameter α₁. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._asym_alpha_1 @asym_alpha_1.setter def asym_alpha_1(self, value: float) -> None: - """Set the ikeda-Carpenter asymmetry parameter α₁. - - Args: - value (float): Ikeda-Carpenter asymmetry parameter α₁. - """ self._asym_alpha_1.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 67a052b2..94e1f202 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -90,116 +90,84 @@ def __init__(self): @property def damp_q(self) -> Parameter: - """Instrumental Q-resolution damping factor (affects high-r PDF - peak amplitude). + """Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). - Returns: - Parameter: Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._damp_q @damp_q.setter def damp_q(self, value: float) -> None: - """Set the instrumental Q-resolution damping factor (affects - high-r PDF peak amplitude). - - Args: - value (float): Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). - """ self._damp_q.value = value @property def broad_q(self) -> Parameter: - """Quadratic PDF peak broadening coefficient (thermal and model - uncertainty contribution). + """Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). - Returns: - Parameter: Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._broad_q @broad_q.setter def broad_q(self, value: float) -> None: - """Set the quadratic PDF peak broadening coefficient (thermal - and model uncertainty contribution). - - Args: - value (float): Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). - """ self._broad_q.value = value @property def cutoff_q(self) -> Parameter: - """Q-value cutoff applied to model PDF for Fourier transform - (controls real-space resolution). + """Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). - Returns: - Parameter: Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._cutoff_q @cutoff_q.setter def cutoff_q(self, value: float) -> None: - """Set the q-value cutoff applied to model PDF for Fourier - transform (controls real-space resolution). - - Args: - value (float): Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). - """ self._cutoff_q.value = value @property def sharp_delta_1(self) -> Parameter: - """PDF peak sharpening coefficient (1/r dependence). + """PDF peak sharpening coefficient (1/r dependence) (Å). - Returns: - Parameter: PDF peak sharpening coefficient (1/r dependence) (Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._sharp_delta_1 @sharp_delta_1.setter def sharp_delta_1(self, value: float) -> None: - """Set the pDF peak sharpening coefficient (1/r dependence). - - Args: - value (float): PDF peak sharpening coefficient (1/r dependence) (Å). - """ self._sharp_delta_1.value = value @property def sharp_delta_2(self) -> Parameter: - """PDF peak sharpening coefficient (1/r² dependence). + """PDF peak sharpening coefficient (1/r² dependence) (Ų). - Returns: - Parameter: PDF peak sharpening coefficient (1/r² dependence) (Ų). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._sharp_delta_2 @sharp_delta_2.setter def sharp_delta_2(self, value: float) -> None: - """Set the pDF peak sharpening coefficient (1/r² dependence). - - Args: - value (float): PDF peak sharpening coefficient (1/r² dependence) (Ų). - """ self._sharp_delta_2.value = value @property def damp_particle_diameter(self) -> Parameter: - """Particle diameter for spherical envelope damping correction - in PDF. + """Particle diameter for spherical envelope damping correction in PDF (Å). - Returns: - Parameter: Particle diameter for spherical envelope damping correction in PDF (Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._damp_particle_diameter @damp_particle_diameter.setter def damp_particle_diameter(self, value: float) -> None: - """Set the particle diameter for spherical envelope damping - correction in PDF. - - Args: - value (float): Particle diameter for spherical envelope damping correction in PDF (Å). - """ self._damp_particle_diameter.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index b17960dc..ef9dd0aa 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -176,176 +176,126 @@ def _wyckoff_letter_default_value(self) -> str: def label(self) -> StringDescriptor: """Unique identifier for the atom site. - Returns: - StringDescriptor: Unique identifier for the atom site. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._label @label.setter def label(self, value: str) -> None: - """Set the unique identifier for the atom site. - - Args: - value (str): Unique identifier for the atom site. - """ self._label.value = value @property def type_symbol(self) -> StringDescriptor: """Chemical symbol of the atom at this site. - Returns: - StringDescriptor: Chemical symbol of the atom at this site. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._type_symbol @type_symbol.setter def type_symbol(self, value: str) -> None: - """Set the chemical symbol of the atom at this site. - - Args: - value (str): Chemical symbol of the atom at this site. - """ self._type_symbol.value = value @property def adp_type(self) -> StringDescriptor: - """Type of atomic displacement parameter (ADP) used (e.g., Biso, - Uiso, Uani, Bani). + """Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). - Returns: - StringDescriptor: Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._adp_type @adp_type.setter def adp_type(self, value: str) -> None: - """Set the type of atomic displacement parameter (ADP) used - (e.g., Biso, Uiso, Uani, Bani). - - Args: - value (str): Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). - """ self._adp_type.value = value @property def wyckoff_letter(self) -> StringDescriptor: - """Wyckoff letter indicating the symmetry of the atom site - within the space group. + """Wyckoff letter indicating the symmetry of the atom site within the space group. - Returns: - StringDescriptor: Wyckoff letter indicating the symmetry of the atom site within the space group. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._wyckoff_letter @wyckoff_letter.setter def wyckoff_letter(self, value: str) -> None: - """Set the wyckoff letter indicating the symmetry of the atom - site within the space group. - - Args: - value (str): Wyckoff letter indicating the symmetry of the atom site within the space group. - """ self._wyckoff_letter.value = value @property def fract_x(self) -> Parameter: - """Fractional x-coordinate of the atom site within the unit - cell. + """Fractional x-coordinate of the atom site within the unit cell. - Returns: - Parameter: Fractional x-coordinate of the atom site within the unit cell. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._fract_x @fract_x.setter def fract_x(self, value: float) -> None: - """Set the fractional x-coordinate of the atom site within the - unit cell. - - Args: - value (float): Fractional x-coordinate of the atom site within the unit cell. - """ self._fract_x.value = value @property def fract_y(self) -> Parameter: - """Fractional y-coordinate of the atom site within the unit - cell. + """Fractional y-coordinate of the atom site within the unit cell. - Returns: - Parameter: Fractional y-coordinate of the atom site within the unit cell. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._fract_y @fract_y.setter def fract_y(self, value: float) -> None: - """Set the fractional y-coordinate of the atom site within the - unit cell. - - Args: - value (float): Fractional y-coordinate of the atom site within the unit cell. - """ self._fract_y.value = value @property def fract_z(self) -> Parameter: - """Fractional z-coordinate of the atom site within the unit - cell. + """Fractional z-coordinate of the atom site within the unit cell. - Returns: - Parameter: Fractional z-coordinate of the atom site within the unit cell. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._fract_z @fract_z.setter def fract_z(self, value: float) -> None: - """Set the fractional z-coordinate of the atom site within the - unit cell. - - Args: - value (float): Fractional z-coordinate of the atom site within the unit cell. - """ self._fract_z.value = value @property def occupancy(self) -> Parameter: - """Occupancy of the atom site, representing the fraction of the - site occupied by the atom type. + """Occupancy of the atom site, representing the fraction of the site occupied by the atom type. - Returns: - Parameter: Occupancy of the atom site, representing the fraction of the site occupied by the atom type. + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._occupancy @occupancy.setter def occupancy(self, value: float) -> None: - """Set the occupancy of the atom site, representing the fraction - of the site occupied by the atom type. - - Args: - value (float): Occupancy of the atom site, representing the fraction of the site occupied by the atom type. - """ self._occupancy.value = value @property def b_iso(self) -> Parameter: - """Isotropic atomic displacement parameter (ADP) for the atom - site. + """Isotropic atomic displacement parameter (ADP) for the atom site (Ų). - Returns: - Parameter: Isotropic atomic displacement parameter (ADP) for the atom site (Ų). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._b_iso @b_iso.setter def b_iso(self, value: float) -> None: - """Set the isotropic atomic displacement parameter (ADP) for the - atom site. - - Args: - value (float): Isotropic atomic displacement parameter (ADP) for the atom site (Ų). - """ self._b_iso.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index f8988403..953b2a29 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -135,8 +135,9 @@ def _update( """Recalculate cell parameters after a change. Args: - called_by_minimizer (bool): Whether the update was triggered - by the fitting minimizer. Currently unused. + called_by_minimizer (bool, default=False): Whether the + update was triggered by the fitting minimizer. Currently + unused. """ del called_by_minimizer # TODO: ??? @@ -148,108 +149,84 @@ def _update( @property def length_a(self) -> Parameter: - """Length of the a axis of the unit cell. + """Length of the a axis of the unit cell (Å). - Returns: - Parameter: Length of the a axis of the unit cell (Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._length_a @length_a.setter def length_a(self, value: float) -> None: - """Set the length of the a axis of the unit cell. - - Args: - value (float): Length of the a axis of the unit cell (Å). - """ self._length_a.value = value @property def length_b(self) -> Parameter: - """Length of the b axis of the unit cell. + """Length of the b axis of the unit cell (Å). - Returns: - Parameter: Length of the b axis of the unit cell (Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._length_b @length_b.setter def length_b(self, value: float) -> None: - """Set the length of the b axis of the unit cell. - - Args: - value (float): Length of the b axis of the unit cell (Å). - """ self._length_b.value = value @property def length_c(self) -> Parameter: - """Length of the c axis of the unit cell. + """Length of the c axis of the unit cell (Å). - Returns: - Parameter: Length of the c axis of the unit cell (Å). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._length_c @length_c.setter def length_c(self, value: float) -> None: - """Set the length of the c axis of the unit cell. - - Args: - value (float): Length of the c axis of the unit cell (Å). - """ self._length_c.value = value @property def angle_alpha(self) -> Parameter: - """Angle between edges b and c. + """Angle between edges b and c (deg). - Returns: - Parameter: Angle between edges b and c (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._angle_alpha @angle_alpha.setter def angle_alpha(self, value: float) -> None: - """Set the angle between edges b and c. - - Args: - value (float): Angle between edges b and c (deg). - """ self._angle_alpha.value = value @property def angle_beta(self) -> Parameter: - """Angle between edges a and c. + """Angle between edges a and c (deg). - Returns: - Parameter: Angle between edges a and c (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._angle_beta @angle_beta.setter def angle_beta(self, value: float) -> None: - """Set the angle between edges a and c. - - Args: - value (float): Angle between edges a and c (deg). - """ self._angle_beta.value = value @property def angle_gamma(self) -> Parameter: - """Angle between edges a and b. + """Angle between edges a and b (deg). - Returns: - Parameter: Angle between edges a and b (deg). + Reading this property returns the underlying + ``Parameter` object. + Assigning to it updates the parameter value. """ return self._angle_gamma @angle_gamma.setter def angle_gamma(self, value: float) -> None: - """Set the angle between edges a and b. - - Args: - value (float): Angle between edges a and b (deg). - """ self._angle_gamma.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 11fe544b..09ab11e6 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -130,18 +130,14 @@ def _it_coordinate_system_code_default_value(self) -> str: def name_h_m(self) -> StringDescriptor: """Hermann-Mauguin symbol of the space group. - Returns: - StringDescriptor: Hermann-Mauguin symbol of the space group. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._name_h_m @name_h_m.setter def name_h_m(self, value: str) -> None: - """Set the hermann-Mauguin symbol of the space group. - - Args: - value (str): Hermann-Mauguin symbol of the space group. - """ self._name_h_m.value = value self._reset_it_coordinate_system_code() @@ -149,16 +145,12 @@ def name_h_m(self, value: str) -> None: def it_coordinate_system_code(self) -> StringDescriptor: """A qualifier identifying which setting in IT is used. - Returns: - StringDescriptor: A qualifier identifying which setting in IT is used. + Reading this property returns the underlying + ``StringDescriptor` object. + Assigning to it updates the parameter value. """ return self._it_coordinate_system_code @it_coordinate_system_code.setter def it_coordinate_system_code(self, value: str) -> None: - """Set the a qualifier identifying which setting in IT is used. - - Args: - value (str): A qualifier identifying which setting in IT is used. - """ self._it_coordinate_system_code.value = value diff --git a/tools/param_consistency.py b/tools/param_consistency.py index 6d74bf33..10c15398 100644 --- a/tools/param_consistency.py +++ b/tools/param_consistency.py @@ -1,48 +1,63 @@ -"""Check and fix consistency between Parameter/Descriptor definitions -and their public property docstrings and type annotations. +"""Check and fix consistency between Parameter/Descriptor +definitions and their public property docstrings and type +annotations. -Usage: - python param_consistency.py --check # validate (exit code 0/1) - python param_consistency.py --fix # auto-fix docstrings and type hints - python param_consistency.py src/mypackage/ --check # scan only a specific directory +Usage:: -Template (see docs/architecture/architecture.md §9.8 for the full spec) ------------------------------------------------------------------------ -Given ``description='Length of the a axis of the unit cell.'``, -``units='Å'``, and type ``Parameter``: + python param_consistency.py --check + python param_consistency.py --fix + python param_consistency.py src/mypackage/ --check -Getter:: +Template (see architecture.md §9.8 for the full spec) +------------------------------------------------------ +Given ``description='Length of the a axis of the unit +cell.'``, ``units='Å'``, and type ``Parameter``: + +Writable:: @property def length_a(self) -> Parameter: - \"""Length of the a axis of the unit cell. + \"""Length of the a axis of the unit cell (Å). - Returns: - Parameter: Length of the a axis of the unit cell (Å). + Reading this property returns the underlying + ``Parameter`` object. Assigning to it updates + the parameter value. \""" return self._length_a -Setter:: - @length_a.setter def length_a(self, value: float) -> None: - \"""Set the length of the a axis of the unit cell. + self._length_a.value = value - Args: - value (float): Length of the a axis of the unit cell (Å). +Read-only:: + + @property + def length_a(self) -> Parameter: + \"""Length of the a axis of the unit cell (Å). + + Reading this property returns the underlying + ``Parameter`` object. \""" - self._length_a.value = value + return self._length_a Rules: -- ``{desc}`` = description without trailing period (single source of truth). -- ``{units}`` = units string; omit ``({units})`` when absent or empty. + +- ``{desc}`` = description without trailing period + (single source of truth). +- ``{units}`` = units string; omit ``({units})`` when + absent or empty. +- Getter summary: ``{desc} ({units}).`` or ``{desc}.`` +- Getter body mentions the descriptor class and, for + writable properties, notes that assignment updates + the value. +- Setter has **no** docstring. - Getter return annotation: the descriptor class name. -- Setter value annotation: ``float`` for numeric, ``str`` for string. +- Setter value annotation: ``float`` for numeric, + ``str`` for string. - Setter return annotation: ``None``. -- Setter ``Args`` uses the **actual parameter name** (conventionally - ``value``). -Exit code 0 when all checks pass (or fix succeeds), 1 otherwise. +Exit code 0 when all checks pass (or fix succeeds), +1 otherwise. """ from __future__ import annotations @@ -54,14 +69,19 @@ def length_a(self, value: float) -> None: from dataclasses import field from pathlib import Path -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # Constants -# --------------------------------------------------------------------------- +# --------------------------------------------------------- -_SRC_ROOT = Path(__file__).resolve().parents[1] / 'src' / 'easydiffraction' +_SRC_ROOT = ( + Path(__file__).resolve().parents[1] + / 'src' + / 'easydiffraction' +) -_DESCRIPTOR_TYPES = frozenset({'Parameter', 'NumericDescriptor', 'StringDescriptor'}) -_NUMERIC_TYPES = frozenset({'Parameter', 'NumericDescriptor'}) +_DESCRIPTOR_TYPES = frozenset( + {'Parameter', 'NumericDescriptor', 'StringDescriptor'} +) # Canonical setter value annotation per descriptor family. _SETTER_ANN: dict[str, str] = { @@ -71,20 +91,20 @@ def length_a(self, value: float) -> None: } -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # Data structures -# --------------------------------------------------------------------------- +# --------------------------------------------------------- @dataclass class DescriptorInfo: - """Descriptor definition extracted from ``__init__``.""" + """Descriptor definition from ``__init__``.""" attr_name: str # e.g. '_length_a' prop_name: str # e.g. 'length_a' - type_name: str # 'Parameter' | 'NumericDescriptor' | 'StringDescriptor' - description: str # e.g. 'Length of the a axis of the unit cell.' - units: str | None # e.g. 'Å', or None / '' for unitless + type_name: str # 'Parameter' | … + description: str # e.g. 'Length of …' + units: str | None # e.g. 'Å', or None @dataclass @@ -98,9 +118,11 @@ class PropertyInfo: @dataclass class Edit: - """A source-level edit: replace ``lines[start:end]`` with *new_text*. + """A source-level edit. - When ``start == end`` the edit is an insertion before that line. + Replace ``lines[start:end]`` with *new_text*. + When ``start == end`` the edit is an insertion + before that line. """ start: int # 0-based inclusive @@ -117,13 +139,13 @@ class FileResult: edits: list[Edit] = field(default_factory=list) -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # Template helpers -# --------------------------------------------------------------------------- +# --------------------------------------------------------- def _strip_dot(s: str) -> str: - """Remove a single trailing period and surrounding whitespace.""" + """Remove trailing period and whitespace.""" s = s.rstrip() if s.endswith('.'): s = s[:-1].rstrip() @@ -134,95 +156,42 @@ def _getter_docstring( desc: str, units: str | None, type_name: str, + has_setter: bool, indent: str, ) -> str: """Build the expected getter docstring.""" d = _strip_dot(desc) - ret = f'{d} ({units}).' if units else f'{d}.' - return ( - f'{indent}"""{d}.\n' - f'\n' - f'{indent}Returns:\n' - f'{indent} {type_name}: {ret}\n' - f'{indent}"""\n' + summary = f'{d} ({units}).' if units else f'{d}.' + body = ( + f'Reading this property returns the underlying\n' + f'{indent}``{type_name}` object.\n' + f'{indent}Assigning to it updates the parameter value.' + if has_setter + else ( + f'Reading this property returns the underlying\n' + f'{indent}``{type_name}` object.' + ) ) - - -def _setter_docstring( - desc: str, - units: str | None, - param_name: str, - value_type: str, - indent: str, -) -> str: - """Build the expected setter docstring.""" - d = _strip_dot(desc) - lower = d[0].lower() + d[1:] - arg = f'{d} ({units}).' if units else f'{d}.' return ( - f'{indent}"""Set the {lower}.\n' + f'{indent}"""{summary}\n' f'\n' - f'{indent}Args:\n' - f'{indent} {param_name} ({value_type}): {arg}\n' + f'{indent}{body}\n' f'{indent}"""\n' ) -def _normalize_docstring_src(text: str) -> str: - """Normalize docstring source for comparison. - - Handles formatting transformations that must not cause false - negatives: - - * **Summary wrapping** — ``docformatter`` may wrap long summary - paragraphs; continuation lines are collapsed into one line. - * **First-character capitalisation** — ``docformatter`` capitalises - the first letter of the summary; comparison is case-insensitive. - * **Hyphen word-breaks** — wrapping at hyphens produces - ``"foo-\\nbar"``; the dangling space is collapsed. - * **Section continuation lines** — ``docformatter`` may wrap long - ``Returns:`` / ``Args:`` content lines; continuation lines are - rejoined so that wrapped and single-line forms compare equal. - """ - parts = text.split('\n\n', 1) - # -- Summary paragraph -- - summary = ' '.join(parts[0].split()).lower() - summary = summary.replace('- ', '-') - summary = summary.replace('""" ', '"""') - if len(parts) <= 1: - return summary - - # -- Structured section (Returns / Args) -- - # Join continuation lines back into their parent line so that - # wrapping differences do not cause false negatives. - section_lines = parts[1].splitlines() - merged: list[str] = [] - for raw in section_lines: - stripped = raw.strip() - # A continuation line is non-empty, doesn't start a recognised - # keyword or the closing triple-quote, and appears right after - # a content line (not a blank or section header). - is_continuation = ( - stripped - and merged - and merged[-1].strip() - and not stripped.startswith(('Returns:', 'Args:', '"""')) - and not merged[-1].strip().endswith(':') - ) - if is_continuation: - merged[-1] = merged[-1].rstrip() + ' ' + stripped - else: - merged.append(raw) - return summary + '\n\n' + '\n'.join(merged) +def _normalize(text: str) -> str: + """Collapse whitespace for comparison.""" + return ' '.join(text.split()).lower() -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # AST helpers -# --------------------------------------------------------------------------- +# --------------------------------------------------------- def _call_name(node: ast.Call) -> str | None: - """Return the simple name of a Call's function.""" + """Return the simple name of a Call's func.""" if isinstance(node.func, ast.Name): return node.func.id if isinstance(node.func, ast.Attribute): @@ -230,8 +199,11 @@ def _call_name(node: ast.Call) -> str | None: return None -def _kwarg_str(call: ast.Call, name: str) -> str | None: - """Extract a string-valued keyword argument from *call*.""" +def _kwarg_str( + call: ast.Call, + name: str, +) -> str | None: + """Extract a string keyword argument.""" for kw in call.keywords: if ( kw.arg == name @@ -243,24 +215,34 @@ def _kwarg_str(call: ast.Call, name: str) -> str | None: def _ann_str(ann: ast.expr | None) -> str | None: - """Return the annotation as a source-level string.""" + """Return annotation as a source string.""" if ann is None: return None if isinstance(ann, ast.Name): return ann.id - if isinstance(ann, ast.Constant) and isinstance(ann.value, str): + if isinstance(ann, ast.Constant) and isinstance( + ann.value, str + ): return ann.value # forward reference return ast.unparse(ann) -def _body_indent(func: ast.FunctionDef, lines: list[str]) -> str: - """Compute the indentation string for the function body.""" +def _body_indent( + func: ast.FunctionDef, + lines: list[str], +) -> str: + """Compute the indentation for the body.""" def_line = lines[func.lineno - 1] - return ' ' * (len(def_line) - len(def_line.lstrip()) + 4) + return ' ' * ( + len(def_line) - len(def_line.lstrip()) + 4 + ) -def _def_line_range(func: ast.FunctionDef, lines: list[str]) -> tuple[int, int]: - """Return 0-based ``[start, end)`` of the ``def`` statement.""" +def _def_line_range( + func: ast.FunctionDef, + lines: list[str], +) -> tuple[int, int]: + """Return 0-based [start, end) of the def.""" start = func.lineno - 1 for i in range(start, min(start + 10, len(lines))): if lines[i].rstrip().endswith(':'): @@ -270,8 +252,10 @@ def _def_line_range(func: ast.FunctionDef, lines: list[str]) -> tuple[int, int]: return start, start + 1 -def _docstring_range(func: ast.FunctionDef) -> tuple[str | None, int, int]: - """Return ``(text, start_0, end_exclusive_0)`` of the docstring.""" +def _docstring_range( + func: ast.FunctionDef, +) -> tuple[str | None, int, int]: + """Return (text, start_0, end_exclusive_0).""" if not func.body: return None, -1, -1 first = func.body[0] @@ -280,33 +264,49 @@ def _docstring_range(func: ast.FunctionDef) -> tuple[str | None, int, int]: and isinstance(first.value, ast.Constant) and isinstance(first.value.value, str) ): - # end_lineno is 1-based inclusive → 0-based exclusive is the same int - return first.value.value, first.lineno - 1, first.end_lineno + # end_lineno is 1-based inclusive + return ( + first.value.value, + first.lineno - 1, + first.end_lineno, + ) return None, -1, -1 -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # Extraction -# --------------------------------------------------------------------------- +# --------------------------------------------------------- -def _extract_descriptors(cls: ast.ClassDef) -> dict[str, DescriptorInfo]: - """Find ``self._xxx = DescriptorType(...)`` assignments in ``__init__``.""" +def _extract_descriptors( + cls: ast.ClassDef, +) -> dict[str, DescriptorInfo]: + """Find self._xxx = DescriptorType(...) in init.""" result: dict[str, DescriptorInfo] = {} init = next( - (n for n in cls.body if isinstance(n, ast.FunctionDef) and n.name == '__init__'), + ( + n + for n in cls.body + if isinstance(n, ast.FunctionDef) + and n.name == '__init__' + ), None, ) if init is None: return result for stmt in ast.walk(init): - # Handle both ast.Assign and ast.AnnAssign - if isinstance(stmt, ast.Assign) and len(stmt.targets) == 1: + if ( + isinstance(stmt, ast.Assign) + and len(stmt.targets) == 1 + ): target = stmt.targets[0] value = stmt.value - elif isinstance(stmt, ast.AnnAssign) and stmt.value is not None: + elif ( + isinstance(stmt, ast.AnnAssign) + and stmt.value is not None + ): target = stmt.target value = stmt.value else: @@ -334,13 +334,17 @@ def _extract_descriptors(cls: ast.ClassDef) -> dict[str, DescriptorInfo]: units = _kwarg_str(value, 'units') or None prop = target.attr.lstrip('_') - result[prop] = DescriptorInfo(target.attr, prop, name, desc_str, units) + result[prop] = DescriptorInfo( + target.attr, prop, name, desc_str, units + ) return result -def _extract_properties(cls: ast.ClassDef) -> dict[str, PropertyInfo]: - """Find property getters and their setters in *cls*.""" +def _extract_properties( + cls: ast.ClassDef, +) -> dict[str, PropertyInfo]: + """Find property getters and setters.""" result: dict[str, PropertyInfo] = {} for item in cls.body: @@ -348,8 +352,13 @@ def _extract_properties(cls: ast.ClassDef) -> dict[str, PropertyInfo]: continue for dec in item.decorator_list: # @property - if isinstance(dec, ast.Name) and dec.id == 'property': - result[item.name] = PropertyInfo(item.name, item) + if ( + isinstance(dec, ast.Name) + and dec.id == 'property' + ): + result[item.name] = PropertyInfo( + item.name, item + ) break # @xxx.setter if ( @@ -364,13 +373,13 @@ def _extract_properties(cls: ast.ClassDef) -> dict[str, PropertyInfo]: return result -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # Analysis (shared by --check and --fix) -# --------------------------------------------------------------------------- +# --------------------------------------------------------- def _analyze_file(path: Path) -> FileResult: - """Analyze one source file and return issues and proposed edits.""" + """Analyze one file, return issues and edits.""" result = FileResult(path) try: source = path.read_text(encoding='utf-8') @@ -395,7 +404,9 @@ def _analyze_file(path: Path) -> FileResult: if prop_name not in descriptors: continue desc = descriptors[prop_name] - _analyze_property(node.name, prop, desc, lines, result) + _analyze_property( + node.name, prop, desc, lines, result + ) return result @@ -407,91 +418,142 @@ def _analyze_property( lines: list[str], result: FileResult, ) -> None: - """Check a single property against the template, accumulating issues and edits.""" + """Check one property against the template.""" loc = f'{cls_name}.{prop.name}' indent = _body_indent(prop.getter, lines) + has_setter = prop.setter is not None # --- Getter return annotation --- actual_ret = _ann_str(prop.getter.returns) expected_ret = desc.type_name if actual_ret != expected_ret: result.issues.append( - f'{loc}: getter annotation -> {actual_ret} (expected {expected_ret})' + f'{loc}: getter annotation ' + f'-> {actual_ret} (expected {expected_ret})' ) ds, de = _def_line_range(prop.getter, lines) - def_indent = lines[ds][: len(lines[ds]) - len(lines[ds].lstrip())] - new_def = f'{def_indent}def {prop.name}(self) -> {expected_ret}:\n' + def_indent = lines[ds][ + : len(lines[ds]) - len(lines[ds].lstrip()) + ] + new_def = ( + f'{def_indent}def {prop.name}' + f'(self) -> {expected_ret}:\n' + ) result.edits.append(Edit(ds, de, new_def)) # --- Getter docstring --- - expected_doc = _getter_docstring(desc.description, desc.units, desc.type_name, indent) - actual_doc_text, doc_s, doc_e = _docstring_range(prop.getter) + expected_doc = _getter_docstring( + desc.description, + desc.units, + desc.type_name, + has_setter, + indent, + ) + actual_doc_text, doc_s, doc_e = _docstring_range( + prop.getter + ) if actual_doc_text is None: - result.issues.append(f'{loc}: getter missing docstring') + result.issues.append( + f'{loc}: getter missing docstring' + ) _, def_end = _def_line_range(prop.getter, lines) - result.edits.append(Edit(def_end, def_end, expected_doc)) + result.edits.append( + Edit(def_end, def_end, expected_doc) + ) else: actual_src = ''.join(lines[doc_s:doc_e]) - if _normalize_docstring_src(actual_src) != _normalize_docstring_src(expected_doc): - result.issues.append(f'{loc}: getter docstring does not match template') - result.edits.append(Edit(doc_s, doc_e, expected_doc)) + if _normalize(actual_src) != _normalize( + expected_doc + ): + result.issues.append( + f'{loc}: getter docstring ' + 'does not match template' + ) + result.edits.append( + Edit(doc_s, doc_e, expected_doc) + ) # --- Setter --- if prop.setter is None: return + # Setter def-line annotations setter_args = prop.setter.args.args - setter_param = setter_args[1].arg if len(setter_args) >= 2 else 'value' + setter_param = ( + setter_args[1].arg + if len(setter_args) >= 2 + else 'value' + ) expected_ann = _SETTER_ANN[desc.type_name] - # Setter def-line annotations (value type + return type) actual_val_ann = None - if len(setter_args) >= 2 and setter_args[1].annotation: - actual_val_ann = _ann_str(setter_args[1].annotation) + if ( + len(setter_args) >= 2 + and setter_args[1].annotation + ): + actual_val_ann = _ann_str( + setter_args[1].annotation + ) actual_ret_ann = _ann_str(prop.setter.returns) - if actual_val_ann != expected_ann or actual_ret_ann != 'None': + if ( + actual_val_ann != expected_ann + or actual_ret_ann != 'None' + ): parts: list[str] = [] if actual_val_ann != expected_ann: - parts.append(f'value: {actual_val_ann} (expected {expected_ann})') + parts.append( + f'value: {actual_val_ann} ' + f'(expected {expected_ann})' + ) if actual_ret_ann != 'None': - parts.append(f'return: {actual_ret_ann} (expected None)') - result.issues.append(f'{loc}: setter annotation — {", ".join(parts)}') + parts.append( + f'return: {actual_ret_ann} ' + '(expected None)' + ) + result.issues.append( + f'{loc}: setter annotation ' + f'— {", ".join(parts)}' + ) ds, de = _def_line_range(prop.setter, lines) - def_indent = lines[ds][: len(lines[ds]) - len(lines[ds].lstrip())] - new_def = f'{def_indent}def {prop.name}(self, {setter_param}: {expected_ann}) -> None:\n' + def_indent = lines[ds][ + : len(lines[ds]) - len(lines[ds].lstrip()) + ] + new_def = ( + f'{def_indent}def {prop.name}' + f'(self, {setter_param}: ' + f'{expected_ann}) -> None:\n' + ) result.edits.append(Edit(ds, de, new_def)) - # Setter docstring - expected_setter_doc = _setter_docstring( - desc.description, desc.units, setter_param, expected_ann, indent + # Setter docstring — should not exist + setter_doc_text, sd_s, sd_e = _docstring_range( + prop.setter ) - actual_setter_doc, sd_s, sd_e = _docstring_range(prop.setter) - - if actual_setter_doc is None: - result.issues.append(f'{loc}: setter missing docstring') - _, def_end = _def_line_range(prop.setter, lines) - result.edits.append(Edit(def_end, def_end, expected_setter_doc)) - else: - actual_setter_src = ''.join(lines[sd_s:sd_e]) - if _normalize_docstring_src(actual_setter_src) != _normalize_docstring_src( - expected_setter_doc - ): - result.issues.append(f'{loc}: setter docstring does not match template') - result.edits.append(Edit(sd_s, sd_e, expected_setter_doc)) + if setter_doc_text is not None: + result.issues.append( + f'{loc}: setter has docstring ' + '(should have none)' + ) + result.edits.append(Edit(sd_s, sd_e, '')) -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # Apply edits -# --------------------------------------------------------------------------- +# --------------------------------------------------------- -def _apply_edits(lines: list[str], edits: list[Edit]) -> list[str]: +def _apply_edits( + lines: list[str], + edits: list[Edit], +) -> list[str]: """Apply edits bottom-up to preserve line numbers.""" - sorted_edits = sorted(edits, key=lambda e: e.start, reverse=True) + sorted_edits = sorted( + edits, key=lambda e: e.start, reverse=True + ) result = list(lines) for edit in sorted_edits: new_lines = edit.new_text.splitlines(keepends=True) @@ -499,16 +561,17 @@ def _apply_edits(lines: list[str], edits: list[Edit]) -> list[str]: return result -# --------------------------------------------------------------------------- +# --------------------------------------------------------- # Entry point -# --------------------------------------------------------------------------- +# --------------------------------------------------------- def _collect_py_files(paths: list[str]) -> list[Path]: - """Resolve *paths* to a sorted list of ``.py`` files. + """Resolve paths to a sorted list of .py files. - Each entry can be a directory (recursively globbed) or a single - ``.py`` file. When *paths* is empty, defaults to ``_SRC_ROOT``. + Each entry can be a directory (recursively globbed) + or a single .py file. When *paths* is empty, + defaults to ``_SRC_ROOT``. """ if not paths: return sorted(_SRC_ROOT.rglob('*.py')) @@ -526,12 +589,18 @@ def _collect_py_files(paths: list[str]) -> list[Path]: def main() -> int: """Run param-consistency check or fix.""" parser = argparse.ArgumentParser( - description='Parameter / property consistency: docstrings and type hints.', + description=( + 'Parameter / property consistency: ' + 'docstrings and type hints.' + ), ) parser.add_argument( 'paths', nargs='*', - help='Directories or .py files to scan (default: src/easydiffraction/)', + help=( + 'Directories or .py files to scan ' + '(default: src/easydiffraction/)' + ), ) group = parser.add_mutually_exclusive_group() group.add_argument( @@ -542,7 +611,7 @@ def main() -> int: group.add_argument( '--fix', action='store_true', - help='Auto-fix docstrings and type hints in-place', + help='Auto-fix docstrings and type hints', ) args = parser.parse_args() @@ -563,13 +632,21 @@ def main() -> int: rel = path if args.fix: - source_lines = path.read_text(encoding='utf-8').splitlines(keepends=True) - fixed_lines = _apply_edits(source_lines, result.edits) - path.write_text(''.join(fixed_lines), encoding='utf-8') + source_lines = path.read_text( + encoding='utf-8' + ).splitlines(keepends=True) + fixed_lines = _apply_edits( + source_lines, result.edits + ) + path.write_text( + ''.join(fixed_lines), encoding='utf-8' + ) count = len(result.issues) total_fixed += count files_touched += 1 - print(f'📝 {rel}: fixed {count} issue(s)') + print( + f'📝 {rel}: fixed {count} issue(s)' + ) else: for issue in result.issues: print(f' ❌ {rel}: {issue}') @@ -578,10 +655,16 @@ def main() -> int: # Summary print() if args.fix: - print(f'✅ Fixed {total_fixed} issue(s) in {files_touched} file(s).') + print( + f'✅ Fixed {total_fixed} issue(s) ' + f'in {files_touched} file(s).' + ) return 0 if total_issues: - print(f'❌ {total_issues} consistency issue(s) found.') + print( + f'❌ {total_issues} consistency ' + 'issue(s) found.' + ) return 1 print('✅ All properties match the template.') return 0 @@ -589,7 +672,3 @@ def main() -> int: if __name__ == '__main__': sys.exit(main()) - - - - From 23dc88dd5fe7c1fb2e7594a2c9398d43a8f33d21 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 10:42:10 +0100 Subject: [PATCH 18/48] Wrap long property docstrings to satisfy max-doc-length --- tools/param_consistency.py | 64 +++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/tools/param_consistency.py b/tools/param_consistency.py index 10c15398..d59fb304 100644 --- a/tools/param_consistency.py +++ b/tools/param_consistency.py @@ -65,6 +65,7 @@ def length_a(self) -> Parameter: import argparse import ast import sys +import textwrap from dataclasses import dataclass from dataclasses import field from pathlib import Path @@ -79,6 +80,9 @@ def length_a(self) -> Parameter: / 'easydiffraction' ) +# Must match [tool.ruff.lint.pycodestyle] max-doc-length. +_MAX_DOC_LENGTH = 72 + _DESCRIPTOR_TYPES = frozenset( {'Parameter', 'NumericDescriptor', 'StringDescriptor'} ) @@ -162,22 +166,39 @@ def _getter_docstring( """Build the expected getter docstring.""" d = _strip_dot(desc) summary = f'{d} ({units}).' if units else f'{d}.' - body = ( - f'Reading this property returns the underlying\n' - f'{indent}``{type_name}` object.\n' - f'{indent}Assigning to it updates the parameter value.' - if has_setter - else ( - f'Reading this property returns the underlying\n' - f'{indent}``{type_name}` object.' + summary_lines = textwrap.wrap( + summary, + width=_MAX_DOC_LENGTH, + initial_indent=f'{indent}"""', + subsequent_indent=indent, + ) + + if has_setter: + body_text = ( + 'Reading this property returns the ' + f'underlying ``{type_name}`` object. ' + 'Assigning to it updates the ' + 'parameter value.' + ) + else: + body_text = ( + 'Reading this property returns the ' + f'underlying ``{type_name}`` object.' ) + body_lines = textwrap.wrap( + body_text, + width=_MAX_DOC_LENGTH, + initial_indent=indent, + subsequent_indent=indent, ) - return ( - f'{indent}"""{summary}\n' - f'\n' - f'{indent}{body}\n' - f'{indent}"""\n' + + parts = ( + summary_lines + + [''] + + body_lines + + [f'{indent}"""'] ) + return '\n'.join(parts) + '\n' def _normalize(text: str) -> str: @@ -463,9 +484,14 @@ def _analyze_property( ) else: actual_src = ''.join(lines[doc_s:doc_e]) - if _normalize(actual_src) != _normalize( + content_ok = _normalize(actual_src) == _normalize( expected_doc - ): + ) + lines_ok = all( + len(line.rstrip('\n')) <= _MAX_DOC_LENGTH + for line in lines[doc_s:doc_e] + ) + if not content_ok: result.issues.append( f'{loc}: getter docstring ' 'does not match template' @@ -473,6 +499,14 @@ def _analyze_property( result.edits.append( Edit(doc_s, doc_e, expected_doc) ) + elif not lines_ok: + result.issues.append( + f'{loc}: getter docstring ' + 'has lines exceeding max-doc-length' + ) + result.edits.append( + Edit(doc_s, doc_e, expected_doc) + ) # --- Setter --- if prop.setter is None: From f59363ad1a3e5d7de1cbf92ee1dbae59dd1c806c Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 12:52:41 +0100 Subject: [PATCH 19/48] Switch from docformatter to format-docstring --- .github/copilot-instructions.md | 2 +- docs/mkdocs.yml | 2 +- pixi.lock | 81 +++--- pixi.toml | 14 +- pyproject.toml | 34 ++- src/easydiffraction/analysis/analysis.py | 66 +++-- .../analysis/calculators/base.py | 25 +- .../analysis/calculators/crysfml.py | 52 ++-- .../analysis/calculators/cryspy.py | 71 +++--- .../analysis/calculators/factory.py | 4 +- .../analysis/categories/aliases/default.py | 15 +- .../categories/constraints/default.py | 13 +- .../analysis/categories/fit_mode/fit_mode.py | 4 +- .../joint_fit_experiments/default.py | 13 +- .../analysis/fit_helpers/metrics.py | 51 ++-- .../analysis/fit_helpers/reporting.py | 36 ++- .../analysis/fit_helpers/tracking.py | 20 +- src/easydiffraction/analysis/fitting.py | 52 ++-- .../analysis/minimizers/base.py | 38 ++- .../analysis/minimizers/dfols.py | 12 +- .../analysis/minimizers/lmfit.py | 38 ++- src/easydiffraction/core/category.py | 10 +- src/easydiffraction/core/collection.py | 11 +- src/easydiffraction/core/datablock.py | 11 +- src/easydiffraction/core/factory.py | 55 ++--- src/easydiffraction/core/guard.py | 8 +- src/easydiffraction/core/metadata.py | 38 ++- src/easydiffraction/core/singleton.py | 11 +- src/easydiffraction/core/variable.py | 51 ++-- .../crystallography/crystallography.py | 20 +- .../categories/background/chebyshev.py | 13 +- .../categories/background/line_segment.py | 19 +- .../experiment/categories/data/bragg_pd.py | 29 ++- .../experiment/categories/data/bragg_sc.py | 32 +-- .../experiment/categories/data/total_pd.py | 16 +- .../categories/excluded_regions/default.py | 12 +- .../categories/experiment_type/default.py | 31 +-- .../experiment/categories/extinction/shelx.py | 10 +- .../experiment/categories/instrument/cwl.py | 10 +- .../experiment/categories/instrument/tof.py | 25 +- .../categories/linked_crystal/default.py | 9 +- .../categories/linked_phases/default.py | 9 +- .../experiment/categories/peak/cwl_mixins.py | 70 +++--- .../experiment/categories/peak/tof_mixins.py | 74 +++--- .../categories/peak/total_mixins.py | 42 ++-- .../datablocks/experiment/collection.py | 30 +-- .../datablocks/experiment/item/base.py | 52 ++-- .../datablocks/experiment/item/bragg_pd.py | 3 +- .../datablocks/experiment/item/bragg_sc.py | 8 +- .../datablocks/experiment/item/factory.py | 44 ++-- .../datablocks/experiment/item/total_pd.py | 3 +- .../categories/atom_sites/default.py | 77 +++--- .../structure/categories/cell/default.py | 36 ++- .../categories/space_group/default.py | 19 +- .../datablocks/structure/collection.py | 9 +- .../datablocks/structure/item/base.py | 24 +- .../datablocks/structure/item/factory.py | 25 +- src/easydiffraction/display/__init__.py | 10 +- src/easydiffraction/display/base.py | 12 +- .../display/plotters/__init__.py | 4 +- src/easydiffraction/display/plotters/ascii.py | 44 ++-- src/easydiffraction/display/plotters/base.py | 37 ++- .../display/plotters/plotly.py | 84 +++---- src/easydiffraction/display/plotting.py | 149 +++++------ .../display/tablers/__init__.py | 8 +- src/easydiffraction/display/tablers/base.py | 35 +-- src/easydiffraction/display/tablers/pandas.py | 45 ++-- src/easydiffraction/display/tablers/rich.py | 51 ++-- src/easydiffraction/display/tables.py | 20 +- src/easydiffraction/io/cif/serialize.py | 7 +- src/easydiffraction/summary/summary.py | 3 +- .../utils/_vendored/__init__.py | 22 +- .../_vendored/jupyter_dark_detect/__init__.py | 6 +- .../_vendored/jupyter_dark_detect/detector.py | 13 +- .../utils/_vendored/theme_detect.py | 16 +- src/easydiffraction/utils/environment.py | 21 +- src/easydiffraction/utils/logging.py | 62 ++--- src/easydiffraction/utils/utils.py | 232 +++++++----------- tools/convert_google_docstrings_to_numpy.py | 226 +++++++++++++++++ tools/param_consistency.py | 59 ++--- 80 files changed, 1314 insertions(+), 1441 deletions(-) create mode 100644 tools/convert_google_docstrings_to_numpy.py diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7dce8780..d29a15d1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -42,7 +42,7 @@ and UPPER_SNAKE_CASE for constants. - Use `from __future__ import annotations` in every module. - Type-annotate all public function signatures. -- Docstrings on all public classes and methods (Google style). +- Docstrings on all public classes and methods (numpy style). - Prefer flat over nested, explicit over clever. - Write straightforward code; do not add defensive checks for unlikely edge cases. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index aef26c01..cc71ad38 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -148,7 +148,7 @@ plugins: python: paths: ['src'] # Change 'src' to your actual sources directory options: - docstring_style: google + docstring_style: numpy group_by_category: false heading_level: 1 show_root_heading: true diff --git a/pixi.lock b/pixi.lock index 3f7ce83c..6496a69e 100644 --- a/pixi.lock +++ b/pixi.lock @@ -99,7 +99,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -110,6 +109,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -146,6 +146,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -296,7 +297,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -403,7 +403,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -414,6 +413,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -450,6 +450,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -600,7 +601,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -707,7 +707,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -718,6 +717,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -753,6 +753,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -903,7 +904,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -1002,7 +1002,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -1013,6 +1012,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -1049,6 +1049,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -1199,7 +1200,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -1316,7 +1316,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -1327,6 +1326,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -1364,6 +1364,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -1515,7 +1516,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -1621,7 +1621,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -1632,6 +1631,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -1669,6 +1669,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -1820,7 +1821,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -1926,7 +1926,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -1937,6 +1936,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -1973,6 +1973,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -2124,7 +2125,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -2222,7 +2222,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -2233,6 +2232,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -2270,6 +2270,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -2421,7 +2422,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -2539,7 +2539,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -2550,6 +2549,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -2586,6 +2586,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -2736,7 +2737,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -2843,7 +2843,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -2854,6 +2853,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -2890,6 +2890,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -3040,7 +3041,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -3147,7 +3147,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -3158,6 +3157,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -3193,6 +3193,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -3343,7 +3344,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -3442,7 +3442,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl @@ -3453,6 +3452,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl @@ -3489,6 +3489,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/60/1f6cee0c46263de1173894f0fafcb3475ded276c472c14d25e0280c18d6d/jupyter_lsp-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e1/1b/dad6fdcc658ed7af26fdf3841e7394072c9549a8b896c381ab49dd11e2d9/jupyterlab-4.5.6-py3-none-any.whl @@ -3639,7 +3640,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/5e/f1e1dd319e35e962a4e00b33150a8868b6329cc1d19fd533436ba5488f09/uncertainties-3.2.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl @@ -4853,15 +4853,6 @@ packages: - trio>=0.30 ; extra == 'trio' - wmi>=1.5.1 ; sys_platform == 'win32' and extra == 'wmi' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl - name: docformatter - version: 1.7.7 - sha256: 7af49f8a46346a77858f6651f431b882c503c2f4442c8b4524b920c863277834 - requires_dist: - - charset-normalizer>=3.0.0,<4.0.0 - - tomli>=2.0.0,<3.0.0 ; python_full_version < '3.11' and extra == 'tomli' - - untokenize>=0.1.1,<0.2.0 - requires_python: '>=3.9,<4.0' - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl name: docstring-parser-fork version: 0.0.14 @@ -4883,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty18 - sha256: d28acf83fdc1dd4d99d00fae4b1607f20fb326757dd1852dd96e2eb681cbe44d + version: 0.10.2+devdirty21 + sha256: 1bd07edc728222b4af7a1fe3486a1ccc1c874950a04196b9867479292e440d76 requires_dist: - asciichartpy - asteval @@ -4915,7 +4906,7 @@ packages: - varname - build ; extra == 'dev' - copier ; extra == 'dev' - - docformatter ; extra == 'dev' + - format-docstring ; extra == 'dev' - gitpython ; extra == 'dev' - interrogate ; extra == 'dev' - jinja2 ; extra == 'dev' @@ -5321,6 +5312,15 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.45.0 ; extra == 'all' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/33/b2/986d1220f6ee931e338d272bc1f3ec02cfe5f9b5fad84e95afdad57f1ebc/format_docstring-0.2.7-py3-none-any.whl + name: format-docstring + version: 0.2.7 + sha256: c9d50eafebe0f260e3270ca662ff3a0ed4050f64d95e352f8c5f88d9aede42d6 + requires_dist: + - click>=8.0 + - jupyter-notebook-parser>=0.1.4 + - tomli>=1.1.0 ; python_full_version < '3.11' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl name: fqdn version: 1.5.1 @@ -6263,6 +6263,11 @@ packages: - jupyter-server>=1.1.2 - importlib-metadata>=4.8.3 ; python_full_version < '3.10' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/f4/a4/61adb19f3c74b0dc0e411de4f06ebef564b1f179928f9dffcbd4b378f2ef/jupyter_notebook_parser-0.1.4-py2.py3-none-any.whl + name: jupyter-notebook-parser + version: 0.1.4 + sha256: 27b3b67cf898684e646d569f017cb27046774ad23866cb0bdf51d5f76a46476b + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl name: jupyter-server version: 2.17.0 @@ -12558,10 +12563,6 @@ packages: - python-docs-theme ; extra == 'doc' - uncertainties[arrays,doc,test] ; extra == 'all' requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/f7/46/e7cea8159199096e1df52da20a57a6665da80c37fb8aeb848a3e47442c32/untokenize-0.1.1.tar.gz - name: untokenize - version: 0.1.1 - sha256: 3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2 - pypi: https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl name: uri-template version: 1.3.0 diff --git a/pixi.toml b/pixi.toml index dd645320..95961594 100644 --- a/pixi.toml +++ b/pixi.toml @@ -104,14 +104,13 @@ test = { depends-on = ['unit-tests'] } ########### pyproject-check = 'python -m validate_pyproject pyproject.toml' +param-docstring-check = 'python tools/param_consistency.py src/ --check' docstring-lint-check = 'pydoclint --quiet src/' -docstring-format-check = 'docformatter --check src/' notebook-lint-check = 'nbqa ruff docs/docs/tutorials/' py-lint-check = 'ruff check src/ tests/ docs/docs/tutorials/' py-format-check = "ruff format --check src/ tests/ docs/docs/tutorials/" nonpy-format-check = "npx prettier --list-different --config=prettierrc.toml --ignore-unknown ." nonpy-format-check-modified = "python tools/nonpy_prettier_modified.py" -param-consistency-check = 'python tools/param_consistency.py src/ --check' check = 'pre-commit run --hook-stage manual --all-files' @@ -119,22 +118,23 @@ check = 'pre-commit run --hook-stage manual --all-files' # 🛠️ Fixes ########## -docs-format-fix = 'docformatter --in-place src/ docs/docs/tutorials/' +param-docstring-fix = 'python tools/param_consistency.py src/ --fix' +docstring-format-fix = 'format-docstring src/ docs/docs/tutorials/' notebook-lint-fix = 'nbqa ruff --fix docs/docs/tutorials/' py-lint-fix = 'ruff check --fix src/ tests/ docs/docs/tutorials/' py-format-fix = "ruff format src/ tests/ docs/docs/tutorials/" nonpy-format-fix = 'npx prettier --write --list-different --config=prettierrc.toml --ignore-unknown .' nonpy-format-fix-modified = "python tools/nonpy_prettier_modified.py --write" -param-consistency-fix = 'python tools/param_consistency.py src/ --fix' -success-message-fix = 'echo "✅ All auto-formatting steps completed successfully!"' +success-message = 'echo "✅ All auto-formatting steps completed successfully!"' fix = { depends-on = [ + 'param-docstring-fix', # ED only + 'docstring-format-fix', 'py-format-fix', - 'docs-format-fix', 'py-lint-fix', 'nonpy-format-fix', 'notebook-lint-fix', - 'success-message-fix', + 'success-message', ] } #################### diff --git a/pyproject.toml b/pyproject.toml index 661a627a..18d5e7f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ dev = [ 'jupytext', # Jupyter notebook text format support 'jupyterquiz', # Quizzes in Jupyter notebooks 'pydoclint', # Docstring linter - 'docformatter', # Docstring formatter + 'format-docstring', # Docstring formatter 'interrogate', # Docstring coverage checker 'copier', # Template management 'mike', # MkDocs: Versioned documentation support @@ -275,13 +275,9 @@ select = [ # Ignore specific rules globally ignore = [ 'COM812', # https://docs.astral.sh/ruff/rules/missing-trailing-comma/ - # The following covered by [tool.pydoclint] section below + # The following is replaced by [tool.pydoclint] 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc - # E501 applies max-line-length to ALL lines including docstrings. - # We ignore it because the ruff formatter already enforces line-length - # for code, and W505 enforces max-doc-length for docstrings. - #'E501', # https://docs.astral.sh/ruff/rules/line-too-long/ # Temporary: 'DTZ005', @@ -342,7 +338,6 @@ max-complexity = 10 max-line-length = 99 # See also `line-length` in [tool.ruff] max-doc-length = 72 - ############################# # Configuration for pydoclint ############################# @@ -358,21 +353,22 @@ max-doc-length = 72 # the parameter declarations in the code (in function's signature). [tool.pydoclint] -exclude = '\.' # Temporarily disable pydoclint until we are ready -style = "google" +#exclude = '\.' # Temporarily disable pydoclint until we are ready +style = 'numpy' check-style-mismatch = true check-arg-defaults = true allow-init-docstring = true -################################ -# Configuration for docformatter -################################ +#################################### +# Configuration for format-docstring +#################################### -# 'docformatter' -- Code formatter for docstrings -# https://docformatter.readthedocs.io/en/latest/ +# 'format-docstring' -- Code formatter for docstrings +# https://github.com/jsh9/format-docstring -[tool.docformatter] -recursive = true -wrap-summaries = 72 -wrap-descriptions = 72 -close-quotes-on-newline = true +[tool.format_docstring] +docstring_style = 'numpy' +line_length = 72 +fix_rst_backticks = true +exclude = "src/easydiffraction/utils/_vendored/jupyter_dark_detect" # ED only +verbose = 'diff' diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 4ddde133..e88b12a8 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -35,23 +35,21 @@ class Analysis: Typical usage: - - Display or filter parameters to fit. - - Select a calculator/minimizer implementation. - - Calculate patterns and run single or joint fits. - - Attributes: - project: The parent Project object. - aliases: A registry of human-friendly aliases for parameters. - constraints: Symbolic constraints between parameters. - calculator: Active calculator used for computations. - fitter: Active fitter/minimizer driver. + - Display or filter parameters to fit. - Select a + calculator/minimizer implementation. - Calculate patterns and run + single or joint fits. + + Attributes: project: The parent Project object. aliases: A + registry of human-friendly aliases for parameters. constraints: + Symbolic constraints between parameters. calculator: Active + calculator used for computations. fitter: Active + fitter/minimizer driver. """ def __init__(self, project) -> None: """Create a new Analysis instance bound to a project. - Args: - project: The project that owns models and experiments. + Args: project: The project that owns models and experiments. """ self.project = project self._aliases_type: str = AliasesFactory.default_tag() @@ -138,8 +136,7 @@ def aliases_type(self) -> str: def aliases_type(self, new_type: str) -> None: """Switch to a different aliases collection type. - Args: - new_type: Aliases tag (e.g. ``'default'``). + Args: new_type: Aliases tag (e.g. ``'default'``). """ supported_tags = AliasesFactory.supported_tags() if new_type not in supported_tags: @@ -176,8 +173,7 @@ def constraints_type(self) -> str: def constraints_type(self, new_type: str) -> None: """Switch to a different constraints collection type. - Args: - new_type: Constraints tag (e.g. ``'default'``). + Args: new_type: Constraints tag (e.g. ``'default'``). """ supported_tags = ConstraintsFactory.supported_tags() if new_type not in supported_tags: @@ -207,11 +203,10 @@ def _get_params_as_dataframe( ) -> pd.DataFrame: """Convert a list of parameters to a DataFrame. - Args: - params: List of DescriptorFloat or Parameter objects. + Args: params: List of DescriptorFloat or Parameter objects. - Returns: - A pandas DataFrame containing parameter information. + Returns: A pandas DataFrame containing parameter + information. """ records = [] for param in params: @@ -486,8 +481,7 @@ def current_minimizer(self) -> Optional[str]: def current_minimizer(self, selection: str) -> None: """Switch to a different minimizer implementation. - Args: - selection: Minimizer selection string, e.g. 'lmfit'. + Args: selection: Minimizer selection string, e.g. 'lmfit'. """ self.fitter = Fitter(selection) console.paragraph('Current minimizer changed to') @@ -511,8 +505,7 @@ def fit_mode_type(self) -> str: def fit_mode_type(self, new_type: str) -> None: """Switch to a different fit-mode category type. - Args: - new_type: Fit-mode tag (e.g. ``'default'``). + Args: new_type: Fit-mode tag (e.g. ``'default'``). """ supported_tags = FitModeFactory.supported_tags() if new_type not in supported_tags: @@ -594,18 +587,18 @@ def fit(self): fitting to see a summary of the fit quality and parameter values. - In 'single' mode, fits each experiment independently. In - 'joint' mode, performs a simultaneous fit across experiments - with weights. + In 'single' mode, fits each experiment independently. In 'joint' + mode, performs a simultaneous fit across experiments with + weights. Sets :attr:`fit_results` on success, which can be accessed - programmatically - (e.g., ``analysis.fit_results.reduced_chi_square``). + programmatically (e.g., + ``analysis.fit_results.reduced_chi_square``). Example:: - project.analysis.fit() - project.analysis.show_fit_results() # Display results + project.analysis.fit() project.analysis.show_fit_results() # + Display results """ structures = self.project.structures if not structures: @@ -670,8 +663,7 @@ def show_fit_results(self) -> None: Example:: - project.analysis.fit() - project.analysis.show_fit_results() + project.analysis.fit() project.analysis.show_fit_results() """ if not hasattr(self, 'fit_results') or self.fit_results is None: log.warning('No fit results available. Run fit() first.') @@ -688,8 +680,8 @@ def _update_categories(self, called_by_minimizer=False) -> None: This ensures aliases and constraints are up-to-date before serialization or after parameter changes. - Args: - called_by_minimizer: Whether this is called during fitting. + Args: called_by_minimizer: Whether this is called during + fitting. """ # Apply constraints to sync dependent parameters if self.constraints._items: @@ -704,8 +696,8 @@ def _update_categories(self, called_by_minimizer=False) -> None: def as_cif(self): """Serialize the analysis section to a CIF string. - Returns: - The analysis section represented as a CIF document string. + Returns: The analysis section represented as a CIF document + string. """ from easydiffraction.io.cif.serialize import analysis_to_cif diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index d566f735..9521ebce 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -31,7 +31,8 @@ def calculate_structure_factors( experiment: ExperimentBase, called_by_minimizer: bool, ) -> None: - """Calculate structure factors for a single structure and + """ + Calculate structure factors for a single structure and experiment. """ pass @@ -43,16 +44,24 @@ def calculate_pattern( experiment: ExperimentBase, called_by_minimizer: bool, ) -> np.ndarray: - """Calculate the diffraction pattern for a single structure and + """ + Calculate the diffraction pattern for a single structure and experiment. - Args: - structure: The structure object. - experiment: The experiment object. - called_by_minimizer: Whether the calculation is called by a - minimizer. - Returns: + Parameters + ---------- + structure : Structures + The structure object. + experiment : ExperimentBase + The experiment object. + called_by_minimizer : bool + Whether the calculation is called by a minimizer. Default is + False. + + Returns + ------- + np.ndarray The calculated diffraction pattern as a NumPy array. """ pass diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 92ac3d9d..6514bd96 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -50,11 +50,9 @@ def calculate_structure_factors( ) -> None: """Call Crysfml to calculate structure factors. - Args: - structures: The structures to calculate structure - factors for. - experiments: The experiments associated with the sample - models. + Args: structures: The structures to calculate structure + factors for. experiments: The experiments associated with + the sample models. """ raise NotImplementedError('HKL calculation is not implemented for CrysfmlCalculator.') @@ -67,15 +65,13 @@ def calculate_pattern( """Calculates the diffraction pattern using Crysfml for the given structure and experiment. - Args: - structure: The structure to calculate the pattern for. - experiment: The experiment associated with the structure. - called_by_minimizer: Whether the calculation is called by a - minimizer. + Args: structure: The structure to calculate the pattern for. + experiment: The experiment associated with the structure. + called_by_minimizer: Whether the calculation is called by a + minimizer. - Returns: - The calculated diffraction pattern as a NumPy array or a - list of floats. + Returns: The calculated diffraction pattern as a NumPy array + or a list of floats. """ # Intentionally unused, required by public API/signature del called_by_minimizer @@ -96,12 +92,10 @@ def _adjust_pattern_length( ) -> List[float]: """Adjusts the length of the pattern to match the target length. - Args: - pattern: The pattern to adjust. - target_length: The desired length of the pattern. + Args: pattern: The pattern to adjust. target_length: The + desired length of the pattern. - Returns: - The adjusted pattern. + Returns: The adjusted pattern. """ # TODO: Check the origin of this discrepancy coming from # PyCrysFML @@ -117,13 +111,11 @@ def _crysfml_dict( """Converts the structure and experiment into a dictionary format for Crysfml. - Args: - structure: The structure to convert. - experiment: The experiment to convert. + Args: structure: The structure to convert. experiment: + The experiment to convert. - Returns: - A dictionary representation of the structure and - experiment. + Returns: A dictionary representation of the structure and + experiment. """ structure_dict = self._convert_structure_to_dict(structure) experiment_dict = self._convert_experiment_to_dict(experiment) @@ -138,11 +130,9 @@ def _convert_structure_to_dict( ) -> Dict[str, Any]: """Converts a structure into a dictionary format. - Args: - structure: The structure to convert. + Args: structure: The structure to convert. - Returns: - A dictionary representation of the structure. + Returns: A dictionary representation of the structure. """ structure_dict = { structure.name: { @@ -178,11 +168,9 @@ def _convert_experiment_to_dict( ) -> Dict[str, Any]: """Converts an experiment into a dictionary format. - Args: - experiment: The experiment to convert. + Args: experiment: The experiment to convert. - Returns: - A dictionary representation of the experiment. + Returns: A dictionary representation of the experiment. """ expt_type = getattr(experiment, 'type', None) instrument = getattr(experiment, 'instrument', None) diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 967189a1..88d16009 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -64,13 +64,10 @@ def calculate_structure_factors( """Raises a NotImplementedError as HKL calculation is not implemented. - Args: - structure: The structure to calculate structure - factors for. - experiment: The experiment associated with the sample - models. - called_by_minimizer: Whether the calculation is called by a - minimizer. + Args: structure: The structure to calculate structure + factors for. experiment: The experiment associated with the + sample models. called_by_minimizer: Whether the + calculation is called by a minimizer. """ combined_name = f'{structure.name}_{experiment.name}' @@ -122,21 +119,18 @@ def calculate_pattern( """Calculates the diffraction pattern using Cryspy for the given structure and experiment. - We only recreate the cryspy_obj if this method is - - NOT called by the minimizer, or - - the cryspy_dict is NOT yet created. - In other cases, we are modifying the existing cryspy_dict - This allows significantly speeding up the calculation - - Args: - structure: The structure to calculate the pattern for. - experiment: The experiment associated with the structure. - called_by_minimizer: Whether the calculation is called by a - minimizer. - - Returns: - The calculated diffraction pattern as a NumPy array or a - list of floats. + We only recreate the cryspy_obj if this method is - NOT called + by the minimizer, or - the cryspy_dict is NOT yet created. In + other cases, we are modifying the existing cryspy_dict This + allows significantly speeding up the calculation + + Args: structure: The structure to calculate the pattern for. + experiment: The experiment associated with the structure. + called_by_minimizer: Whether the calculation is called by a + minimizer. + + Returns: The calculated diffraction pattern as a NumPy array + or a list of floats. """ combined_name = f'{structure.name}_{experiment.name}' @@ -197,12 +191,10 @@ def _recreate_cryspy_dict( """Recreates the Cryspy dictionary for the given structure and experiment. - Args: - structure: The structure to update. - experiment: The experiment to update. + Args: structure: The structure to update. experiment: + The experiment to update. - Returns: - The updated Cryspy dictionary. + Returns: The updated Cryspy dictionary. """ combined_name = f'{structure.name}_{experiment.name}' cryspy_dict = copy.deepcopy(self._cryspy_dicts[combined_name]) @@ -311,12 +303,10 @@ def _recreate_cryspy_obj( """Recreates the Cryspy object for the given structure and experiment. - Args: - structure: The structure to recreate. - experiment: The experiment to recreate. + Args: structure: The structure to recreate. experiment: + The experiment to recreate. - Returns: - The recreated Cryspy object. + Returns: The recreated Cryspy object. """ cryspy_obj = str_to_globaln('') @@ -341,11 +331,10 @@ def _convert_structure_to_cryspy_cif( ) -> str: """Converts a structure to a Cryspy CIF string. - Args: - structure: The structure to convert. + Args: structure: The structure to convert. - Returns: - The Cryspy CIF string representation of the structure. + Returns: The Cryspy CIF string representation of the + structure. """ return structure.as_cif @@ -356,13 +345,11 @@ def _convert_experiment_to_cryspy_cif( ) -> str: """Converts an experiment to a Cryspy CIF string. - Args: - experiment: The experiment to convert. - linked_structure: The structure linked to the - experiment. + Args: experiment: The experiment to convert. + linked_structure: The structure linked to the experiment. - Returns: - The Cryspy CIF string representation of the experiment. + Returns: The Cryspy CIF string representation of the + experiment. """ # Try to get experiment attributes expt_type = getattr(experiment, 'type', None) diff --git a/src/easydiffraction/analysis/calculators/factory.py b/src/easydiffraction/analysis/calculators/factory.py index fac3b570..edc395db 100644 --- a/src/easydiffraction/analysis/calculators/factory.py +++ b/src/easydiffraction/analysis/calculators/factory.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: BSD-3-Clause """Calculator factory — delegates to ``FactoryBase``. -Overrides ``_supported_map`` to filter out calculators whose engines -are not importable in the current environment. +Overrides ``_supported_map`` to filter out calculators whose engines are +not importable in the current environment. """ from __future__ import annotations diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index d8667cfb..c106c59d 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -24,10 +24,9 @@ class Alias(CategoryItem): Maps a human-readable ``label`` to a concrete ``param_uid`` used by the engine. - Args: - label: Alias label. Must match ``^[A-Za-z_][A-Za-z0-9_]*$``. - param_uid: Target parameter uid. Same identifier pattern as - ``label``. + Args: label: Alias label. Must match ``^[A-Za- + z_][A-Za-z0-9_]*$``. param_uid: Target parameter uid. Same + identifier pattern as ``label``. """ def __init__(self) -> None: @@ -64,8 +63,8 @@ def label(self) -> StringDescriptor: """... Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._label @@ -78,8 +77,8 @@ def param_uid(self) -> StringDescriptor: """... Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._param_uid diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 4a14aa33..1ec65b42 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -22,9 +22,8 @@ class Constraint(CategoryItem): """Single constraint item. - Args: - lhs_alias: Left-hand side alias name being constrained. - rhs_expr: Right-hand side expression as a string. + Args: lhs_alias: Left-hand side alias name being constrained. + rhs_expr: Right-hand side expression as a string. """ def __init__(self) -> None: @@ -61,8 +60,8 @@ def lhs_alias(self) -> StringDescriptor: """Left-hand side of the equation. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._lhs_alias @@ -75,8 +74,8 @@ def rhs_expr(self) -> StringDescriptor: """Right-hand side expression. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._rhs_expr diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index ecd31415..5ec2039a 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -51,8 +51,8 @@ def mode(self) -> StringDescriptor: """Fitting strategy. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._mode diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index 03916a4e..3530b1b9 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -25,9 +25,8 @@ class JointFitExperiment(CategoryItem): """A single joint-fit entry. - Args: - id: Experiment identifier used in the fit session. - weight: Relative weight factor in the combined objective. + Args: id: Experiment identifier used in the fit session. weight: + Relative weight factor in the combined objective. """ def __init__(self) -> None: @@ -64,8 +63,8 @@ def id(self) -> StringDescriptor: """Experiment identifier. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._id @@ -78,8 +77,8 @@ def weight(self) -> NumericDescriptor: """Weight factor. Reading this property returns the underlying - ``NumericDescriptor` object. - Assigning to it updates the parameter value. + ``NumericDescriptor`` object. Assigning to it updates the + parameter value. """ return self._weight diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index cc267102..1e488c5a 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -17,12 +17,10 @@ def calculate_r_factor( """Calculate the R-factor (reliability factor) between observed and calculated data. - Args: - y_obs: Observed data points. - y_calc: Calculated data points. + Args: y_obs: Observed data points. y_calc: Calculated data + points. - Returns: - R-factor value. + Returns: R-factor value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -39,13 +37,10 @@ def calculate_weighted_r_factor( """Calculate the weighted R-factor between observed and calculated data. - Args: - y_obs: Observed data points. - y_calc: Calculated data points. - weights: Weights for each data point. + Args: y_obs: Observed data points. y_calc: Calculated data + points. weights: Weights for each data point. - Returns: - Weighted R-factor value. + Returns: Weighted R-factor value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -62,12 +57,10 @@ def calculate_rb_factor( """Calculate the Bragg R-factor between observed and calculated data. - Args: - y_obs: Observed data points. - y_calc: Calculated data points. + Args: y_obs: Observed data points. y_calc: Calculated data + points. - Returns: - Bragg R-factor value. + Returns: Bragg R-factor value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -83,12 +76,10 @@ def calculate_r_factor_squared( """Calculate the R-factor squared between observed and calculated data. - Args: - y_obs: Observed data points. - y_calc: Calculated data points. + Args: y_obs: Observed data points. y_calc: Calculated data + points. - Returns: - R-factor squared value. + Returns: R-factor squared value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -103,12 +94,10 @@ def calculate_reduced_chi_square( ) -> float: """Calculate the reduced chi-square statistic. - Args: - residuals: Residuals between observed and calculated data. - num_parameters: Number of free parameters used in the model. + Args: residuals: Residuals between observed and calculated data. + num_parameters: Number of free parameters used in the model. - Returns: - Reduced chi-square value. + Returns: Reduced chi-square value. """ residuals = np.asarray(residuals) chi_square = np.sum(residuals**2) @@ -127,13 +116,11 @@ def get_reliability_inputs( """Collect observed and calculated data points for reliability calculations. - Args: - structures: Collection of structures. - experiments: Collection of experiments. + Args: structures: Collection of structures. experiments: + Collection of experiments. - Returns: - Tuple containing arrays of (observed values, calculated values, - error values) + Returns: Tuple containing arrays of (observed values, calculated + values, error values) """ y_obs_all = [] y_calc_all = [] diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index 895cdeb6..b6f5efc5 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -36,19 +36,17 @@ def __init__( ) -> None: """Initialize FitResults with the given parameters. - Args: - success: Indicates if the fit was successful. - parameters: List of parameters used in the fit. - chi_square: Chi-square value of the fit. - reduced_chi_square: Reduced chi-square value of the fit. - message: Message related to the fit. - iterations: Number of iterations performed. - engine_result: Result from the fitting engine. - starting_parameters: Initial parameters for the fit. - fitting_time: Time taken for the fitting process. - **kwargs: Additional engine-specific fields. If ``redchi`` - is provided and ``reduced_chi_square`` is not set, it is - used as the reduced chi-square value. + Args: success: Indicates if the fit was successful. + parameters: List of parameters used in the fit. chi_square: + Chi-square value of the fit. reduced_chi_square: Reduced + chi-square value of the fit. message: Message related to the + fit. iterations: Number of iterations performed. + engine_result: Result from the fitting engine. + starting_parameters: Initial parameters for the fit. + fitting_time: Time taken for the fitting process. **kwargs: + Additional engine-specific fields. If ``redchi`` is + provided and ``reduced_chi_square`` is not set, it is used as + the reduced chi-square value. """ self.success: bool = success self.parameters: List[Any] = parameters if parameters is not None else [] @@ -79,12 +77,12 @@ def display_results( ) -> None: """Render a human-readable summary of the fit. - Args: - y_obs: Observed intensities for pattern R-factor metrics. - y_calc: Calculated intensities for pattern R-factor metrics. - y_err: Standard deviations of observed intensities for wR. - f_obs: Observed structure-factor magnitudes for Bragg R. - f_calc: Calculated structure-factor magnitudes for Bragg R. + Args: y_obs: Observed intensities for pattern R-factor + metrics. y_calc: Calculated intensities for pattern R-factor + metrics. y_err: Standard deviations of observed intensities + for wR. f_obs: Observed structure-factor magnitudes for + Bragg R. f_calc: Calculated structure-factor magnitudes for + Bragg R. """ status_icon = '✅' if self.success else '❌' rf = rf2 = wr = br = None diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index 5bf99e60..c9d4c530 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -58,9 +58,8 @@ def _make_display_handle() -> Any | None: environment. - In Jupyter, returns an IPython DisplayHandle and creates a - placeholder. - - In terminal, returns a _TerminalLiveHandle backed by rich Live. - - If neither applies, returns None. + placeholder. - In terminal, returns a _TerminalLiveHandle backed by + rich Live. - If neither applies, returns None. """ if in_jupyter() and display is not None and HTML is not None: h = DisplayHandle() @@ -114,12 +113,10 @@ def track( ) -> np.ndarray: """Update progress with current residuals and parameters. - Args: - residuals: Residuals between measured and calculated data. - parameters: Current free parameters being fitted. + Args: residuals: Residuals between measured and calculated + data. parameters: Current free parameters being fitted. - Returns: - Residuals unchanged, for optimizer consumption. + Returns: Residuals unchanged, for optimizer consumption. """ self._iteration += 1 @@ -202,8 +199,8 @@ def stop_timer(self) -> None: def start_tracking(self, minimizer_name: str) -> None: """Initialize display and headers and announce the minimizer. - Args: - minimizer_name: Name of the minimizer used for the run. + Args: minimizer_name: Name of the minimizer used for the + run. """ console.print(f"🚀 Starting fit process with '{minimizer_name}'...") console.print('📈 Goodness-of-fit (reduced χ²) change:') @@ -223,8 +220,7 @@ def start_tracking(self, minimizer_name: str) -> None: def add_tracking_info(self, row: List[str]) -> None: """Append a formatted row to the progress display. - Args: - row: Columns corresponding to DEFAULT_HEADERS. + Args: row: Columns corresponding to DEFAULT_HEADERS. """ # Append and update via the active handle (Jupyter or # terminal live) diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index d33c0ed2..f2dbd2f0 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -38,15 +38,13 @@ def fit( """Run the fitting process. This method performs the optimization but does not display - results. Use :meth:`show_fit_results` on the Analysis object - to display the fit results after fitting is complete. - - Args: - structures: Collection of structures. - experiments: Collection of experiments. - weights: Optional weights for joint fitting. - analysis: Optional Analysis object to update its categories - during fitting. + results. Use :meth:`show_fit_results` on the Analysis object to + display the fit results after fitting is complete. + + Args: structures: Collection of structures. experiments: + Collection of experiments. weights: Optional weights for + joint fitting. analysis: Optional Analysis object to update + its categories during fitting. """ params = structures.free_parameters + experiments.free_parameters @@ -79,12 +77,11 @@ def _process_fit_results( This method is typically called by :meth:`Analysis.show_fit_results` rather than directly. It - calculates R-factors and other metrics, then renders them to - the console. + calculates R-factors and other metrics, then renders them to the + console. - Args: - structures: Collection of structures. - experiments: Collection of experiments. + Args: structures: Collection of structures. experiments: + Collection of experiments. """ y_obs, y_calc, y_err = get_reliability_inputs( structures, @@ -110,12 +107,10 @@ def _collect_free_parameters( ) -> List[Parameter]: """Collect free parameters from structures and experiments. - Args: - structures: Collection of structures. - experiments: Collection of experiments. + Args: structures: Collection of structures. experiments: + Collection of experiments. - Returns: - List of free parameters. + Returns: List of free parameters. """ free_params: List[Parameter] = structures.free_parameters + experiments.free_parameters return free_params @@ -133,17 +128,14 @@ def _residual_function( and calculated patterns. It updates the parameter values according to the optimizer-provided engine_params. - Args: - engine_params: Engine-specific parameter dict. - parameters: List of parameters being optimized. - structures: Collection of structures. - experiments: Collection of experiments. - weights: Optional weights for joint fitting. - analysis: Optional Analysis object to update its categories - during fitting. - - Returns: - Array of weighted residuals. + Args: engine_params: Engine-specific parameter dict. + parameters: List of parameters being optimized. structures: + Collection of structures. experiments: Collection of + experiments. weights: Optional weights for joint fitting. + analysis: Optional Analysis object to update its categories + during fitting. + + Returns: Array of weighted residuals. """ # Sync parameters back to objects self.minimizer._sync_result_to_parameters(parameters, engine_params) diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index eda5b649..ec1c880c 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -18,12 +18,10 @@ class MinimizerBase(ABC): """Abstract base for concrete minimizers. - Contract: - - Subclasses must implement ``_prepare_solver_args``, - ``_run_solver``, ``_sync_result_to_parameters`` and - ``_check_success``. - - The ``fit`` method orchestrates the full workflow and returns - :class:`FitResults`. + Contract: - Subclasses must implement ``_prepare_solver_args``, + ``_run_solver``, ``_sync_result_to_parameters`` and + ``_check_success``. - The ``fit`` method orchestrates the full + workflow and returns :class:`FitResults`. """ def __init__( @@ -46,8 +44,7 @@ def __init__( def _start_tracking(self, minimizer_name: str) -> None: """Initialize progress tracking and timer. - Args: - minimizer_name: Human-readable name shown in progress. + Args: minimizer_name: Human-readable name shown in progress. """ self.tracker.reset() self.tracker.start_tracking(minimizer_name) @@ -62,11 +59,10 @@ def _stop_tracking(self) -> None: def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: """Prepare keyword-arguments for the underlying solver. - Args: - parameters: List of free parameters to be fitted. + Args: parameters: List of free parameters to be fitted. - Returns: - Mapping of keyword arguments to pass into ``_run_solver``. + Returns: Mapping of keyword arguments to pass into + ``_run_solver``. """ pass @@ -97,12 +93,10 @@ def _finalize_fit( ) -> FitResults: """Build :class:`FitResults` and store it on ``self.result``. - Args: - parameters: Parameters after the solver finished. - raw_result: Backend-specific solver output object. + Args: parameters: Parameters after the solver finished. + raw_result: Backend-specific solver output object. - Returns: - FitResults: Aggregated outcome of the fit. + Returns: FitResults: Aggregated outcome of the fit. """ self._sync_result_to_parameters(parameters, raw_result) success = self._check_success(raw_result) @@ -128,13 +122,11 @@ def fit( ) -> FitResults: """Run the full minimization workflow. - Args: - parameters: Free parameters to optimize. - objective_function: Callable returning residuals for a given - set of engine arguments. + Args: parameters: Free parameters to optimize. + objective_function: Callable returning residuals for a given set + of engine arguments. - Returns: - FitResults with success flag, best chi2 and timing. + Returns: FitResults with success flag, best chi2 and timing. """ minimizer_name = self.name or 'Unnamed Minimizer' if self.method is not None: diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index 9ceefe5b..da5268b4 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -59,9 +59,8 @@ def _sync_result_to_parameters( ) -> None: """Synchronizes the result from the solver to the parameters. - Args: - parameters: List of parameters being optimized. - raw_result: The result object returned by the solver. + Args: parameters: List of parameters being optimized. + raw_result: The result object returned by the solver. """ # Ensure compatibility with raw_result coming from dfols.solve() result_values = raw_result.x if hasattr(raw_result, 'x') else raw_result @@ -77,10 +76,9 @@ def _sync_result_to_parameters( def _check_success(self, raw_result: Any) -> bool: """Determines success from DFO-LS result dictionary. - Args: - raw_result: The result object returned by the solver. + Args: raw_result: The result object returned by the solver. - Returns: - True if the optimization was successful, False otherwise. + Returns: True if the optimization was successful, False + otherwise. """ return raw_result.flag == raw_result.EXIT_SUCCESS diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index 0d7aa83b..c326ff2f 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -42,12 +42,10 @@ def _prepare_solver_args( ) -> Dict[str, Any]: """Prepares the solver arguments for the lmfit minimizer. - Args: - parameters: List of parameters to be optimized. + Args: parameters: List of parameters to be optimized. - Returns: - A dictionary containing the prepared lmfit. Parameters - object. + Returns: A dictionary containing the prepared lmfit. + Parameters object. """ engine_parameters = lmfit.Parameters() for param in parameters: @@ -63,12 +61,10 @@ def _prepare_solver_args( def _run_solver(self, objective_function: Any, **kwargs: Any) -> Any: """Runs the lmfit solver. - Args: - objective_function: The objective function to minimize. - **kwargs: Additional arguments for the solver. + Args: objective_function: The objective function to + minimize. **kwargs: Additional arguments for the solver. - Returns: - The result of the lmfit minimization. + Returns: The result of the lmfit minimization. """ engine_parameters = kwargs.get('engine_parameters') @@ -87,9 +83,8 @@ def _sync_result_to_parameters( ) -> None: """Synchronizes the result from the solver to the parameters. - Args: - parameters: List of parameters being optimized. - raw_result: The result object returned by the solver. + Args: parameters: List of parameters being optimized. + raw_result: The result object returned by the solver. """ param_values = raw_result.params if hasattr(raw_result, 'params') else raw_result @@ -104,11 +99,10 @@ def _sync_result_to_parameters( def _check_success(self, raw_result: Any) -> bool: """Determines success from lmfit MinimizerResult. - Args: - raw_result: The result object returned by the solver. + Args: raw_result: The result object returned by the solver. - Returns: - True if the optimization was successful, False otherwise. + Returns: True if the optimization was successful, False + otherwise. """ return getattr(raw_result, 'success', False) @@ -122,12 +116,10 @@ def _iteration_callback( ) -> None: """Callback function for each iteration of the minimizer. - Args: - params: The current parameters. - iter: The current iteration number. - resid: The residuals. - *args: Additional positional arguments. - **kwargs: Additional keyword arguments. + Args: params: The current parameters. iter: The current + iteration number. resid: The residuals. *args: + Additional positional arguments. **kwargs: Additional + keyword arguments. """ # Intentionally unused, required by callback signature del params, resid, args, kwargs diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index 883aec56..14e7bed7 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -176,8 +176,8 @@ def _mark_parent_dirty(self) -> None: """Set ``_need_categories_update`` on the parent datablock. Called whenever the collection content changes (items added or - removed) so that subsequent ``_update_categories()`` calls - re-run all category updates. + removed) so that subsequent ``_update_categories()`` calls re- + run all category updates. """ parent = getattr(self, '_parent', None) if parent is not None and hasattr(parent, '_need_categories_update'): @@ -218,8 +218,7 @@ def from_cif(self, block): def add(self, item) -> None: """Insert or replace a pre-built item into the collection. - Args: - item: A ``CategoryItem`` instance to add. + Args: item: A ``CategoryItem`` instance to add. """ self[item._identity.category_entry_name] = item self._mark_parent_dirty() @@ -230,8 +229,7 @@ def create(self, **kwargs) -> None: A default instance of the collection's item type is created, then each keyword argument is applied via ``setattr``. - Args: - **kwargs: Attribute names and values for the new item. + Args: **kwargs: Attribute names and values for the new item. """ child_obj = self._item_type() diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index aac21cf8..f0e29bbf 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -15,9 +15,8 @@ class CollectionBase(GuardedBase): """A minimal collection with stable iteration and name indexing. - Args: - item_type: Type of items accepted by the collection. Used for - validation and tooling; not enforced at runtime here. + Args: item_type: Type of items accepted by the collection. Used + for validation and tooling; not enforced at runtime here. """ def __init__(self, item_type) -> None: @@ -77,11 +76,9 @@ def __len__(self) -> int: def remove(self, name: str) -> None: """Remove an item by its key. - Args: - name: Identity key of the item to remove. + Args: name: Identity key of the item to remove. - Raises: - KeyError: If no item with the given key exists. + Raises: KeyError: If no item with the given key exists. """ del self[name] diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 0fdb452b..57e8fb9e 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -123,9 +123,9 @@ class DatablockCollection(CollectionBase): Each item is a DatablockItem. - Subclasses provide explicit ``add_from_*`` convenience methods - that delegate to the corresponding factory classmethods, then - call :meth:`add` with the resulting item. + Subclasses provide explicit ``add_from_*`` convenience methods that + delegate to the corresponding factory classmethods, then call + :meth:`add` with the resulting item. """ def _key_for(self, item): @@ -135,9 +135,8 @@ def _key_for(self, item): def add(self, item) -> None: """Add a pre-built item to the collection. - Args: - item: A ``DatablockItem`` instance (e.g. a ``Structure`` - or ``ExperimentBase`` subclass). + Args: item: A ``DatablockItem`` instance (e.g. a + ``Structure`` or ``ExperimentBase`` subclass). """ self[item._identity.datablock_entry_name] = item diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index 6aed086a..8f228c79 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -3,8 +3,8 @@ """Base factory with registration, lookup, and context-dependent defaults. -Concrete factories inherit from ``FactoryBase`` and only need to -define ``_default_rules``. +Concrete factories inherit from ``FactoryBase`` and only need to define +``_default_rules``. """ from __future__ import annotations @@ -26,7 +26,7 @@ class FactoryBase: Subclasses must set: * ``_default_rules`` -- mapping of ``frozenset`` conditions to tag - strings. Use ``frozenset(): 'tag'`` for a universal default. + strings. Use ``frozenset(): 'tag'`` for a universal default. The ``__init_subclass__`` hook ensures every subclass gets its own independent ``_registry`` list. @@ -52,9 +52,8 @@ def register(cls, klass): Usage:: - @SomeFactory.register - class MyClass(SomeBase): - type_info = TypeInfo(...) + @SomeFactory.register class MyClass(SomeBase): type_info = + TypeInfo(...) Returns the class unmodified. """ @@ -87,15 +86,12 @@ def default_tag(cls, **conditions) -> str: biggest subset of the given conditions wins. A rule with an empty key (``frozenset()``) acts as a universal fallback. - Args: - **conditions: Experimental-axis values, e.g. - ``scattering_type=ScatteringTypeEnum.BRAGG``. + Args: **conditions: Experimental-axis values, e.g. + ``scattering_type=ScatteringTypeEnum.BRAGG``. - Returns: - The resolved default tag string. + Returns: The resolved default tag string. - Raises: - ValueError: If no rule matches the given conditions. + Raises: ValueError: If no rule matches the given conditions. """ condition_set = frozenset(conditions.items()) best_match_tag: str | None = None @@ -121,12 +117,10 @@ def default_tag(cls, **conditions) -> str: def create(cls, tag: str, **kwargs) -> Any: """Instantiate a registered class by *tag*. - Args: - tag: ``type_info.tag`` value. - **kwargs: Forwarded to the class constructor. + Args: tag: ``type_info.tag`` value. **kwargs: Forwarded + to the class constructor. - Raises: - ValueError: If *tag* is not in the registry. + Raises: ValueError: If *tag* is not in the registry. """ supported = cls._supported_map() if tag not in supported: @@ -139,8 +133,7 @@ def create_default_for(cls, **conditions) -> Any: Combines ``default_tag(**conditions)`` with ``create(tag)``. - Args: - **conditions: Experimental-axis values. + Args: **conditions: Experimental-axis values. """ tag = cls.default_tag(**conditions) return cls.create(tag) @@ -161,12 +154,11 @@ def supported_for( ) -> List[Type]: """Return classes matching conditions and/or calculator. - Args: - calculator: Optional ``CalculatorEnum`` value. - sample_form: Optional ``SampleFormEnum`` value. - scattering_type: Optional ``ScatteringTypeEnum`` value. - beam_mode: Optional ``BeamModeEnum`` value. - radiation_probe: Optional ``RadiationProbeEnum`` value. + Args: calculator: Optional ``CalculatorEnum`` value. + sample_form: Optional ``SampleFormEnum`` value. scattering_type: + Optional ``ScatteringTypeEnum`` value. beam_mode: Optional + ``BeamModeEnum`` value. radiation_probe: Optional + ``RadiationProbeEnum`` value. """ result = [] for klass in cls._supported_map().values(): @@ -200,12 +192,11 @@ def show_supported( ) -> None: """Pretty-print a table of supported types. - Args: - calculator: Optional ``CalculatorEnum`` filter. - sample_form: Optional ``SampleFormEnum`` filter. - scattering_type: Optional ``ScatteringTypeEnum`` filter. - beam_mode: Optional ``BeamModeEnum`` filter. - radiation_probe: Optional ``RadiationProbeEnum`` filter. + Args: calculator: Optional ``CalculatorEnum`` filter. + sample_form: Optional ``SampleFormEnum`` filter. + scattering_type: Optional ``ScatteringTypeEnum`` filter. + beam_mode: Optional ``BeamModeEnum`` filter. radiation_probe: + Optional ``RadiationProbeEnum`` filter. """ matching = cls.supported_for( calculator=calculator, diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index a6f1242c..3f72a458 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -81,9 +81,8 @@ def _iter_properties(cls): """Iterate over all public properties defined in the class hierarchy. - Yields: - tuple[str, property]: Each (key, property) pair for public - attributes. + Yields: tuple[str, property]: Each (key, property) pair for + public attributes. """ for base in cls.mro(): for key, attr in base.__dict__.items(): @@ -161,8 +160,7 @@ def _first_sentence(docstring: str | None) -> str: def _iter_methods(cls): """Iterate over public methods in the class hierarchy. - Yields: - tuple[str, callable]: Each (name, function) pair. + Yields: tuple[str, callable]: Each (name, function) pair. """ seen: set = set() for base in cls.mro(): diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index a1ad457c..53c678ba 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -4,9 +4,9 @@ Three frozen dataclasses describe a concrete class: -- ``TypeInfo`` — stable tag and human-readable description. -- ``Compatibility`` — experimental conditions (multiple fields). -- ``CalculatorSupport`` — which calculation engines can handle it. +- ``TypeInfo`` — stable tag and human-readable description. - +``Compatibility`` — experimental conditions (multiple fields). - +``CalculatorSupport`` — which calculation engines can handle it. """ from __future__ import annotations @@ -20,13 +20,12 @@ class TypeInfo: """Stable identity and human-readable description for a factory- created class. - Attributes: - tag: Short, stable string identifier used for serialization, - user-facing selection, and factory lookup. Must be unique - within a factory's registry. Examples: ``'line-segment'``, - ``'pseudo-voigt'``, ``'cryspy'``. - description: One-line human-readable explanation. Used in - ``show_supported()`` tables and documentation. + Attributes: tag: Short, stable string identifier used for + serialization, user-facing selection, and factory lookup. + Must be unique within a factory's registry. Examples: + ``'line-segment'``, ``'pseudo-voigt'``, ``'cryspy'``. + description: One-line human-readable explanation. Used in + ``show_supported()`` tables and documentation. """ tag: str @@ -62,10 +61,8 @@ def supports( Example:: - compat.supports( - scattering_type=ScatteringTypeEnum.BRAGG, - beam_mode=BeamModeEnum.CONSTANT_WAVELENGTH, - ) + compat.supports( scattering_type=ScatteringTypeEnum.BRAGG, + beam_mode=BeamModeEnum.CONSTANT_WAVELENGTH, ) """ for axis, value in ( ('sample_form', sample_form), @@ -85,9 +82,8 @@ def supports( class CalculatorSupport: """Which calculation engines can handle this class. - Attributes: - calculators: Frozenset of ``CalculatorEnum`` values. Empty - means "any calculator" (no restriction). + Attributes: calculators: Frozenset of ``CalculatorEnum`` values. + Empty means "any calculator" (no restriction). """ calculators: FrozenSet = frozenset() @@ -95,12 +91,10 @@ class CalculatorSupport: def supports(self, calculator) -> bool: """Check if a specific calculator can handle this class. - Args: - calculator: A ``CalculatorEnum`` value. + Args: calculator: A ``CalculatorEnum`` value. - Returns: - ``True`` if the calculator is in the set, or if the set is - empty (meaning any calculator is accepted). + Returns: ``True`` if the calculator is in the set, or if the + set is empty (meaning any calculator is accepted). """ if not self.calculators: return True diff --git a/src/easydiffraction/core/singleton.py b/src/easydiffraction/core/singleton.py index 7644bc39..d0b0c589 100644 --- a/src/easydiffraction/core/singleton.py +++ b/src/easydiffraction/core/singleton.py @@ -106,7 +106,7 @@ def set_aliases(self, aliases): """Sets the alias map (name → parameter wrapper). Called when user registers parameter aliases like: - alias='biso_La', param=model.atom_sites['La'].b_iso + alias='biso_La', param=model.atom_sites['La'].b_iso """ self._alias_to_param = dict(aliases.items()) @@ -114,8 +114,8 @@ def set_constraints(self, constraints): """Sets the constraints and triggers parsing into internal format. - Called when user registers expressions like: - lhs_alias='occ_Ba', rhs_expr='1 - occ_La' + Called when user registers expressions like: lhs_alias='occ_Ba', + rhs_expr='1 - occ_La' """ self._constraints = constraints._items self._parse_constraints() @@ -139,9 +139,8 @@ def apply(self) -> None: """Evaluates constraints and applies them to dependent parameters. - For each constraint: - - Evaluate RHS using current values of aliases - - Locate the dependent parameter by alias → uid → param + For each constraint: - Evaluate RHS using current values of + aliases - Locate the dependent parameter by alias → uid → param - Update its value and mark it as constrained """ if not self._parsed_constraints: diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index fa0c26ec..7ea699d0 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -29,16 +29,15 @@ class GenericDescriptorBase(GuardedBase): """Base class for all parameter-like descriptors. - A descriptor encapsulates a typed value with validation, - human-readable name/description and a globally unique identifier - that is stable across the session. Concrete subclasses specialize - the expected data type and can extend the public API with - additional behavior (e.g. units). - - Attributes: - name: Local parameter name (e.g. 'a', 'b_iso'). - description: Optional human-readable description. - uid: Stable random identifier for external references. + A descriptor encapsulates a typed value with validation, human- + readable name/description and a globally unique identifier that is + stable across the session. Concrete subclasses specialize the + expected data type and can extend the public API with additional + behavior (e.g. units). + + Attributes: name: Local parameter name (e.g. 'a', 'b_iso'). + description: Optional human-readable description. uid: Stable + random identifier for external references. """ _BOOL_SPEC_TEMPLATE = AttributeSpec( @@ -55,10 +54,9 @@ def __init__( ): """Initialize the descriptor with validation and identity. - Args: - value_spec: Validation specification for the value. - name: Local name of the descriptor within its category. - description: Optional human-readable description. + Args: value_spec: Validation specification for the value. + name: Local name of the descriptor within its category. + description: Optional human-readable description. """ super().__init__() @@ -166,16 +164,16 @@ def value(self, v): def _set_value_from_minimizer(self, v) -> None: """Set the value from a minimizer, bypassing validation. - Writes ``_value`` directly — no type or range checks — but - still marks the owning :class:`DatablockItem` dirty so that + Writes ``_value`` directly — no type or range checks — but still + marks the owning :class:`DatablockItem` dirty so that ``_update_categories()`` knows work is needed. This exists because: 1. Physical-range validators (e.g. intensity ≥ 0) would reject - trial values the minimizer needs to explore. - 2. Validation overhead is measurable over thousands of - objective-function evaluations. + trial values the minimizer needs to explore. 2. Validation + overhead is measurable over thousands of objective-function + evaluations. """ self._value = v parent_datablock = self._datablock_item() @@ -390,9 +388,8 @@ def __init__( ) -> None: """String descriptor bound to a CIF handler. - Args: - cif_handler: Object that tracks CIF identifiers. - **kwargs: Forwarded to GenericStringDescriptor. + Args: cif_handler: Object that tracks CIF identifiers. + **kwargs: Forwarded to GenericStringDescriptor. """ super().__init__(**kwargs) self._cif_handler = cif_handler @@ -411,9 +408,8 @@ def __init__( ) -> None: """Numeric descriptor bound to a CIF handler. - Args: - cif_handler: Object that tracks CIF identifiers. - **kwargs: Forwarded to GenericNumericDescriptor. + Args: cif_handler: Object that tracks CIF identifiers. + **kwargs: Forwarded to GenericNumericDescriptor. """ super().__init__(**kwargs) self._cif_handler = cif_handler @@ -432,9 +428,8 @@ def __init__( ) -> None: """Fittable parameter bound to a CIF handler. - Args: - cif_handler: Object that tracks CIF identifiers. - **kwargs: Forwarded to GenericParameter. + Args: cif_handler: Object that tracks CIF identifiers. + **kwargs: Forwarded to GenericParameter. """ super().__init__(**kwargs) self._cif_handler = cif_handler diff --git a/src/easydiffraction/crystallography/crystallography.py b/src/easydiffraction/crystallography/crystallography.py index 529a3221..472c96ca 100644 --- a/src/easydiffraction/crystallography/crystallography.py +++ b/src/easydiffraction/crystallography/crystallography.py @@ -24,12 +24,10 @@ def apply_cell_symmetry_constraints( """Apply symmetry constraints to unit cell parameters based on space group. - Args: - cell: Dictionary containing lattice parameters. - name_hm: Hermann-Mauguin symbol of the space group. + Args: cell: Dictionary containing lattice parameters. name_hm: + Hermann-Mauguin symbol of the space group. - Returns: - The cell dictionary with applied symmetry constraints. + Returns: The cell dictionary with applied symmetry constraints. """ it_number = get_it_number_by_name_hm_short(name_hm) if it_number is None: @@ -93,14 +91,12 @@ def apply_atom_site_symmetry_constraints( """Apply symmetry constraints to atomic coordinates based on site symmetry. - Args: - atom_site: Dictionary containing atom position data. - name_hm: Hermann-Mauguin symbol of the space group. - coord_code: Coordinate system code. - wyckoff_letter: Wyckoff position letter. + Args: atom_site: Dictionary containing atom position data. + name_hm: Hermann-Mauguin symbol of the space group. coord_code: + Coordinate system code. wyckoff_letter: Wyckoff position letter. - Returns: - The atom_site dictionary with applied symmetry constraints. + Returns: The atom_site dictionary with applied symmetry + constraints. """ it_number = get_it_number_by_name_hm_short(name_hm) if it_number is None: diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 66d803e0..b2b0584f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -88,8 +88,8 @@ def id(self) -> StringDescriptor: """Identifier for this background polynomial term. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._id @@ -102,8 +102,8 @@ def order(self) -> NumericDescriptor: """Order used in a Chebyshev polynomial background term. Reading this property returns the underlying - ``NumericDescriptor` object. - Assigning to it updates the parameter value. + ``NumericDescriptor`` object. Assigning to it updates the + parameter value. """ return self._order @@ -115,9 +115,8 @@ def order(self, value: float) -> None: def coef(self) -> Parameter: """Coefficient used in a Chebyshev polynomial background term. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._coef diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index f4fc2f6a..a74f0c11 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -97,8 +97,8 @@ def id(self) -> StringDescriptor: """Identifier for this background line segment. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._id @@ -108,11 +108,12 @@ def id(self, value: str) -> None: @property def x(self) -> NumericDescriptor: - """X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. + """X-coordinates used to create many straight-line segments + representing the background in a calculated diffractogram. Reading this property returns the underlying - ``NumericDescriptor` object. - Assigning to it updates the parameter value. + ``NumericDescriptor`` object. Assigning to it updates the + parameter value. """ return self._x @@ -122,11 +123,11 @@ def x(self, value: float) -> None: @property def y(self) -> Parameter: - """Intensity used to create many straight-line segments representing the background in a calculated diffractogram. + """Intensity used to create many straight-line segments + representing the background in a calculated diffractogram. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._y diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 56a63c2d..3aa6813b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -126,43 +126,46 @@ def point_id(self) -> StringDescriptor: """Identifier for this data point in the dataset. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._point_id @property def d_spacing(self) -> NumericDescriptor: - """d-spacing value corresponding to this data point. + """D-spacing value corresponding to this data point. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._d_spacing @property def intensity_meas(self) -> NumericDescriptor: - """Intensity recorded at each measurement point as a function of angle/time. + """Intensity recorded at each measurement point as a function of + angle/time. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._intensity_meas @property def intensity_meas_su(self) -> NumericDescriptor: - """Standard uncertainty of the measured intensity at this data point. + """Standard uncertainty of the measured intensity at this data + point. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._intensity_meas_su @property def intensity_calc(self) -> NumericDescriptor: - """Intensity value for a computed diffractogram at this data point. + """Intensity value for a computed diffractogram at this data + point. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._intensity_calc @@ -171,7 +174,7 @@ def intensity_bkg(self) -> NumericDescriptor: """Intensity value for a computed background at this data point. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._intensity_bkg @@ -180,7 +183,7 @@ def calc_status(self) -> StringDescriptor: """Status code of the data point in the calculation process. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._calc_status @@ -218,7 +221,7 @@ def two_theta(self) -> NumericDescriptor: """Measured 2θ diffraction angle (deg). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._two_theta @@ -249,7 +252,7 @@ def time_of_flight(self) -> NumericDescriptor: """Measured time for time-of-flight neutron measurement (µs). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._time_of_flight diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 3e2c2ac6..e229797a 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -142,16 +142,17 @@ def id(self) -> StringDescriptor: """Identifier of the reflection. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._id @property def d_spacing(self) -> NumericDescriptor: - """The distance between lattice planes in the crystal for this reflection (Å). + """The distance between lattice planes in the crystal for this + reflection (Å). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._d_spacing @@ -160,7 +161,7 @@ def sin_theta_over_lambda(self) -> NumericDescriptor: """The sin(θ)/λ value for this reflection (Å⁻¹). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._sin_theta_over_lambda @@ -169,7 +170,7 @@ def index_h(self) -> NumericDescriptor: """Miller index h of a measured reflection. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._index_h @@ -178,7 +179,7 @@ def index_k(self) -> NumericDescriptor: """Miller index k of a measured reflection. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._index_k @@ -187,16 +188,17 @@ def index_l(self) -> NumericDescriptor: """Miller index l of a measured reflection. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._index_l @property def intensity_meas(self) -> NumericDescriptor: - """The intensity of the reflection derived from the measurements. + """The intensity of the reflection derived from the + measurements. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._intensity_meas @@ -205,25 +207,27 @@ def intensity_meas_su(self) -> NumericDescriptor: """Standard uncertainty of the measured intensity. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._intensity_meas_su @property def intensity_calc(self) -> NumericDescriptor: - """The intensity of the reflection calculated from the atom site data. + """The intensity of the reflection calculated from the atom site + data. Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._intensity_calc @property def wavelength(self) -> NumericDescriptor: - """The mean wavelength of radiation used to measure this reflection (Å). + """The mean wavelength of radiation used to measure this + reflection (Å). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._wavelength diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index 7ecffbca..1b5d867c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -125,7 +125,7 @@ def point_id(self) -> StringDescriptor: """Identifier for this data point in the dataset. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._point_id @@ -134,7 +134,7 @@ def r(self) -> NumericDescriptor: """Interatomic distance in real space (Å). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._r @@ -143,7 +143,7 @@ def g_r_meas(self) -> NumericDescriptor: """Measured pair distribution function G(r). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._g_r_meas @@ -152,7 +152,7 @@ def g_r_meas_su(self) -> NumericDescriptor: """Standard uncertainty of measured G(r). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._g_r_meas_su @@ -161,7 +161,7 @@ def g_r_calc(self) -> NumericDescriptor: """Calculated pair distribution function G(r). Reading this property returns the underlying - ``NumericDescriptor` object. + ``NumericDescriptor`` object. """ return self._g_r_calc @@ -170,7 +170,7 @@ def calc_status(self) -> StringDescriptor: """Status code of the data point in calculation. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._calc_status @@ -305,8 +305,8 @@ def intensity_bkg(self) -> np.ndarray: class TotalData(TotalDataBase): """Total scattering (PDF) data collection in r-space. - Note: Works for both CWL and TOF measurements as PDF data - is always transformed to r-space. + Note: Works for both CWL and TOF measurements as PDF data is always + transformed to r-space. """ type_info = TypeInfo(tag='total-pd', description='Total scattering (PDF) data') diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 839f1c08..51492d9e 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -75,8 +75,8 @@ def id(self) -> StringDescriptor: """Identifier for this excluded region. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._id @@ -89,8 +89,8 @@ def start(self) -> NumericDescriptor: """Start of the excluded region. Reading this property returns the underlying - ``NumericDescriptor` object. - Assigning to it updates the parameter value. + ``NumericDescriptor`` object. Assigning to it updates the + parameter value. """ return self._start @@ -103,8 +103,8 @@ def end(self) -> NumericDescriptor: """End of the excluded region. Reading this property returns the underlying - ``NumericDescriptor` object. - Assigning to it updates the parameter value. + ``NumericDescriptor`` object. Assigning to it updates the + parameter value. """ return self._end diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 3ebbf94c..c2176f29 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: BSD-3-Clause """Experiment type descriptor (form, beam, probe, scattering). -This lightweight container stores the categorical attributes defining -an experiment configuration and handles CIF serialization via +This lightweight container stores the categorical attributes defining an +experiment configuration and handles CIF serialization via ``CifHandler``. """ @@ -28,11 +28,9 @@ class ExperimentType(CategoryItem): """Container of categorical attributes defining experiment flavor. - Args: - sample_form: Powder or Single crystal. - beam_mode: Constant wavelength (CW) or time-of-flight (TOF). - radiation_probe: Neutrons or X-rays. - scattering_type: Bragg or Total. + Args: sample_form: Powder or Single crystal. beam_mode: + Constant wavelength (CW) or time-of-flight (TOF). radiation_probe: + Neutrons or X-rays. scattering_type: Bragg or Total. """ type_info = TypeInfo( @@ -113,19 +111,21 @@ def _set_scattering_type(self, value: str) -> None: @property def sample_form(self) -> StringDescriptor: - """Specifies whether the diffraction data corresponds to powder diffraction or single crystal diffraction. + """Specifies whether the diffraction data corresponds to powder + diffraction or single crystal diffraction. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._sample_form @property def beam_mode(self) -> StringDescriptor: - """Defines whether the measurement is performed with a constant wavelength (CW) or time-of-flight (TOF) method. + """Defines whether the measurement is performed with a constant + wavelength (CW) or time-of-flight (TOF) method. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._beam_mode @@ -134,15 +134,18 @@ def radiation_probe(self) -> StringDescriptor: """Specifies whether the measurement uses neutrons or X-rays. Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._radiation_probe @property def scattering_type(self) -> StringDescriptor: - """Specifies whether the experiment uses Bragg scattering (for conventional structure refinement) or total scattering (for pair distribution function analysis - PDF). + """Specifies whether the experiment uses Bragg scattering (for. + + conventional structure refinement) or total scattering (for pair + distribution function analysis - PDF). Reading this property returns the underlying - ``StringDescriptor` object. + ``StringDescriptor`` object. """ return self._scattering_type diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index 7d8f45bc..6a5e3118 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -71,9 +71,8 @@ def __init__(self) -> None: def mosaicity(self) -> Parameter: """Mosaicity value for extinction correction (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._mosaicity @@ -85,9 +84,8 @@ def mosaicity(self, value: float) -> None: def radius(self) -> Parameter: """Crystal radius for extinction correction (µm). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._radius diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index df527fda..913e74f6 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -39,9 +39,8 @@ def __init__(self) -> None: def setup_wavelength(self) -> Parameter: """Incident neutron or X-ray wavelength (Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._setup_wavelength @@ -104,9 +103,8 @@ def __init__(self) -> None: def calib_twotheta_offset(self) -> Parameter: """Instrument misalignment offset (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._calib_twotheta_offset diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index ce9563ae..76f073b9 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -102,9 +102,8 @@ def __init__(self) -> None: def setup_twotheta_bank(self) -> Parameter: """Detector bank position (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._setup_twotheta_bank @@ -116,9 +115,8 @@ def setup_twotheta_bank(self, value: float) -> None: def calib_d_to_tof_offset(self) -> Parameter: """TOF offset (µs). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._calib_d_to_tof_offset @@ -130,9 +128,8 @@ def calib_d_to_tof_offset(self, value: float) -> None: def calib_d_to_tof_linear(self) -> Parameter: """TOF linear conversion (µs/Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._calib_d_to_tof_linear @@ -144,9 +141,8 @@ def calib_d_to_tof_linear(self, value: float) -> None: def calib_d_to_tof_quad(self) -> Parameter: """TOF quadratic correction (µs/Ų). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._calib_d_to_tof_quad @@ -158,9 +154,8 @@ def calib_d_to_tof_quad(self, value: float) -> None: def calib_d_to_tof_recip(self) -> Parameter: """TOF reciprocal velocity correction (µs·Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._calib_d_to_tof_recip diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index 7419671f..d0d46304 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -66,8 +66,8 @@ def id(self) -> StringDescriptor: """Identifier of the linked crystal. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._id @@ -79,9 +79,8 @@ def id(self, value: str) -> None: def scale(self) -> Parameter: """Scale factor of the linked crystal. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._scale diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index 60723ba9..8587fd00 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -57,8 +57,8 @@ def id(self) -> StringDescriptor: """Identifier of the linked phase. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._id @@ -70,9 +70,8 @@ def id(self, value: str) -> None: def scale(self) -> Parameter: """Scale factor of the linked phase. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._scale diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 6ec1c8ee..c389f571 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -78,11 +78,11 @@ def __init__(self): @property def broad_gauss_u(self) -> Parameter: - """Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). + """Gaussian broadening coefficient (dependent on sample size and + instrument resolution) (deg²). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_gauss_u @@ -92,11 +92,11 @@ def broad_gauss_u(self, value: float) -> None: @property def broad_gauss_v(self) -> Parameter: - """Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + """Gaussian broadening coefficient (instrumental broadening + contribution) (deg²). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_gauss_v @@ -106,11 +106,11 @@ def broad_gauss_v(self, value: float) -> None: @property def broad_gauss_w(self) -> Parameter: - """Gaussian broadening coefficient (instrumental broadening contribution) (deg²). + """Gaussian broadening coefficient (instrumental broadening + contribution) (deg²). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_gauss_w @@ -120,11 +120,11 @@ def broad_gauss_w(self, value: float) -> None: @property def broad_lorentz_x(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on sample strain effects) (deg). + """Lorentzian broadening coefficient (dependent on sample strain + effects) (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_lorentz_x @@ -134,11 +134,11 @@ def broad_lorentz_x(self, value: float) -> None: @property def broad_lorentz_y(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on microstructural defects and strain) (deg). + """Lorentzian broadening coefficient (dependent on + microstructural defects and strain) (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_lorentz_y @@ -202,9 +202,8 @@ def __init__(self): def asym_empir_1(self) -> Parameter: """Empirical asymmetry coefficient p1. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_empir_1 @@ -216,9 +215,8 @@ def asym_empir_1(self, value: float) -> None: def asym_empir_2(self) -> Parameter: """Empirical asymmetry coefficient p2. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_empir_2 @@ -230,9 +228,8 @@ def asym_empir_2(self, value: float) -> None: def asym_empir_3(self) -> Parameter: """Empirical asymmetry coefficient p3. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_empir_3 @@ -244,9 +241,8 @@ def asym_empir_3(self, value: float) -> None: def asym_empir_4(self) -> Parameter: """Empirical asymmetry coefficient p4. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_empir_4 @@ -290,9 +286,8 @@ def __init__(self): def asym_fcj_1(self) -> Parameter: """Finger-Cox-Jephcoat asymmetry parameter 1. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_fcj_1 @@ -304,9 +299,8 @@ def asym_fcj_1(self, value: float) -> None: def asym_fcj_2(self) -> Parameter: """Finger-Cox-Jephcoat asymmetry parameter 2. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_fcj_2 diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index 1e7468e4..9f2bcabb 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -110,11 +110,11 @@ def __init__(self): @property def broad_gauss_sigma_0(self) -> Parameter: - """Gaussian broadening coefficient (instrumental resolution) (µs²). + """Gaussian broadening coefficient (instrumental resolution) + (µs²). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_gauss_sigma_0 @@ -124,11 +124,11 @@ def broad_gauss_sigma_0(self, value: float) -> None: @property def broad_gauss_sigma_1(self) -> Parameter: - """Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). + """Gaussian broadening coefficient (dependent on d-spacing) + (µs/Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_gauss_sigma_1 @@ -138,11 +138,11 @@ def broad_gauss_sigma_1(self, value: float) -> None: @property def broad_gauss_sigma_2(self) -> Parameter: - """Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). + """Gaussian broadening coefficient (instrument-dependent term) + (µs²/Ų). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_gauss_sigma_2 @@ -152,11 +152,11 @@ def broad_gauss_sigma_2(self, value: float) -> None: @property def broad_lorentz_gamma_0(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on microstrain effects) (µs). + """Lorentzian broadening coefficient (dependent on microstrain + effects) (µs). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_lorentz_gamma_0 @@ -166,11 +166,11 @@ def broad_lorentz_gamma_0(self, value: float) -> None: @property def broad_lorentz_gamma_1(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). + """Lorentzian broadening coefficient (dependent on d-spacing) + (µs/Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_lorentz_gamma_1 @@ -180,11 +180,11 @@ def broad_lorentz_gamma_1(self, value: float) -> None: @property def broad_lorentz_gamma_2(self) -> Parameter: - """Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). + """Lorentzian broadening coefficient (instrument-dependent term) + (µs²/Ų). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_lorentz_gamma_2 @@ -194,11 +194,11 @@ def broad_lorentz_gamma_2(self, value: float) -> None: @property def broad_mix_beta_0(self) -> Parameter: - """Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + """Mixing parameter. Defines the ratio of Gaussian to Lorentzian + contributions in TOF profiles (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_mix_beta_0 @@ -208,11 +208,11 @@ def broad_mix_beta_0(self, value: float) -> None: @property def broad_mix_beta_1(self) -> Parameter: - """Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). + """Mixing parameter. Defines the ratio of Gaussian to Lorentzian + contributions in TOF profiles (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_mix_beta_1 @@ -252,9 +252,8 @@ def __init__(self): def asym_alpha_0(self) -> Parameter: """Ikeda-Carpenter asymmetry parameter α₀. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_alpha_0 @@ -266,9 +265,8 @@ def asym_alpha_0(self, value: float) -> None: def asym_alpha_1(self) -> Parameter: """Ikeda-Carpenter asymmetry parameter α₁. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._asym_alpha_1 diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 94e1f202..3c2f65a7 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -90,11 +90,11 @@ def __init__(self): @property def damp_q(self) -> Parameter: - """Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). + """Instrumental Q-resolution damping factor (affects high-r PDF + peak amplitude) (Å⁻¹). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._damp_q @@ -104,11 +104,11 @@ def damp_q(self, value: float) -> None: @property def broad_q(self) -> Parameter: - """Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). + """Quadratic PDF peak broadening coefficient (thermal and model + uncertainty contribution) (Å⁻²). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._broad_q @@ -118,11 +118,11 @@ def broad_q(self, value: float) -> None: @property def cutoff_q(self) -> Parameter: - """Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). + """Q-value cutoff applied to model PDF for Fourier transform + (controls real-space resolution) (Å⁻¹). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._cutoff_q @@ -134,9 +134,8 @@ def cutoff_q(self, value: float) -> None: def sharp_delta_1(self) -> Parameter: """PDF peak sharpening coefficient (1/r dependence) (Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._sharp_delta_1 @@ -148,9 +147,8 @@ def sharp_delta_1(self, value: float) -> None: def sharp_delta_2(self) -> Parameter: """PDF peak sharpening coefficient (1/r² dependence) (Ų). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._sharp_delta_2 @@ -160,11 +158,11 @@ def sharp_delta_2(self, value: float) -> None: @property def damp_particle_diameter(self) -> Parameter: - """Particle diameter for spherical envelope damping correction in PDF (Å). + """Particle diameter for spherical envelope damping correction + in PDF (Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._damp_particle_diameter diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index 4f690399..404886bb 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -37,12 +37,11 @@ def create( ) -> None: """Add an experiment without associating a data file. - Args: - name: Experiment identifier. - sample_form: Sample form (e.g. ``'powder'``). - beam_mode: Beam mode (e.g. ``'constant wavelength'``). - radiation_probe: Radiation probe (e.g. ``'neutron'``). - scattering_type: Scattering type (e.g. ``'bragg'``). + Args: name: Experiment identifier. sample_form: Sample + form (e.g. ``'powder'``). beam_mode: Beam mode (e.g. + ``'constant wavelength'``). radiation_probe: Radiation probe + (e.g. ``'neutron'``). scattering_type: Scattering type (e.g. + ``'bragg'``). """ experiment = ExperimentFactory.from_scratch( name=name, @@ -61,8 +60,7 @@ def add_from_cif_str( ) -> None: """Add an experiment from a CIF string. - Args: - cif_str: Full CIF document as a string. + Args: cif_str: Full CIF document as a string. """ experiment = ExperimentFactory.from_cif_str(cif_str) self.add(experiment) @@ -75,8 +73,7 @@ def add_from_cif_path( ) -> None: """Add an experiment from a CIF file path. - Args: - cif_path(str): Path to a CIF document. + Args: cif_path(str): Path to a CIF document. """ experiment = ExperimentFactory.from_cif_path(cif_path) self.add(experiment) @@ -94,13 +91,12 @@ def add_from_data_path( ) -> None: """Add an experiment from a data file path. - Args: - name: Experiment identifier. - data_path: Path to the measured data file. - sample_form: Sample form (e.g. ``'powder'``). - beam_mode: Beam mode (e.g. ``'constant wavelength'``). - radiation_probe: Radiation probe (e.g. ``'neutron'``). - scattering_type: Scattering type (e.g. ``'bragg'``). + Args: name: Experiment identifier. data_path: Path to + the measured data file. sample_form: Sample form (e.g. + ``'powder'``). beam_mode: Beam mode (e.g. ``'constant + wavelength'``). radiation_probe: Radiation probe (e.g. + ``'neutron'``). scattering_type: Scattering type (e.g. + ``'bragg'``). """ experiment = ExperimentFactory.from_data_path( name=name, diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 8672f673..19075a94 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -60,8 +60,7 @@ def name(self) -> str: def name(self, new: str) -> None: """Rename the experiment. - Args: - new: New name for this experiment. + Args: new: New name for this experiment. """ self._name = new @@ -88,8 +87,7 @@ def show_as_cif(self) -> None: def _load_ascii_data_to_experiment(self, data_path: str) -> None: """Load ASCII data from file into the experiment data category. - Args: - data_path: Path to the ASCII file to load. + Args: data_path: Path to the ASCII file to load. """ raise NotImplementedError() @@ -120,9 +118,8 @@ def calculator_type(self) -> str: def calculator_type(self, tag: str) -> None: """Switch to a different calculator backend. - Args: - tag: Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``, - ``'pdffit'``). + Args: tag: Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``, + ``'pdffit'``). """ from easydiffraction.analysis.calculators.factory import CalculatorFactory @@ -233,9 +230,8 @@ def __init__( def _load_ascii_data_to_experiment(self, data_path: str) -> None: """Load single crystal data from an ASCII file. - Args: - data_path: Path to data file with columns compatible with - the beam mode. + Args: data_path: Path to data file with columns compatible + with the beam mode. """ pass @@ -257,8 +253,7 @@ def extinction_type(self) -> str: def extinction_type(self, new_type: str) -> None: """Switch to a different extinction correction model. - Args: - new_type: Extinction tag (e.g. ``'shelx'``). + Args: new_type: Extinction tag (e.g. ``'shelx'``). """ supported_tags = ExtinctionFactory.supported_tags() if new_type not in supported_tags: @@ -301,8 +296,7 @@ def linked_crystal_type(self) -> str: def linked_crystal_type(self, new_type: str) -> None: """Switch to a different linked-crystal reference type. - Args: - new_type: Linked-crystal tag (e.g. ``'default'``). + Args: new_type: Linked-crystal tag (e.g. ``'default'``). """ supported_tags = LinkedCrystalFactory.supported_tags() if new_type not in supported_tags: @@ -345,8 +339,7 @@ def instrument_type(self) -> str: def instrument_type(self, new_type: str) -> None: """Switch to a different instrument type. - Args: - new_type: Instrument tag (e.g. ``'cwl-sc'``). + Args: new_type: Instrument tag (e.g. ``'cwl-sc'``). """ supported = InstrumentFactory.supported_for( scattering_type=self.type.scattering_type.value, @@ -397,8 +390,7 @@ def data_type(self) -> str: def data_type(self, new_type: str) -> None: """Switch to a different data collection type. - Args: - new_type: Data tag (e.g. ``'bragg-sc'``). + Args: new_type: Data tag (e.g. ``'bragg-sc'``). """ supported_tags = DataFactory.supported_tags() if new_type not in supported_tags: @@ -456,11 +448,9 @@ def _get_valid_linked_phases( ) -> List[Any]: """Get valid linked phases for this experiment. - Args: - structures: Collection of structures. + Args: structures: Collection of structures. - Returns: - A list of valid linked phases. + Returns: A list of valid linked phases. """ if not self.linked_phases: print('Warning: No linked phases defined. Returning empty pattern.') @@ -487,9 +477,9 @@ def _get_valid_linked_phases( def _load_ascii_data_to_experiment(self, data_path: str) -> None: """Load powder diffraction data from an ASCII file. - Args: - data_path: Path to data file with columns compatible with - the beam mode (e.g. 2θ/I/σ for CWL, TOF/I/σ for TOF). + Args: data_path: Path to data file with columns compatible + with the beam mode (e.g. 2θ/I/σ for CWL, TOF/I/σ for + TOF). """ pass @@ -507,8 +497,7 @@ def linked_phases_type(self) -> str: def linked_phases_type(self, new_type: str) -> None: """Switch to a different linked-phases collection type. - Args: - new_type: Linked-phases tag (e.g. ``'default'``). + Args: new_type: Linked-phases tag (e.g. ``'default'``). """ supported_tags = LinkedPhasesFactory.supported_tags() if new_type not in supported_tags: @@ -547,8 +536,7 @@ def excluded_regions_type(self) -> str: def excluded_regions_type(self, new_type: str) -> None: """Switch to a different excluded-regions collection type. - Args: - new_type: Excluded-regions tag (e.g. ``'default'``). + Args: new_type: Excluded-regions tag (e.g. ``'default'``). """ supported_tags = ExcludedRegionsFactory.supported_tags() if new_type not in supported_tags: @@ -593,8 +581,7 @@ def data_type(self) -> str: def data_type(self, new_type: str) -> None: """Switch to a different data collection type. - Args: - new_type: Data tag (e.g. ``'bragg-pd-cwl'``). + Args: new_type: Data tag (e.g. ``'bragg-pd-cwl'``). """ supported_tags = DataFactory.supported_tags() if new_type not in supported_tags: @@ -632,8 +619,7 @@ def peak_profile_type(self): def peak_profile_type(self, new_type: str): """Change the active peak profile type, if supported. - Args: - new_type: New profile type as tag string. + Args: new_type: New profile type as tag string. """ supported = PeakFactory.supported_for( scattering_type=self.type.scattering_type.value, diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index 5434a531..ca331fc8 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -118,8 +118,7 @@ def instrument_type(self) -> str: def instrument_type(self, new_type: str) -> None: """Switch to a different instrument type. - Args: - new_type: Instrument tag (e.g. ``'cwl-pd'``). + Args: new_type: Instrument tag (e.g. ``'cwl-pd'``). """ supported = InstrumentFactory.supported_for( scattering_type=self.type.scattering_type.value, diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py index e4db951f..1f0bc9cc 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py @@ -48,8 +48,8 @@ def __init__( def _load_ascii_data_to_experiment(self, data_path: str) -> None: """Load measured data from an ASCII file into the data category. - The file format is space/column separated with 5 columns: - ``h k l Iobs sIobs``. + The file format is space/column separated with 5 columns: ``h k + l Iobs sIobs``. """ try: data = np.loadtxt(data_path) @@ -112,8 +112,8 @@ def __init__( def _load_ascii_data_to_experiment(self, data_path: str) -> None: """Load measured data from an ASCII file into the data category. - The file format is space/column separated with 6 columns: - ``h k l Iobs sIobs wavelength``. + The file format is space/column separated with 6 columns: ``h k + l Iobs sIobs wavelength``. """ try: data = np.loadtxt(data_path) diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 91fe88be..650d86d4 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -142,15 +142,13 @@ def from_scratch( ) -> ExperimentBase: """Create an experiment without measured data. - Args: - name: Experiment identifier. - sample_form: Sample form (e.g. ``'powder'``). - beam_mode: Beam mode (e.g. ``'constant wavelength'``). - radiation_probe: Radiation probe (e.g. ``'neutron'``). - scattering_type: Scattering type (e.g. ``'bragg'``). - - Returns: - An experiment instance with only metadata. + Args: name: Experiment identifier. sample_form: Sample + form (e.g. ``'powder'``). beam_mode: Beam mode (e.g. + ``'constant wavelength'``). radiation_probe: Radiation probe + (e.g. ``'neutron'``). scattering_type: Scattering type (e.g. + ``'bragg'``). + + Returns: An experiment instance with only metadata. """ expt_type = cls._create_experiment_type( sample_form=sample_form, @@ -171,11 +169,9 @@ def from_cif_str( ) -> ExperimentBase: """Create an experiment from a CIF string. - Args: - cif_str: Full CIF document as a string. + Args: cif_str: Full CIF document as a string. - Returns: - A populated experiment instance. + Returns: A populated experiment instance. """ doc = document_from_string(cif_str) block = pick_sole_block(doc) @@ -190,11 +186,9 @@ def from_cif_path( ) -> ExperimentBase: """Create an experiment from a CIF file path. - Args: - cif_path: Path to a CIF file. + Args: cif_path: Path to a CIF file. - Returns: - A populated experiment instance. + Returns: A populated experiment instance. """ doc = document_from_path(cif_path) block = pick_sole_block(doc) @@ -214,16 +208,14 @@ def from_data_path( ) -> ExperimentBase: """Create an experiment from a raw data ASCII file. - Args: - name: Experiment identifier. - data_path: Path to the measured data file. - sample_form: Sample form (e.g. ``'powder'``). - beam_mode: Beam mode (e.g. ``'constant wavelength'``). - radiation_probe: Radiation probe (e.g. ``'neutron'``). - scattering_type: Scattering type (e.g. ``'bragg'``). + Args: name: Experiment identifier. data_path: Path to + the measured data file. sample_form: Sample form (e.g. + ``'powder'``). beam_mode: Beam mode (e.g. ``'constant + wavelength'``). radiation_probe: Radiation probe (e.g. + ``'neutron'``). scattering_type: Scattering type (e.g. + ``'bragg'``). - Returns: - An experiment instance with measured data attached. + Returns: An experiment instance with measured data attached. """ expt_obj = cls.from_scratch( name=name, diff --git a/src/easydiffraction/datablocks/experiment/item/total_pd.py b/src/easydiffraction/datablocks/experiment/item/total_pd.py index 7a2c55f3..33390617 100644 --- a/src/easydiffraction/datablocks/experiment/item/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/total_pd.py @@ -45,8 +45,7 @@ def _load_ascii_data_to_experiment(self, data_path): """Loads x, y, sy values from an ASCII data file into the experiment. - The file must be structured as: - x y sy + The file must be structured as: x y sy """ try: from diffpy.utils.parsers.loaddata import loadData diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index ef9dd0aa..4d790f5e 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -140,8 +140,8 @@ def __init__(self) -> None: def _type_symbol_allowed_values(self) -> list[str]: """Return chemical symbols accepted by *cryspy*. - Returns: - list[str]: Unique element/isotope symbols from the database. + Returns: list[str]: Unique element/isotope symbols from the + database. """ return list({key[1] for key in DATABASE['Isotopes']}) @@ -149,8 +149,7 @@ def _type_symbol_allowed_values(self) -> list[str]: def _wyckoff_letter_allowed_values(self) -> list[str]: """Return allowed Wyckoff-letter symbols. - Returns: - list[str]: Currently a hard-coded placeholder list. + Returns: list[str]: Currently a hard-coded placeholder list. """ # TODO: Need to now current space group. How to access it? Via # parent Cell? Then letters = @@ -162,8 +161,7 @@ def _wyckoff_letter_allowed_values(self) -> list[str]: def _wyckoff_letter_default_value(self) -> str: """Return the default Wyckoff letter. - Returns: - str: First element of the allowed values list. + Returns: str: First element of the allowed values list. """ # TODO: What to pass as default? return self._wyckoff_letter_allowed_values[0] @@ -177,8 +175,8 @@ def label(self) -> StringDescriptor: """Unique identifier for the atom site. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._label @@ -191,8 +189,8 @@ def type_symbol(self) -> StringDescriptor: """Chemical symbol of the atom at this site. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._type_symbol @@ -202,11 +200,12 @@ def type_symbol(self, value: str) -> None: @property def adp_type(self) -> StringDescriptor: - """Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). + """Type of atomic displacement parameter (ADP) used (e.g., Biso, + Uiso, Uani, Bani). Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._adp_type @@ -216,11 +215,12 @@ def adp_type(self, value: str) -> None: @property def wyckoff_letter(self) -> StringDescriptor: - """Wyckoff letter indicating the symmetry of the atom site within the space group. + """Wyckoff letter indicating the symmetry of the atom site + within the space group. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._wyckoff_letter @@ -230,11 +230,11 @@ def wyckoff_letter(self, value: str) -> None: @property def fract_x(self) -> Parameter: - """Fractional x-coordinate of the atom site within the unit cell. + """Fractional x-coordinate of the atom site within the unit + cell. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._fract_x @@ -244,11 +244,11 @@ def fract_x(self, value: float) -> None: @property def fract_y(self) -> Parameter: - """Fractional y-coordinate of the atom site within the unit cell. + """Fractional y-coordinate of the atom site within the unit + cell. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._fract_y @@ -258,11 +258,11 @@ def fract_y(self, value: float) -> None: @property def fract_z(self) -> Parameter: - """Fractional z-coordinate of the atom site within the unit cell. + """Fractional z-coordinate of the atom site within the unit + cell. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._fract_z @@ -272,11 +272,11 @@ def fract_z(self, value: float) -> None: @property def occupancy(self) -> Parameter: - """Occupancy of the atom site, representing the fraction of the site occupied by the atom type. + """Occupancy of the atom site, representing the fraction of the + site occupied by the atom type. - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._occupancy @@ -286,11 +286,11 @@ def occupancy(self, value: float) -> None: @property def b_iso(self) -> Parameter: - """Isotropic atomic displacement parameter (ADP) for the atom site (Ų). + """Isotropic atomic displacement parameter (ADP) for the atom + site (Ų). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._b_iso @@ -352,9 +352,8 @@ def _update( ) -> None: """Recalculate atom sites after a change. - Args: - called_by_minimizer (bool): Whether the update was triggered - by the fitting minimizer. Currently unused. + Args: called_by_minimizer (bool): Whether the update was + triggered by the fitting minimizer. Currently unused. """ del called_by_minimizer diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 953b2a29..2d0f4faa 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -134,10 +134,8 @@ def _update( ) -> None: """Recalculate cell parameters after a change. - Args: - called_by_minimizer (bool, default=False): Whether the - update was triggered by the fitting minimizer. Currently - unused. + Args: called_by_minimizer (bool, default=False): Whether the + update was triggered by the fitting minimizer. Currently unused. """ del called_by_minimizer # TODO: ??? @@ -151,9 +149,8 @@ def _update( def length_a(self) -> Parameter: """Length of the a axis of the unit cell (Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._length_a @@ -165,9 +162,8 @@ def length_a(self, value: float) -> None: def length_b(self) -> Parameter: """Length of the b axis of the unit cell (Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._length_b @@ -179,9 +175,8 @@ def length_b(self, value: float) -> None: def length_c(self) -> Parameter: """Length of the c axis of the unit cell (Å). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._length_c @@ -193,9 +188,8 @@ def length_c(self, value: float) -> None: def angle_alpha(self) -> Parameter: """Angle between edges b and c (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._angle_alpha @@ -207,9 +201,8 @@ def angle_alpha(self, value: float) -> None: def angle_beta(self) -> Parameter: """Angle between edges a and c (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._angle_beta @@ -221,9 +214,8 @@ def angle_beta(self, value: float) -> None: def angle_gamma(self) -> Parameter: """Angle between edges a and b (deg). - Reading this property returns the underlying - ``Parameter` object. - Assigning to it updates the parameter value. + Reading this property returns the underlying ``Parameter`` + object. Assigning to it updates the parameter value. """ return self._angle_gamma diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 09ab11e6..4b55b1c7 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -93,8 +93,7 @@ def _reset_it_coordinate_system_code(self) -> None: def _name_h_m_allowed_values(self) -> list[str]: """Return the list of recognised Hermann–Mauguin short symbols. - Returns: - list[str]: All short H-M symbols known to *cryspy*. + Returns: list[str]: All short H-M symbols known to *cryspy*. """ return ACCESIBLE_NAME_HM_SHORT @@ -103,9 +102,8 @@ def _it_coordinate_system_code_allowed_values(self) -> list[str]: """Return allowed IT coordinate system codes for the current group. - Returns: - list[str]: Coordinate-system codes, or ``['']`` when none - are defined. + Returns: list[str]: Coordinate-system codes, or ``['']`` + when none are defined. """ name = self.name_h_m.value it_number = get_it_number_by_name_hm_short(name) @@ -117,8 +115,7 @@ def _it_coordinate_system_code_allowed_values(self) -> list[str]: def _it_coordinate_system_code_default_value(self) -> str: """Return the default IT coordinate system code. - Returns: - str: First element of the allowed codes list. + Returns: str: First element of the allowed codes list. """ return self._it_coordinate_system_code_allowed_values[0] @@ -131,8 +128,8 @@ def name_h_m(self) -> StringDescriptor: """Hermann-Mauguin symbol of the space group. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._name_h_m @@ -146,8 +143,8 @@ def it_coordinate_system_code(self) -> StringDescriptor: """A qualifier identifying which setting in IT is used. Reading this property returns the underlying - ``StringDescriptor` object. - Assigning to it updates the parameter value. + ``StringDescriptor`` object. Assigning to it updates the + parameter value. """ return self._it_coordinate_system_code diff --git a/src/easydiffraction/datablocks/structure/collection.py b/src/easydiffraction/datablocks/structure/collection.py index 4df632f9..052b16db 100644 --- a/src/easydiffraction/datablocks/structure/collection.py +++ b/src/easydiffraction/datablocks/structure/collection.py @@ -35,8 +35,7 @@ def create( ) -> None: """Create a minimal structure and add it to the collection. - Args: - name (str): Identifier for the new structure. + Args: name (str): Identifier for the new structure. """ structure = StructureFactory.from_scratch(name=name) self.add(structure) @@ -49,8 +48,7 @@ def add_from_cif_str( ) -> None: """Create a structure from CIF content and add it. - Args: - cif_str (str): CIF file content as a string. + Args: cif_str (str): CIF file content as a string. """ structure = StructureFactory.from_cif_str(cif_str) self.add(structure) @@ -63,8 +61,7 @@ def add_from_cif_path( ) -> None: """Create a structure from a CIF file and add it. - Args: - cif_path (str): Filesystem path to a CIF file. + Args: cif_path (str): Filesystem path to a CIF file. """ structure = StructureFactory.from_cif_path(cif_path) self.add(structure) diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index ef1f67d6..0f42e968 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -42,8 +42,7 @@ def __init__( def name(self) -> str: """Name identifier for this structure. - Returns: - str: The structure's name. + Returns: str: The structure's name. """ return self._name @@ -52,8 +51,7 @@ def name(self) -> str: def name(self, new: str) -> None: """Set the name identifier for this structure. - Args: - new (str): New name string. + Args: new (str): New name string. """ self._name = new @@ -71,8 +69,7 @@ def cell(self) -> Cell: def cell(self, new: Cell) -> None: """Replace the unit-cell category for this structure. - Args: - new (Cell): New unit-cell instance. + Args: new (Cell): New unit-cell instance. """ self._cell = new @@ -85,8 +82,7 @@ def cell_type(self) -> str: def cell_type(self, new_type: str) -> None: """Switch to a different unit-cell type. - Args: - new_type: Cell tag (e.g. ``'default'``). + Args: new_type: Cell tag (e.g. ``'default'``). """ supported_tags = CellFactory.supported_tags() if new_type not in supported_tags: @@ -124,8 +120,7 @@ def space_group(self) -> SpaceGroup: def space_group(self, new: SpaceGroup) -> None: """Replace the space-group category for this structure. - Args: - new (SpaceGroup): New space-group instance. + Args: new (SpaceGroup): New space-group instance. """ self._space_group = new @@ -138,8 +133,7 @@ def space_group_type(self) -> str: def space_group_type(self, new_type: str) -> None: """Switch to a different space-group type. - Args: - new_type: Space-group tag (e.g. ``'default'``). + Args: new_type: Space-group tag (e.g. ``'default'``). """ supported_tags = SpaceGroupFactory.supported_tags() if new_type not in supported_tags: @@ -177,8 +171,7 @@ def atom_sites(self) -> AtomSites: def atom_sites(self, new: AtomSites) -> None: """Replace the atom-sites collection for this structure. - Args: - new (AtomSites): New atom-sites collection. + Args: new (AtomSites): New atom-sites collection. """ self._atom_sites = new @@ -191,8 +184,7 @@ def atom_sites_type(self) -> str: def atom_sites_type(self, new_type: str) -> None: """Switch to a different atom-sites collection type. - Args: - new_type: Atom-sites tag (e.g. ``'default'``). + Args: new_type: Atom-sites tag (e.g. ``'default'``). """ supported_tags = AtomSitesFactory.supported_tags() if new_type not in supported_tags: diff --git a/src/easydiffraction/datablocks/structure/item/factory.py b/src/easydiffraction/datablocks/structure/item/factory.py index 74bc21c7..b39164f3 100644 --- a/src/easydiffraction/datablocks/structure/item/factory.py +++ b/src/easydiffraction/datablocks/structure/item/factory.py @@ -44,11 +44,9 @@ def _from_gemmi_block( ) -> Structure: """Build a structure from a single *gemmi* CIF block. - Args: - block (gemmi.cif.Block): Parsed CIF data block. + Args: block (gemmi.cif.Block): Parsed CIF data block. - Returns: - Structure: A fully populated structure instance. + Returns: Structure: A fully populated structure instance. """ name = name_from_block(block) structure = Structure(name=name) @@ -69,11 +67,10 @@ def from_scratch( ) -> Structure: """Create a minimal default structure. - Args: - name (str): Identifier for the new structure. + Args: name (str): Identifier for the new structure. - Returns: - Structure: An empty structure with default categories. + Returns: Structure: An empty structure with default + categories. """ return Structure(name=name) @@ -86,11 +83,9 @@ def from_cif_str( ) -> Structure: """Create a structure by parsing a CIF string. - Args: - cif_str (str): Raw CIF content. + Args: cif_str (str): Raw CIF content. - Returns: - Structure: A populated structure instance. + Returns: Structure: A populated structure instance. """ doc = document_from_string(cif_str) block = pick_sole_block(doc) @@ -105,11 +100,9 @@ def from_cif_path( ) -> Structure: """Create a structure by reading and parsing a CIF file. - Args: - cif_path (str): Filesystem path to a CIF file. + Args: cif_path (str): Filesystem path to a CIF file. - Returns: - Structure: A populated structure instance. + Returns: Structure: A populated structure instance. """ doc = document_from_path(cif_path) block = pick_sole_block(doc) diff --git a/src/easydiffraction/display/__init__.py b/src/easydiffraction/display/__init__.py index 5f4f18b3..4f5877fb 100644 --- a/src/easydiffraction/display/__init__.py +++ b/src/easydiffraction/display/__init__.py @@ -2,13 +2,13 @@ # SPDX-License-Identifier: BSD-3-Clause """Display subsystem for tables and plots. -This package contains user-facing facades and backend implementations -to render tabular data and plots in different environments. +This package contains user-facing facades and backend implementations to +render tabular data and plots in different environments. - Tables: see :mod:`easydiffraction.display.tables` and the engines in - :mod:`easydiffraction.display.tablers`. -- Plots: see :mod:`easydiffraction.display.plotting` and the engines in - :mod:`easydiffraction.display.plotters`. +:mod:`easydiffraction.display.tablers`. - Plots: see +:mod:`easydiffraction.display.plotting` and the engines in +:mod:`easydiffraction.display.plotters`. """ # TODO: The following works in Jupyter, but breaks MkDocs builds. diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index bce08c7e..f862a9b4 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -93,15 +93,13 @@ class RendererFactoryBase(ABC): def create(cls, engine_name: str) -> Any: """Create a backend instance for the given engine. - Args: - engine_name: Identifier of the engine to instantiate as - listed in ``_registry()``. + Args: engine_name: Identifier of the engine to instantiate + as listed in ``_registry()``. - Returns: - A new backend instance corresponding to ``engine_name``. + Returns: A new backend instance corresponding to + ``engine_name``. - Raises: - ValueError: If the engine name is not supported. + Raises: ValueError: If the engine name is not supported. """ registry = cls._registry() if engine_name not in registry: diff --git a/src/easydiffraction/display/plotters/__init__.py b/src/easydiffraction/display/plotters/__init__.py index 31bd07bd..f365ad2c 100644 --- a/src/easydiffraction/display/plotters/__init__.py +++ b/src/easydiffraction/display/plotters/__init__.py @@ -5,6 +5,6 @@ This subpackage implements plotting engines used by the high-level plotting facade: -- :mod:`.ascii` for terminal-friendly ASCII plots. -- :mod:`.plotly` for interactive plots in notebooks or browsers. +- :mod:`.ascii` for terminal-friendly ASCII plots. - :mod:`.plotly` for +interactive plots in notebooks or browsers. """ diff --git a/src/easydiffraction/display/plotters/ascii.py b/src/easydiffraction/display/plotters/ascii.py index da11d1d2..299359b2 100644 --- a/src/easydiffraction/display/plotters/ascii.py +++ b/src/easydiffraction/display/plotters/ascii.py @@ -2,9 +2,9 @@ # SPDX-License-Identifier: BSD-3-Clause """ASCII plotting backend. -Renders compact line charts in the terminal using -``asciichartpy``. This backend is well suited for quick feedback in -CLI environments and keeps a consistent API with other plotters. +Renders compact line charts in the terminal using ``asciichartpy``. This +backend is well suited for quick feedback in CLI environments and keeps +a consistent API with other plotters. """ import asciichartpy @@ -28,14 +28,12 @@ class AsciiPlotter(PlotterBase): def _get_legend_item(self, label): """Return a colored legend entry for a given series label. - The legend uses a colored line matching the series color and - the human-readable name from :data:`SERIES_CONFIG`. + The legend uses a colored line matching the series color and the + human-readable name from :data:`SERIES_CONFIG`. - Args: - label: Series identifier (e.g., ``'meas'``). + Args: label: Series identifier (e.g., ``'meas'``). - Returns: - A formatted legend string with color escapes. + Returns: A formatted legend string with color escapes. """ color_start = DEFAULT_COLORS[label] color_end = asciichartpy.reset @@ -59,14 +57,12 @@ def plot_powder( against an x-axis variable (2θ, TOF, d-spacing). Uses ASCII characters for terminal display. - Args: - x: 1D array-like of x values (only used for range - display). - y_series: Sequence of y arrays to plot. - labels: Series identifiers corresponding to y_series. - axes_labels: Ignored; kept for API compatibility. - title: Figure title printed above the chart. - height: Number of text rows to allocate for the chart. + Args: x: 1D array-like of x values (only used for range + display). y_series: Sequence of y arrays to plot. labels: + Series identifiers corresponding to y_series. axes_labels: + Ignored; kept for API compatibility. title: Figure title + printed above the chart. height: Number of text rows to + allocate for the chart. """ # Intentionally unused; kept for a consistent display API del axes_labels @@ -104,14 +100,12 @@ def plot_single_crystal( Creates an ASCII scatter plot showing measured vs calculated values with a diagonal reference line. - Args: - x_calc: 1D array-like of calculated values (x-axis). - y_meas: 1D array-like of measured values (y-axis). - y_meas_su: 1D array-like of measurement uncertainties - (ignored in ASCII mode). - axes_labels: Pair of strings for the x and y titles. - title: Figure title. - height: Number of text rows for the chart (default: 15). + Args: x_calc: 1D array-like of calculated values (x-axis). + y_meas: 1D array-like of measured values (y-axis). y_meas_su: 1D + array-like of measurement uncertainties (ignored in ASCII mode). + axes_labels: Pair of strings for the x and y titles. title: + Figure title. height: Number of text rows for the chart + (default: 15). """ # Intentionally unused; ASCII scatter doesn't show error bars del y_meas_su diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index 0c2e044c..cde44c58 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -20,8 +20,8 @@ class XAxisType(str, Enum): """X-axis types for diffraction plots. - Values match attribute names in data models for direct use - with ``getattr(pattern, x_axis)``. + Values match attribute names in data models for direct use with + ``getattr(pattern, x_axis)``. """ TWO_THETA = 'two_theta' @@ -156,11 +156,10 @@ class PlotterBase(ABC): Implementations accept x values, multiple y-series, optional labels and render a plot to the chosen medium. - Two main plot types are supported: - - ``plot_powder``: Line plots for powder diffraction patterns - (intensity vs. 2θ/TOF/d-spacing). - - ``plot_single_crystal``: Scatter plots comparing measured vs. - calculated values (e.g., F²meas vs F²calc for single crystal). + Two main plot types are supported: - ``plot_powder``: Line plots for + powder diffraction patterns (intensity vs. 2θ/TOF/d-spacing). - + ``plot_single_crystal``: Scatter plots comparing measured vs. + calculated values (e.g., F²meas vs F²calc for single crystal). """ @abstractmethod @@ -178,13 +177,11 @@ def plot_powder( Suitable for powder diffraction data where intensity is plotted against an x-axis variable (2θ, TOF, d-spacing). - Args: - x: 1D array of x-axis values. - y_series: Sequence of y arrays to plot. - labels: Identifiers corresponding to y_series. - axes_labels: Pair of strings for the x and y titles. - title: Figure title. - height: Backend-specific height (text rows or pixels). + Args: x: 1D array of x-axis values. y_series: Sequence + of y arrays to plot. labels: Identifiers corresponding to + y_series. axes_labels: Pair of strings for the x and y + titles. title: Figure title. height: Backend-specific + height (text rows or pixels). """ pass @@ -203,12 +200,10 @@ def plot_single_crystal( Suitable for single crystal diffraction data where measured values are plotted against calculated values with error bars. - Args: - x_calc: 1D array of calculated values (x-axis). - y_meas: 1D array of measured values (y-axis). - y_meas_su: 1D array of measurement uncertainties. - axes_labels: Pair of strings for the x and y titles. - title: Figure title. - height: Backend-specific height (text rows or pixels). + Args: x_calc: 1D array of calculated values (x-axis). + y_meas: 1D array of measured values (y-axis). y_meas_su: 1D + array of measurement uncertainties. axes_labels: Pair of + strings for the x and y titles. title: Figure title. height: + Backend-specific height (text rows or pixels). """ pass diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index 62f658ec..09a5504f 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -44,14 +44,12 @@ def _get_powder_trace( ): """Create a Plotly trace for powder diffraction data. - Args: - x: 1D array-like of x-axis values. - y: 1D array-like of y-axis values. - label: Series identifier (``'meas'``, ``'calc'``, or - ``'resid'``). - - Returns: - A configured :class:`plotly.graph_objects.Scatter` trace. + Args: x: 1D array-like of x-axis values. y: 1D array- + like of y-axis values. label: Series identifier (``'meas'``, + ``'calc'``, or ``'resid'``). + + Returns: A configured :class:`plotly.graph_objects.Scatter` + trace. """ mode = SERIES_CONFIG[label]['mode'] name = SERIES_CONFIG[label]['name'] @@ -76,14 +74,12 @@ def _get_single_crystal_trace( ): """Create a Plotly trace for single crystal diffraction data. - Args: - x_calc: 1D array-like of calculated values (x-axis). - y_meas: 1D array-like of measured values (y-axis). - y_meas_su: 1D array-like of measurement uncertainties. + Args: x_calc: 1D array-like of calculated values (x-axis). + y_meas: 1D array-like of measured values (y-axis). y_meas_su: 1D + array-like of measurement uncertainties. - Returns: - A configured :class:`plotly.graph_objects.Scatter` trace - with markers and error bars. + Returns: A configured :class:`plotly.graph_objects.Scatter` + trace with markers and error bars. """ trace = go.Scatter( x=x_calc, @@ -111,8 +107,7 @@ def _get_diagonal_shape(self): Returns a y=x diagonal line spanning the plot area using paper coordinates (0,0) to (1,1). - Returns: - A dict configuring a diagonal line shape. + Returns: A dict configuring a diagonal line shape. """ return dict( type='line', @@ -129,8 +124,7 @@ def _get_diagonal_shape(self): def _get_config(self): """Return the Plotly figure configuration. - Returns: - A dict with display and mode bar settings. + Returns: A dict with display and mode bar settings. """ return dict( displaylogo=False, @@ -150,12 +144,10 @@ def _get_figure( ): """Create and configure a Plotly figure. - Args: - data: List of traces to include in the figure. - layout: Layout configuration dict. + Args: data: List of traces to include in the figure. layout: + Layout configuration dict. - Returns: - A configured :class:`plotly.graph_objects.Figure`. + Returns: A configured :class:`plotly.graph_objects.Figure`. """ fig = go.Figure(data=data, layout=layout) # Format axis ticks: @@ -173,8 +165,8 @@ def _show_figure( Renders the figure using the appropriate method for the current environment (browser for PyCharm, inline HTML for Jupyter). - Args: - fig: A :class:`plotly.graph_objects.Figure` to display. + Args: fig: A :class:`plotly.graph_objects.Figure` to + display. """ config = self._get_config() @@ -197,13 +189,11 @@ def _get_layout( ): """Create a Plotly layout configuration. - Args: - title: Figure title. - axes_labels: Pair of strings for the x and y titles. - **kwargs: Additional layout parameters (e.g., shapes). + Args: title: Figure title. axes_labels: Pair of strings + for the x and y titles. **kwargs: Additional layout + parameters (e.g., shapes). - Returns: - A configured :class:`plotly.graph_objects.Layout`. + Returns: A configured :class:`plotly.graph_objects.Layout`. """ return go.Layout( margin=dict( @@ -250,13 +240,11 @@ def plot_powder( Suitable for powder diffraction data where intensity is plotted against an x-axis variable (2θ, TOF, d-spacing). - Args: - x: 1D array-like of x-axis values. - y_series: Sequence of y arrays to plot. - labels: Series identifiers corresponding to y_series. - axes_labels: Pair of strings for the x and y titles. - title: Figure title. - height: Ignored; Plotly auto-sizes based on renderer. + Args: x: 1D array-like of x-axis values. y_series: + Sequence of y arrays to plot. labels: Series identifiers + corresponding to y_series. axes_labels: Pair of strings for + the x and y titles. title: Figure title. height: + Ignored; Plotly auto-sizes based on renderer. """ # Intentionally unused; accepted for API compatibility del height @@ -287,16 +275,14 @@ def plot_single_crystal( """Render a scatter plot for single crystal diffraction data. Suitable for single crystal diffraction data where measured - values are plotted against calculated values with error bars - and a diagonal reference line. - - Args: - x_calc: 1D array-like of calculated values (x-axis). - y_meas: 1D array-like of measured values (y-axis). - y_meas_su: 1D array-like of measurement uncertainties. - axes_labels: Pair of strings for the x and y titles. - title: Figure title. - height: Ignored; Plotly auto-sizes based on renderer. + values are plotted against calculated values with error bars and + a diagonal reference line. + + Args: x_calc: 1D array-like of calculated values (x-axis). + y_meas: 1D array-like of measured values (y-axis). y_meas_su: 1D + array-like of measurement uncertainties. axes_labels: Pair of + strings for the x and y titles. title: Figure title. height: + Ignored; Plotly auto-sizes based on renderer. """ # Intentionally unused; accepted for API compatibility del height diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 779116b3..84bcef10 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -84,14 +84,12 @@ def _auto_x_range_for_ascii(self, pattern, x_array, x_min, x_max): """For the ASCII engine, narrow the range around the tallest peak. - Args: - pattern: Data pattern object (needs ``intensity_meas``). - x_array: Full x-axis array. - x_min: Current minimum (may be ``None``). - x_max: Current maximum (may be ``None``). - - Returns: - Tuple of ``(x_min, x_max)``, possibly narrowed. + Args: pattern: Data pattern object (needs + ``intensity_meas``). x_array: Full x-axis array. x_min: + Current minimum (may be ``None``). x_max: Current maximum + (may be ``None``). + + Returns: Tuple of ``(x_min, x_max)``, possibly narrowed. """ if self._engine == 'asciichartpy' and (x_min is None or x_max is None): max_intensity_pos = np.argmax(pattern.intensity_meas) @@ -111,16 +109,13 @@ def _filtered_y_array( ): """Filter an array by the inclusive x-range limits. - Args: - y_array: 1D array-like of y values. - x_array: 1D array-like of x values (same length as - ``y_array``). - x_min: Minimum x limit (or ``None`` to use default). - x_max: Maximum x limit (or ``None`` to use default). + Args: y_array: 1D array-like of y values. x_array: 1D + array-like of x values (same length as ``y_array``). + x_min: Minimum x limit (or ``None`` to use default). x_max: + Maximum x limit (or ``None`` to use default). - Returns: - Filtered ``y_array`` values where ``x_array`` lies within - ``[x_min, x_max]``. + Returns: Filtered ``y_array`` values where ``x_array`` lies + within ``[x_min, x_max]``. """ if x_min is None: x_min = self.x_min @@ -152,22 +147,18 @@ def _prepare_powder_data( ): """Validate, resolve axes, auto-range, and filter arrays. - Args: - pattern: Data pattern object with intensity arrays. - expt_name: Experiment name for error messages. - expt_type: Experiment type with sample_form, scattering, - and beam enums. - x_min: Optional minimum x-axis limit. - x_max: Optional maximum x-axis limit. - x: Explicit x-axis type or ``None``. - need_meas: Whether ``intensity_meas`` is required. - need_calc: Whether ``intensity_calc`` is required. - show_residual: If ``True``, compute meas − calc residual. - - Returns: - A dict with keys ``x_filtered``, ``y_series``, ``y_labels``, - ``axes_labels``, and ``x_axis``; or ``None`` when a required - array is missing. + Args: pattern: Data pattern object with intensity arrays. + expt_name: Experiment name for error messages. expt_type: + Experiment type with sample_form, scattering, and beam + enums. x_min: Optional minimum x-axis limit. x_max: + Optional maximum x-axis limit. x: Explicit x-axis type or + ``None``. need_meas: Whether ``intensity_meas`` is required. + need_calc: Whether ``intensity_calc`` is required. + show_residual: If ``True``, compute meas − calc residual. + + Returns: A dict with keys ``x_filtered``, ``y_series``, + ``y_labels``, ``axes_labels``, and ``x_axis``; or ``None`` + when a required array is missing. """ x_axis, x_name, sample_form, scattering_type, _ = self._resolve_x_axis(expt_type, x) @@ -225,14 +216,12 @@ def _prepare_powder_data( def _resolve_x_axis(self, expt_type, x): """Determine the x-axis type from experiment metadata. - Args: - expt_type: Experiment type with sample_form, - scattering_type, and beam_mode enums. - x: Explicit x-axis type or ``None`` to auto-detect. + Args: expt_type: Experiment type with sample_form, + scattering_type, and beam_mode enums. x: Explicit x-axis + type or ``None`` to auto-detect. - Returns: - Tuple of ``(x_axis, x_name, sample_form, scattering_type, - beam_mode)``. + Returns: Tuple of ``(x_axis, x_name, sample_form, + scattering_type, beam_mode)``. """ sample_form = expt_type.sample_form.value scattering_type = expt_type.scattering_type.value @@ -254,8 +243,7 @@ def x_min(self): def x_min(self, value): """Set the minimum x-axis limit. - Args: - value: Minimum limit or ``None`` to reset to default. + Args: value: Minimum limit or ``None`` to reset to default. """ if value is not None: self._x_min = value @@ -271,8 +259,7 @@ def x_max(self): def x_max(self, value): """Set the maximum x-axis limit. - Args: - value: Maximum limit or ``None`` to reset to default. + Args: value: Maximum limit or ``None`` to reset to default. """ if value is not None: self._x_max = value @@ -288,8 +275,7 @@ def height(self): def height(self, value): """Set plot height. - Args: - value: Height value or ``None`` to reset to default. + Args: value: Height value or ``None`` to reset to default. """ if value is not None: self._height = value @@ -326,16 +312,14 @@ def plot_meas( ): """Plot measured pattern using the current engine. - Args: - pattern: Object with x-axis arrays (``two_theta``, - ``time_of_flight``, ``d_spacing``) and ``meas`` array. - expt_name: Experiment name for the title. - expt_type: Experiment type with scattering/beam enums. - x_min: Optional minimum x-axis limit. - x_max: Optional maximum x-axis limit. - x: X-axis type (``'two_theta'``, ``'time_of_flight'``, or - ``'d_spacing'``). If ``None``, auto-detected from - beam mode. + Args: pattern: Object with x-axis arrays (``two_theta``, + ``time_of_flight``, ``d_spacing``) and ``meas`` array. + expt_name: Experiment name for the title. expt_type: + Experiment type with scattering/beam enums. x_min: Optional + minimum x-axis limit. x_max: Optional maximum x-axis limit. + x: X-axis type (``'two_theta'``, ``'time_of_flight'``, or + ``'d_spacing'``). If ``None``, auto-detected from beam + mode. """ ctx = self._prepare_powder_data( pattern, @@ -369,16 +353,14 @@ def plot_calc( ): """Plot calculated pattern using the current engine. - Args: - pattern: Object with x-axis arrays (``two_theta``, - ``time_of_flight``, ``d_spacing``) and ``calc`` array. - expt_name: Experiment name for the title. - expt_type: Experiment type with scattering/beam enums. - x_min: Optional minimum x-axis limit. - x_max: Optional maximum x-axis limit. - x: X-axis type (``'two_theta'``, ``'time_of_flight'``, or - ``'d_spacing'``). If ``None``, auto-detected from - beam mode. + Args: pattern: Object with x-axis arrays (``two_theta``, + ``time_of_flight``, ``d_spacing``) and ``calc`` array. + expt_name: Experiment name for the title. expt_type: + Experiment type with scattering/beam enums. x_min: Optional + minimum x-axis limit. x_max: Optional maximum x-axis limit. + x: X-axis type (``'two_theta'``, ``'time_of_flight'``, or + ``'d_spacing'``). If ``None``, auto-detected from beam + mode. """ ctx = self._prepare_powder_data( pattern, @@ -415,25 +397,22 @@ def plot_meas_vs_calc( Supports both powder and single crystal data with a unified API. - For powder diffraction: - - x='two_theta', 'time_of_flight', or 'd_spacing' - - Auto-detected from beam mode if not specified - - For single crystal diffraction: - - x='intensity_calc' (default): scatter plot - - x='d_spacing' or 'sin_theta_over_lambda': line plot - - Args: - pattern: Data pattern object with meas/calc arrays. - expt_name: Experiment name for the title. - expt_type: Experiment type with sample_form, - scattering, and beam enums. - x_min: Optional minimum x-axis limit. - x_max: Optional maximum x-axis limit. - show_residual: If ``True``, add residual series - (powder only). - x: X-axis type. If ``None``, auto-detected from sample form - and beam mode. + For powder diffraction: - x='two_theta', 'time_of_flight', + or 'd_spacing' - Auto-detected from beam mode if not + specified + + For single crystal diffraction: - x='intensity_calc' + (default): scatter plot - x='d_spacing' or + 'sin_theta_over_lambda': line plot + + Args: pattern: Data pattern object with meas/calc arrays. + expt_name: Experiment name for the title. expt_type: + Experiment type with sample_form, scattering, and beam + enums. x_min: Optional minimum x-axis limit. x_max: + Optional maximum x-axis limit. show_residual: If ``True``, + add residual series (powder only). x: X-axis type. + If ``None``, auto-detected from sample form and beam + mode. """ x_axis, _, sample_form, scattering_type, _ = self._resolve_x_axis(expt_type, x) diff --git a/src/easydiffraction/display/tablers/__init__.py b/src/easydiffraction/display/tablers/__init__.py index d68c1cda..7cbc22c0 100644 --- a/src/easydiffraction/display/tablers/__init__.py +++ b/src/easydiffraction/display/tablers/__init__.py @@ -2,9 +2,9 @@ # SPDX-License-Identifier: BSD-3-Clause """Tabular rendering backends. -This subpackage provides concrete implementations for rendering -tables in different environments: +This subpackage provides concrete implementations for rendering tables +in different environments: -- :mod:`.rich` for terminal and notebooks using the Rich library. -- :mod:`.pandas` for notebooks using DataFrame Styler. +- :mod:`.rich` for terminal and notebooks using the Rich library. - +:mod:`.pandas` for notebooks using DataFrame Styler. """ diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index 01d67c07..e77f4a93 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -21,9 +21,8 @@ class TableBackendBase(ABC): """Abstract base class for concrete table backends. - Subclasses implement the ``render`` method which receives an - index-aware pandas DataFrame and the alignment for each column - header. + Subclasses implement the ``render`` method which receives an index- + aware pandas DataFrame and the alignment for each column header. """ FLOAT_PRECISION = 5 @@ -37,12 +36,10 @@ def __init__(self) -> None: def _format_value(self, value: Any) -> Any: """Format floats with fixed precision and others as strings. - Args: - value: Cell value to format. + Args: value: Cell value to format. - Returns: - A string representation with fixed precision for floats or - ``str(value)`` for other types. + Returns: A string representation with fixed precision for + floats or ``str(value)`` for other types. """ return self._float_fmt(value) if isinstance(value, float) else str(value) @@ -65,12 +62,10 @@ def _is_dark_theme(self) -> bool: def _rich_to_hex(self, color): """Convert a Rich color name to a CSS-style hex string. - Args: - color: Rich color name or specification parsable by - :mod:`rich`. + Args: color: Rich color name or specification parsable by + :mod:`rich`. - Returns: - Hex color string in the form ``#RRGGBB``. + Returns: Hex color string in the form ``#RRGGBB``. """ c = Color.parse(color) rgb = c.get_truecolor() @@ -96,15 +91,11 @@ def render( ) -> Any: """Render the provided DataFrame with backend-specific styling. - Args: - alignments: Iterable of column justifications (e.g., - ``'left'`` or ``'center'``) corresponding to the data - columns. - df: Index-aware DataFrame with data to render. - display_handle: Optional environment-specific handle to - enable in-place updates. + Args: alignments: Iterable of column justifications (e.g., + ``'left'`` or ``'center'``) corresponding to the data columns. + df: Index-aware DataFrame with data to render. display_handle: + Optional environment-specific handle to enable in-place updates. - Returns: - Backend-defined return value (commonly ``None``). + Returns: Backend-defined return value (commonly ``None``). """ pass diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index c05cc6ed..4f063fe4 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -24,12 +24,10 @@ class PandasTableBackend(TableBackendBase): def _build_base_styles(self, color: str) -> list[dict]: """Return base CSS table styles for a given border color. - Args: - color: CSS color value (e.g., ``#RRGGBB``) to use for - borders and header accents. + Args: color: CSS color value (e.g., ``#RRGGBB``) to use for + borders and header accents. - Returns: - A list of ``Styler.set_table_styles`` dictionaries. + Returns: A list of ``Styler.set_table_styles`` dictionaries. """ return [ # Margins and outer border on the entire table @@ -79,13 +77,11 @@ def _build_base_styles(self, color: str) -> list[dict]: def _build_header_alignment_styles(self, df, alignments) -> list[dict]: """Generate header cell alignment styles per column. - Args: - df: DataFrame whose columns are being rendered. - alignments: Iterable of text alignment values (e.g., - ``'left'``, ``'center'``) matching ``df`` columns. + Args: df: DataFrame whose columns are being rendered. + alignments: Iterable of text alignment values (e.g., ``'left'``, + ``'center'``) matching ``df`` columns. - Returns: - A list of CSS rules for header cell alignment. + Returns: A list of CSS rules for header cell alignment. """ return [ { @@ -98,13 +94,11 @@ def _build_header_alignment_styles(self, df, alignments) -> list[dict]: def _apply_styling(self, df, alignments, color: str): """Build a configured Styler with alignments and base styles. - Args: - df: DataFrame to style. - alignments: Iterable of text alignment values for columns. - color: CSS color value used for borders/header. + Args: df: DataFrame to style. alignments: Iterable of + text alignment values for columns. color: CSS color value + used for borders/header. - Returns: - A configured pandas Styler ready for display. + Returns: A configured pandas Styler ready for display. """ table_styles = self._build_base_styles(color) header_alignment_styles = self._build_header_alignment_styles(df, alignments) @@ -127,10 +121,9 @@ def _update_display(self, styler, display_handle) -> None: DisplayHandle, update the output area in-place using HTML. Otherwise, display once via IPython ``display()``. - Args: - styler: Configured DataFrame Styler to be rendered. - display_handle: Optional IPython DisplayHandle used for - in-place updates. + Args: styler: Configured DataFrame Styler to be rendered. + display_handle: Optional IPython DisplayHandle used for in-place + updates. """ # Handle with update() method if display_handle is not None and hasattr(display_handle, 'update'): @@ -158,11 +151,11 @@ def render( ) -> Any: """Render a styled DataFrame. - Args: - alignments: Iterable of column justifications (e.g. 'left'). - df: DataFrame whose index is displayed as the first column. - display_handle: Optional IPython DisplayHandle to update an - existing output area in place when running in Jupyter. + Args: alignments: Iterable of column justifications (e.g. + 'left'). df: DataFrame whose index is displayed as the first + column. display_handle: Optional IPython DisplayHandle to + update an existing output area in place when running in + Jupyter. """ color = self._pandas_border_color styler = self._apply_styling(df, alignments, color) diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index 693451b4..d592ef82 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -43,15 +43,13 @@ class RichTableBackend(TableBackendBase): def _to_html(self, table: Table) -> str: """Render a Rich table to HTML using an off-screen console. - A fresh ``Console(record=True, file=StringIO())`` avoids - private attribute access and guarantees no visible output - in notebooks. + A fresh ``Console(record=True, file=StringIO())`` avoids private + attribute access and guarantees no visible output in notebooks. - Args: - table: Rich :class:`~rich.table.Table` to export. + Args: table: Rich :class:`~rich.table.Table` to export. - Returns: - HTML string with inline styles for notebook display. + Returns: HTML string with inline styles for notebook + display. """ tmp = Console(force_jupyter=False, record=True, file=io.StringIO()) tmp.print(table) @@ -66,13 +64,12 @@ def _to_html(self, table: Table) -> str: def _build_table(self, df, alignments, color: str) -> Table: """Construct a Rich Table with formatted data and alignment. - Args: - df: DataFrame-like object providing rows to render. - alignments: Iterable of text alignment values for columns. - color: Rich color name used for borders/index style. + Args: df: DataFrame-like object providing rows to render. + alignments: Iterable of text alignment values for columns. + color: Rich color name used for borders/index style. - Returns: - A :class:`~rich.table.Table` configured for display. + Returns: A :class:`~rich.table.Table` configured for + display. """ table = Table( title=None, @@ -99,17 +96,15 @@ def _build_table(self, df, alignments, color: str) -> Table: def _update_display(self, table: Table, display_handle) -> None: """Single, consistent update path for Jupyter and terminal. - - With a handle that has ``update()``: - * If it's an IPython DisplayHandle, export to HTML and - update. - * Otherwise, treat it as a terminal/live-like handle and - update with the Rich renderable. - - Without a handle, print once to the shared console. - - Args: - table: Rich :class:`~rich.table.Table` to display. - display_handle: Optional environment-specific handle for - in-place updates (IPython or terminal live). + - With a handle that has ``update()``: * If it's an IPython + DisplayHandle, export to HTML and update. * Otherwise, + treat it as a terminal/live-like handle and update with the + Rich renderable. - Without a handle, print once to the shared + console. + + Args: table: Rich :class:`~rich.table.Table` to display. + display_handle: Optional environment-specific handle for in- + place updates (IPython or terminal live). """ # Handle with update() method if display_handle is not None and hasattr(display_handle, 'update'): @@ -142,11 +137,9 @@ def render( ) -> Any: """Render a styled table using Rich. - Args: - alignments: Iterable of text-align values for columns. - df: Index-aware DataFrame to render. - display_handle: Optional environment handle for in-place - updates. + Args: alignments: Iterable of text-align values for columns. + df: Index-aware DataFrame to render. display_handle: + Optional environment handle for in-place updates. """ color = self._rich_border_color table = self._build_table(df, alignments, color) diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index cac8ac44..bea9ed0a 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -68,15 +68,13 @@ def show_config(self) -> None: def render(self, df, display_handle: Any | None = None) -> Any: """Render a DataFrame as a table using the active backend. - Args: - df: DataFrame with a two-level column index where the - second level provides per-column alignment. - display_handle: Optional environment-specific handle used - to update an existing output area in-place (e.g., an - IPython DisplayHandle or a terminal live handle). - - Returns: - Backend-specific return value (usually ``None``). + Args: df: DataFrame with a two-level column index where the + second level provides per-column alignment. display_handle: + Optional environment-specific handle used to update an + existing output area in-place (e.g., an IPython + DisplayHandle or a terminal live handle). + + Returns: Backend-specific return value (usually ``None``). """ # Work on a copy to avoid mutating the original DataFrame df = df.copy() @@ -101,8 +99,8 @@ def _registry(cls) -> dict: """Build registry, adapting available engines to the environment. - - In Jupyter: expose both 'rich' and 'pandas'. - - In terminal: expose only 'rich' (pandas is notebook-only). + - In Jupyter: expose both 'rich' and 'pandas'. - In terminal: + expose only 'rich' (pandas is notebook-only). """ base = { TableEngineEnum.RICH.value: { diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index eb539d83..cbea397a 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -26,10 +26,9 @@ def format_value(value) -> str: """Format a single CIF value, quoting strings with whitespace, and format floats with global precision. - .. note:: - The precision must be high enough so that the minimizer's - finite-difference Jacobian probes (typically ~1e-8 relative) - survive the float→string→float round-trip through CIF. + .. note:: The precision must be high enough so that the + minimizer's finite-difference Jacobian probes (typically ~1e-8 + relative) survive the float→string→float round-trip through CIF. """ width = 12 precision = 8 diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index b89c363d..67e25d9c 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -18,8 +18,7 @@ class Summary: def __init__(self, project) -> None: """Initialize the summary with a reference to the project. - Args: - project: The Project instance this summary belongs to. + Args: project: The Project instance this summary belongs to. """ self.project = project diff --git a/src/easydiffraction/utils/_vendored/__init__.py b/src/easydiffraction/utils/_vendored/__init__.py index 0ec35f4c..43a5d921 100644 --- a/src/easydiffraction/utils/_vendored/__init__.py +++ b/src/easydiffraction/utils/_vendored/__init__.py @@ -5,19 +5,19 @@ This package contains third-party code that has been vendored into the project to avoid external dependencies not available on conda-forge. -Packages: - jupyter_dark_detect/ - Vendored copy of jupyter_dark_detect (MIT License). - Check jupyter_dark_detect.__version__ for vendored version. +Packages: jupyter_dark_detect/ Vendored copy of +jupyter_dark_detect (MIT License). Check +jupyter_dark_detect.__version__ for vendored version. - To update, replace these files from upstream: - - https://github.com/OpenMined/jupyter-dark-detect/blob/main/jupyter_dark_detect/__init__.py - - https://github.com/OpenMined/jupyter-dark-detect/blob/main/jupyter_dark_detect/detector.py +To update, replace these files from upstream: - +https://github.com/OpenMined/jupyter-dark-detect/blob/main/jupyter_dark_detect/__init__.py + - +https://github.com/OpenMined/jupyter-dark-detect/blob/main/jupyter_dark_detect/detector.py - Then run 'pixi run fix' to format and check for issues. +Then run 'pixi run fix' to format and check for issues. Modules: - theme_detect: - Custom wrapper around jupyter_dark_detect with optimized - detection order for EasyDiffraction's use case. +theme_detect: +Custom wrapper around jupyter_dark_detect with optimized +detection order for EasyDiffraction's use case. """ diff --git a/src/easydiffraction/utils/_vendored/jupyter_dark_detect/__init__.py b/src/easydiffraction/utils/_vendored/jupyter_dark_detect/__init__.py index 11ef7b95..640883dd 100644 --- a/src/easydiffraction/utils/_vendored/jupyter_dark_detect/__init__.py +++ b/src/easydiffraction/utils/_vendored/jupyter_dark_detect/__init__.py @@ -1,7 +1,7 @@ -"""jupyter-dark-detect: Detect dark mode in Jupyter environments. +"""Jupyter-dark-detect: Detect dark mode in Jupyter environments. -This package provides a simple API to detect whether Jupyter Notebook/Lab -is running in dark mode across different environments. +This package provides a simple API to detect whether Jupyter +Notebook/Lab is running in dark mode across different environments. """ from .detector import is_dark diff --git a/src/easydiffraction/utils/_vendored/jupyter_dark_detect/detector.py b/src/easydiffraction/utils/_vendored/jupyter_dark_detect/detector.py index 8f34b5af..e247ba87 100644 --- a/src/easydiffraction/utils/_vendored/jupyter_dark_detect/detector.py +++ b/src/easydiffraction/utils/_vendored/jupyter_dark_detect/detector.py @@ -14,14 +14,11 @@ def is_dark() -> bool: """Check if Jupyter Notebook/Lab is running in dark mode. - This function attempts multiple detection strategies: - 1. JupyterLab theme settings files - 2. VS Code settings (when running in VS Code) - 3. JavaScript DOM inspection - 4. System preferences (macOS) - - Returns: - bool: True if dark mode is detected, False otherwise + This function attempts multiple detection strategies: 1. JupyterLab + theme settings files 2. VS Code settings (when running in VS Code) + 3. JavaScript DOM inspection 4. System preferences (macOS) + + Returns: bool: True if dark mode is detected, False otherwise """ # Try JupyterLab settings first result = _check_jupyterlab_settings() diff --git a/src/easydiffraction/utils/_vendored/theme_detect.py b/src/easydiffraction/utils/_vendored/theme_detect.py index bae7826d..9f9bf264 100644 --- a/src/easydiffraction/utils/_vendored/theme_detect.py +++ b/src/easydiffraction/utils/_vendored/theme_detect.py @@ -44,13 +44,12 @@ def is_dark() -> bool: Detection order: - 1. JupyterLab settings files (most reliable for JupyterLab) - 2. VS Code settings (when running in VS Code) - 3. JavaScript DOM inspection (for browser-based Jupyter) - 4. System preferences (fallback - may differ from Jupyter theme) + 1. JupyterLab settings files (most reliable for JupyterLab) 2. VS + Code settings (when running in VS Code) 3. JavaScript DOM inspection + (for browser-based Jupyter) 4. System preferences (fallback - may + differ from Jupyter theme) - Returns: - True if dark mode is detected, False otherwise. + Returns: True if dark mode is detected, False otherwise. """ # Try Jupyter-specific methods first result = _check_jupyterlab_settings() @@ -79,9 +78,8 @@ def is_dark() -> bool: def get_detection_result() -> dict[str, Optional[bool]]: """Get results from all detection methods for debugging. - Returns: - Dictionary with detection method names as keys and their - results (True/False/None) as values. + Returns: Dictionary with detection method names as keys and + their results (True/False/None) as values. """ return { 'jupyterlab_settings': _check_jupyterlab_settings(), diff --git a/src/easydiffraction/utils/environment.py b/src/easydiffraction/utils/environment.py index cee1ba46..11ed2816 100644 --- a/src/easydiffraction/utils/environment.py +++ b/src/easydiffraction/utils/environment.py @@ -19,8 +19,7 @@ def in_warp() -> bool: def in_pycharm() -> bool: """Determines if the current environment is PyCharm. - Returns: - bool: True if running inside PyCharm, False otherwise. + Returns: bool: True if running inside PyCharm, False otherwise. """ return os.environ.get('PYCHARM_HOSTED') == '1' @@ -28,8 +27,7 @@ def in_pycharm() -> bool: def in_colab() -> bool: """Determines if the current environment is Google Colab. - Returns: - bool: True if running in Google Colab, False otherwise. + Returns: bool: True if running in Google Colab, False otherwise. """ try: return find_spec('google.colab') is not None @@ -40,8 +38,8 @@ def in_colab() -> bool: def in_jupyter() -> bool: """Return True when running inside a Jupyter Notebook. - Returns: - bool: True if inside a Jupyter Notebook, False otherwise. + Returns: bool: True if inside a Jupyter Notebook, False + otherwise. """ try: import IPython # type: ignore[import-not-found] @@ -78,9 +76,8 @@ def in_jupyter() -> bool: def in_github_ci() -> bool: """Return True when running under GitHub Actions CI. - Returns: - bool: True if env var ``GITHUB_ACTIONS`` is set, False - otherwise. + Returns: bool: True if env var ``GITHUB_ACTIONS`` is set, False + otherwise. """ return os.environ.get('GITHUB_ACTIONS') is not None @@ -94,9 +91,9 @@ def is_ipython_display_handle(obj: object) -> bool: """Return True if ``obj`` is an IPython DisplayHandle instance. Tries to import ``IPython.display.DisplayHandle`` and uses - ``isinstance`` when available. Falls back to a conservative - module name heuristic if IPython is missing. Any errors result - in ``False``. + ``isinstance`` when available. Falls back to a conservative module + name heuristic if IPython is missing. Any errors result in + ``False``. """ try: # Fast path when IPython is available from IPython.display import DisplayHandle # type: ignore[import-not-found] diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index dddaf1a0..d368884a 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -96,9 +96,8 @@ class ConsoleManager: def _detect_width() -> int: """Detect a suitable console width for the shared Console. - Returns: - The detected terminal width, clamped at - ``_MIN_CONSOLE_WIDTH`` to avoid cramped layouts. + Returns: The detected terminal width, clamped at + ``_MIN_CONSOLE_WIDTH`` to avoid cramped layouts. """ min_width = ConsoleManager._MIN_CONSOLE_WIDTH try: @@ -136,11 +135,10 @@ def setup_handlers( ) -> None: """Install Rich handler and optional Jupyter traceback support. - Args: - logger: Logger instance to attach handlers to. - level: Minimum log level to emit. - rich_tracebacks: Whether to enable Rich tracebacks. - mode: Output mode name ("compact" or "verbose"). + Args: logger: Logger instance to attach handlers to. level: + Minimum log level to emit. rich_tracebacks: Whether to + enable Rich tracebacks. mode: Output mode name ("compact" or + "verbose"). """ logger.handlers.clear() logger.propagate = False @@ -177,11 +175,9 @@ def configure( ) -> None: """Configure the logger with RichHandler and exception hooks. - Args: - logger: Logger instance to configure. - mode: Output mode (compact or verbose). - level: Minimum log level to emit. - rich_tracebacks: Whether to enable Rich tracebacks. + Args: logger: Logger instance to configure. mode: Output + mode (compact or verbose). level: Minimum log level to emit. + rich_tracebacks: Whether to enable Rich tracebacks. """ LoggerConfig.setup_handlers( logger, @@ -206,8 +202,7 @@ class ExceptionHookManager: def install_verbose_hook(logger: logging.Logger) -> None: """Install a verbose exception hook that prints rich tracebacks. - Args: - logger: Logger used to emit the exception information. + Args: logger: Logger used to emit the exception information. """ if not hasattr(Logger, '_orig_excepthook'): Logger._orig_excepthook = sys.excepthook # type: ignore[attr-defined] @@ -235,8 +230,7 @@ def aligned_excepthook( def install_compact_hook(logger: logging.Logger) -> None: """Install a compact exception hook that logs message-only. - Args: - logger: Logger used to emit the error message. + Args: logger: Logger used to emit the error message. """ if not hasattr(Logger, '_orig_excepthook'): Logger._orig_excepthook = sys.excepthook # type: ignore[attr-defined] @@ -263,13 +257,11 @@ def _suppress_traceback(logger): """Build a Jupyter custom exception callback that logs only the message. - Args: - logger: Logger used to emit error messages. + Args: logger: Logger used to emit error messages. - Returns: - A callable suitable for IPython's set_custom_exc that - suppresses full tracebacks and logs only the exception - message. + Returns: A callable suitable for IPython's set_custom_exc + that suppresses full tracebacks and logs only the exception + message. """ def suppress_jupyter_traceback(*args, **kwargs): @@ -289,8 +281,7 @@ def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: """Install a Jupyter/IPython custom exception handler that suppresses tracebacks. - Args: - logger: Logger used to emit error messages. + Args: logger: Logger used to emit error messages. """ try: from IPython import get_ipython @@ -313,9 +304,8 @@ def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: class Logger: """Centralized logging with Rich formatting and two modes. - Environment variables: - ED_LOG_MODE: set default mode ('verbose' or 'compact') - ED_LOG_LEVEL: set default level ('DEBUG', 'INFO', etc.) + Environment variables: ED_LOG_MODE: set default mode ('verbose' or + 'compact') ED_LOG_LEVEL: set default level ('DEBUG', 'INFO', etc.) """ # --- Enums --- @@ -371,13 +361,12 @@ def configure( ) -> None: """Configure logger. - mode: default COMPACT in Jupyter else VERBOSE - level: minimum log level - rich_tracebacks: override automatic choice + mode: default COMPACT in Jupyter else VERBOSE level: minimum log + level rich_tracebacks: override automatic choice - Environment variables: - ED_LOG_MODE: set default mode ('verbose' or 'compact') - ED_LOG_LEVEL: set default level ('DEBUG', 'INFO', etc.) + Environment variables: ED_LOG_MODE: set default mode ('verbose' + or 'compact') ED_LOG_LEVEL: set default level ('DEBUG', 'INFO', + etc.) """ env_mode = os.getenv('ED_LOG_MODE') env_level = os.getenv('ED_LOG_LEVEL') @@ -514,9 +503,8 @@ def print(cls, *objects, **kwargs): """Print objects to the console with left padding. - Renderables (Rich types like Text, Table, Panel, etc.) are - kept as-is. - - Non-renderables (ints, floats, Path, etc.) are converted to - str(). + kept as-is. - Non-renderables (ints, floats, Path, etc.) are + converted to str(). """ safe_objects = [] for obj in objects: diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index c1224ae8..b835201e 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -32,11 +32,9 @@ def _validate_url(url: str) -> None: """Validate that a URL uses only safe HTTP/HTTPS schemes. - Args: - url: The URL to validate. + Args: url: The URL to validate. - Raises: - ValueError: If the URL scheme is not HTTP or HTTPS. + Raises: ValueError: If the URL scheme is not HTTP or HTTPS. """ parsed = urlparse(url) if parsed.scheme not in ('http', 'https'): @@ -102,8 +100,8 @@ def _fetch_tutorials_index() -> dict: (e.g., '0.8.0.post1'). For development versions, 'dev' is used. Returns: - dict: The tutorials index as a dictionary, or empty dict if - fetch fails. + dict: The tutorials index as a dictionary, or empty dict if + fetch fails. """ version = _get_version_for_url() index_url = f'https://easyscience.github.io/diffraction-lib/{version}/tutorials/index.json' @@ -128,21 +126,16 @@ def download_data( """Download a dataset by numeric ID using the remote diffraction index. - Example: - path = download_data(id=12, destination="data") + Example: path = download_data(id=12, destination="data") - Args: - id: Numeric dataset id (e.g. 12). - destination: Directory to save the file into (created if - missing). - overwrite: Whether to overwrite the file if it already exists. + Args: id: Numeric dataset id (e.g. 12). destination: + Directory to save the file into (created if missing). + overwrite: Whether to overwrite the file if it already exists. - Returns: - str: Full path to the downloaded file as string. + Returns: str: Full path to the downloaded file as string. - Raises: - KeyError: If the id is not found in the index. - ValueError: If the resolved URL is not HTTP/HTTPS. + Raises: KeyError: If the id is not found in the index. + ValueError: If the resolved URL is not HTTP/HTTPS. """ index = _fetch_data_index() key = str(id) @@ -197,12 +190,11 @@ def download_data( def package_version(package_name: str) -> str | None: """Get the installed version string of the specified package. - Args: - package_name (str): The name of the package to query. + Args: package_name (str): The name of the package to query. - Returns: - str | None: The raw version string (may include local part, - e.g., '1.2.3+abc123'), or None if the package is not installed. + Returns: str | None: The raw version string (may include local + part, e.g., '1.2.3+abc123'), or None if the package is not + installed. """ try: return version(package_name) @@ -217,12 +209,10 @@ def stripped_package_version(package_name: str) -> str | None: Returns only the public version segment (e.g., '1.2.3' or '1.2.3.post4'), omitting any local segment (e.g., '+d136'). - Args: - package_name (str): The name of the package to query. + Args: package_name (str): The name of the package to query. - Returns: - str | None: The public version string, or None if the package - is not installed. + Returns: str | None: The public version string, or None if the + package is not installed. """ v_str = package_version(package_name) if v_str is None: @@ -238,18 +228,14 @@ def _is_dev_version(package_name: str) -> bool: """Check if the installed package version is a development/local version. - A version is considered "dev" if: - - The raw version contains '+dev', '+dirty', or '+devdirty' (local - suffixes from versioningit) - - The public version is '999.0.0' (versioningit default-tag - fallback) + A version is considered "dev" if: - The raw version contains '+dev', + '+dirty', or '+devdirty' (local suffixes from versioningit) - The + public version is '999.0.0' (versioningit default-tag fallback) - Args: - package_name (str): The name of the package to query. + Args: package_name (str): The name of the package to query. - Returns: - bool: True if the version is a development version, False - otherwise. + Returns: bool: True if the version is a development version, + False otherwise. """ raw_version = package_version(package_name) if raw_version is None: @@ -271,12 +257,10 @@ def _get_version_for_url(package_name: str = 'easydiffraction') -> str: Returns the public version for released versions, or 'dev' for development/local versions. - Args: - package_name (str): The name of the package to query. + Args: package_name (str): The name of the package to query. - Returns: - str: The version string to use in URLs ('dev' or a version like - '0.8.0.post1'). + Returns: str: The version string to use in URLs ('dev' or a + version like '0.8.0.post1'). """ if _is_dev_version(package_name): return 'dev' @@ -304,12 +288,11 @@ def _resolve_tutorial_url(url_template: str) -> str: """Replace {version} placeholder in URL template with actual version. - Args: - url_template (str): URL template containing {version} - placeholder. + Args: url_template (str): URL template containing {version} + placeholder. - Returns: - str: URL with {version} replaced by actual version string. + Returns: str: URL with {version} replaced by actual version + string. """ version = _get_version_for_url() return url_template.replace('{version}', version) @@ -355,21 +338,16 @@ def download_tutorial( ) -> str: """Download a tutorial notebook by numeric ID. - Example: - path = download_tutorial(id=1, destination="tutorials") + Example: path = download_tutorial(id=1, destination="tutorials") - Args: - id: Numeric tutorial id (e.g. 1). - destination: Directory to save the file into (created if - missing). - overwrite: Whether to overwrite the file if it already exists. + Args: id: Numeric tutorial id (e.g. 1). destination: + Directory to save the file into (created if missing). + overwrite: Whether to overwrite the file if it already exists. - Returns: - str: Full path to the downloaded file as string. + Returns: str: Full path to the downloaded file as string. - Raises: - KeyError: If the id is not found in the index. - ValueError: If the resolved URL is not HTTP/HTTPS. + Raises: KeyError: If the id is not found in the index. + ValueError: If the resolved URL is not HTTP/HTTPS. """ index = _fetch_tutorials_index() key = str(id) @@ -422,16 +400,13 @@ def download_all_tutorials( ) -> list[str]: """Download all available tutorial notebooks. - Example: - paths = download_all_tutorials(destination="tutorials") + Example: paths = download_all_tutorials(destination="tutorials") - Args: - destination: Directory to save the files into (created if - missing). - overwrite: Whether to overwrite files if they already exist. + Args: destination: Directory to save the files into (created if + missing). overwrite: Whether to overwrite files if they already + exist. - Returns: - list[str]: List of full paths to the downloaded files. + Returns: list[str]: List of full paths to the downloaded files. """ index = _fetch_tutorials_index() if not index: @@ -460,8 +435,7 @@ def download_all_tutorials( def show_version() -> None: """Print the installed version of the easydiffraction package. - Args: - None + Args: None """ current_ed_version = package_version('easydiffraction') console.print(f'Current easydiffraction v{current_ed_version}') @@ -488,8 +462,7 @@ def render_cif(cif_text) -> None: """Display the CIF text as a formatted table in Jupyter Notebook or terminal. - Args: - cif_text: The CIF text to display. + Args: cif_text: The CIF text to display. """ # Split into lines lines: List[str] = [line for line in cif_text.splitlines()] @@ -515,32 +488,25 @@ def tof_to_d( """Convert time-of-flight (TOF) to d-spacing using a quadratic calibration. - Model: - TOF = offset + linear * d + quad * d² - - The function: - - Uses a linear fallback when the quadratic term is effectively - zero. - - Solves the quadratic for d and selects the smallest positive, - finite root. - - Returns NaN where no valid solution exists. - - Expects ``tof`` as a NumPy array; output matches its shape. - - Args: - tof (np.ndarray): Time-of-flight values (µs). Must be a NumPy - array. - offset (float): Calibration offset (µs). - linear (float): Linear calibration coefficient (µs/Å). - quad (float): Quadratic calibration coefficient (µs/Ų). - quad_eps (float, optional): Threshold to treat ``quad`` as zero. - Defaults to 1e-20. + Model: TOF = offset + linear * d + quad * d² - Returns: - np.ndarray: d-spacing values (Å), NaN where invalid. + The function: - Uses a linear fallback when the quadratic term is + effectively zero. - Solves the quadratic for d and selects the + smallest positive, finite root. - Returns NaN where no valid + solution exists. - Expects ``tof`` as a NumPy array; output + matches its shape. + + Args: tof (np.ndarray): Time-of-flight values (µs). Must be a + NumPy array. offset (float): Calibration offset (µs). + linear (float): Linear calibration coefficient (µs/Å). quad + (float): Quadratic calibration coefficient (µs/Ų). quad_eps + (float, optional): Threshold to treat ``quad`` as zero. Defaults to + 1e-20. - Raises: - TypeError: If ``tof`` is not a NumPy array or coefficients are - not real numbers. + Returns: np.ndarray: d-spacing values (Å), NaN where invalid. + + Raises: TypeError: If ``tof`` is not a NumPy array or + coefficients are not real numbers. """ # Type checks if not isinstance(tof, np.ndarray): @@ -597,12 +563,10 @@ def tof_to_d( def twotheta_to_d(twotheta, wavelength): """Convert 2-theta to d-spacing using Bragg's law. - Parameters: - twotheta (float or np.ndarray): 2-theta angle in degrees. - wavelength (float): Wavelength in Å. + Parameters: twotheta (float or np.ndarray): 2-theta angle in + degrees. wavelength (float): Wavelength in Å. - Returns: - d (float or np.ndarray): d-spacing in Å. + Returns: d (float or np.ndarray): d-spacing in Å. """ # Convert twotheta from degrees to radians theta_rad = np.radians(twotheta / 2) @@ -616,12 +580,10 @@ def twotheta_to_d(twotheta, wavelength): def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda): """Convert sin(theta)/lambda to d-spacing. - Parameters: - sin_theta_over_lambda (float or np.ndarray): sin(theta)/lambda - in 1/Å. + Parameters: sin_theta_over_lambda (float or np.ndarray): + sin(theta)/lambda in 1/Å. - Returns: - d (float or np.ndarray): d-spacing in Å. + Returns: d (float or np.ndarray): d-spacing in Å. """ # Avoid division by zero with np.errstate(divide='ignore', invalid='ignore'): @@ -635,15 +597,12 @@ def get_value_from_xye_header(file_path, key): """Extracts a floating point value from the first line of the file, corresponding to the given key. - Parameters: - file_path (str): Path to the input file. - key (str): The key to extract ('DIFC' or 'two_theta'). + Parameters: file_path (str): Path to the input file. key + (str): The key to extract ('DIFC' or 'two_theta'). - Returns: - float: The extracted value. + Returns: float: The extracted value. - Raises: - ValueError: If the key is not found. + Raises: ValueError: If the key is not found. """ pattern = rf'{key}\s*=\s*([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)' @@ -661,34 +620,25 @@ def str_to_ufloat(s: Optional[str], default: Optional[float] = None) -> UFloat: """Parse a CIF-style numeric string into a `ufloat` with an optional uncertainty. - Examples of supported input: - - "3.566" → ufloat(3.566, nan) - - "3.566(2)" → ufloat(3.566, 0.002) - - None → ufloat(default, nan) - - Behavior: - - If the input string contains a value with parentheses (e.g. - "3.566(2)"), the number in parentheses is interpreted as an - estimated standard deviation (esd) in the last digit(s). - - If the input string has no parentheses, an uncertainty of NaN is - assigned to indicate "no esd provided". - - If parsing fails, the function falls back to the given `default` - value with uncertainty NaN. - - Parameters - ---------- - s : str or None - Numeric string in CIF format (e.g. "3.566", "3.566(2)") or None. - default : float or None, optional - Default value to use if `s` is None or parsing fails. - Defaults to None. - - Returns: - ------- - UFloat - An `uncertainties.UFloat` object with the parsed value and - uncertainty. The uncertainty will be NaN if not specified or - parsing failed. + Examples of supported input: - "3.566" → ufloat(3.566, nan) - + "3.566(2)" → ufloat(3.566, 0.002) - None → + ufloat(default, nan) + + Behavior: - If the input string contains a value with parentheses + (e.g. "3.566(2)"), the number in parentheses is interpreted as an + estimated standard deviation (esd) in the last digit(s). - If the + input string has no parentheses, an uncertainty of NaN is assigned + to indicate "no esd provided". - If parsing fails, the function + falls back to the given `default` value with uncertainty NaN. + + Parameters ---------- s : str or None Numeric string in CIF + format (e.g. "3.566", "3.566(2)") or None. default : float or None, + optional Default value to use if `s` is None or parsing fails. + Defaults to None. + + Returns: ------- UFloat An `uncertainties.UFloat` object with + the parsed value and uncertainty. The uncertainty will be NaN if + not specified or parsing failed. """ if s is None: return ufloat(default, np.nan) diff --git a/tools/convert_google_docstrings_to_numpy.py b/tools/convert_google_docstrings_to_numpy.py new file mode 100644 index 00000000..01ebaad0 --- /dev/null +++ b/tools/convert_google_docstrings_to_numpy.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python3 +"""Convert Google-style Python docstrings to numpydoc style.""" + +from __future__ import annotations + +import ast +import re +import sys +from pathlib import Path + +from docstring_parser import DocstringStyle +from docstring_parser import compose +from docstring_parser import parse +from format_docstring.docstring_rewriter import calc_abs_pos +from format_docstring.docstring_rewriter import calc_line_starts +from format_docstring.docstring_rewriter import find_docstring +from format_docstring.docstring_rewriter import rebuild_literal + +SECTION_NAMES = ( + 'Args', + 'Arguments', + 'Returns', + 'Raises', + 'Yields', + 'Attributes', + 'Examples', + 'Notes', +) +GOOGLE_SECTION_RE = re.compile( + r'(?m)^(?P[ \t]*)(?P
' + + '|'.join(SECTION_NAMES) + + r'):\s*(?P\S.*)?$' +) +SECTION_KINDS_WITH_ITEMS = {'Args', 'Arguments', 'Attributes'} + + +def _iter_python_files(paths: list[Path]) -> list[Path]: + files: list[Path] = [] + for path in paths: + if path.is_file() and path.suffix == '.py': + files.append(path) + continue + + if not path.exists(): + continue + + for file_path in sorted(path.rglob('*.py')): + if '_vendored' in file_path.parts: + continue + if '.pixi' in file_path.parts: + continue + files.append(file_path) + + return files + + +def _collect_names(node: ast.AST) -> list[str]: + names: list[str] = [] + + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + args = list(node.args.posonlyargs) + list(node.args.args) + args += list(node.args.kwonlyargs) + names.extend(arg.arg for arg in args) + if node.args.vararg is not None: + names.append(node.args.vararg.arg) + if node.args.kwarg is not None: + names.append(node.args.kwarg.arg) + return [name for name in names if name not in {'self', 'cls'}] + + if isinstance(node, ast.ClassDef): + init_method = next( + ( + stmt + for stmt in node.body + if isinstance(stmt, ast.FunctionDef) and stmt.name == '__init__' + ), + None, + ) + if init_method is not None: + names.extend(_collect_names(init_method)) + + for stmt in node.body: + if isinstance(stmt, ast.AnnAssign) and isinstance(stmt.target, ast.Name): + names.append(stmt.target.id) + elif isinstance(stmt, ast.Assign): + for target in stmt.targets: + if isinstance(target, ast.Name): + names.append(target.id) + + return list(dict.fromkeys(names)) + + +def _repair_inline_sections(docstring: str, names: list[str]) -> str: + repaired = docstring.replace('\r\n', '\n') + lines = repaired.split('\n') + out: list[str] = [] + current_section: str | None = None + section_indent = '' + + for raw_line in lines: + heading = GOOGLE_SECTION_RE.match(raw_line) + if heading: + current_section = heading.group('section') + section_indent = heading.group('indent') + section_name = 'Args' if current_section == 'Arguments' else current_section + out.append(f'{section_indent}{section_name}:') + rest = heading.group('rest') + if rest: + out.append(f'{section_indent} {rest.strip()}') + continue + + if current_section is not None and raw_line.strip(): + stripped = raw_line.strip() + if current_section in SECTION_KINDS_WITH_ITEMS: + for name in sorted(names, key=len, reverse=True): + stripped = re.sub( + rf'([ \t]{{2,}})({re.escape(name)}(?:\s*\([^)]*\))?:)', + rf'\n{section_indent} \2', + stripped, + ) + out.extend( + ( + line + if line.startswith(f'{section_indent} ') + else f'{section_indent} {line.strip()}' + ) + for line in stripped.split('\n') + ) + continue + + out.append(raw_line) + if not raw_line.strip(): + continue + + current_section = None + section_indent = '' + + return '\n'.join(out) + + +def _looks_google(docstring: str) -> bool: + return bool(GOOGLE_SECTION_RE.search(docstring)) + + +def _convert_docstring(docstring: str, names: list[str]) -> str | None: + if not _looks_google(docstring): + return None + + repaired = _repair_inline_sections(docstring, names) + + try: + parsed = parse(repaired, style=DocstringStyle.GOOGLE) + except Exception: + return None + + converted = compose(parsed, style=DocstringStyle.NUMPYDOC) + return converted if converted != docstring else None + + +def _convert_file(path: Path) -> bool: + source_code = path.read_text() + tree = ast.parse(source_code, type_comments=True) + line_starts = calc_line_starts(source_code) + replacements: list[tuple[int, int, str]] = [] + + nodes: list[ast.AST] = [tree] + nodes.extend(ast.walk(tree)) + + for node in nodes: + if not isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): + continue + + docstring_obj = find_docstring(node) + if docstring_obj is None: + continue + + value = docstring_obj.value + end_lineno = getattr(value, 'end_lineno', None) + end_col_offset = getattr(value, 'end_col_offset', None) + if end_lineno is None or end_col_offset is None: + continue + + docstring = ast.get_docstring(node, clean=False) + if docstring is None: + continue + + converted = _convert_docstring(docstring, _collect_names(node)) + if converted is None: + continue + + start = calc_abs_pos(source_code, line_starts, value.lineno, value.col_offset) + end = calc_abs_pos(source_code, line_starts, end_lineno, end_col_offset) + original_literal = source_code[start:end] + new_literal = rebuild_literal(original_literal, converted) + if new_literal is None or new_literal == original_literal: + continue + + replacements.append((start, end, new_literal)) + + if not replacements: + return False + + replacements.sort(reverse=True) + new_source = source_code + for start, end, replacement in replacements: + new_source = new_source[:start] + replacement + new_source[end:] + + path.write_text(new_source) + return True + + +def main(argv: list[str]) -> int: + input_paths = [Path(arg) for arg in argv] if argv else [Path('src'), Path('tools')] + changed = 0 + + for path in _iter_python_files(input_paths): + if _convert_file(path): + changed += 1 + print(f'Converted {path}') + + print(f'Converted docstrings in {changed} file(s).') + return 0 + + +if __name__ == '__main__': + raise SystemExit(main(sys.argv[1:])) diff --git a/tools/param_consistency.py b/tools/param_consistency.py index d59fb304..cd63989c 100644 --- a/tools/param_consistency.py +++ b/tools/param_consistency.py @@ -65,7 +65,6 @@ def length_a(self) -> Parameter: import argparse import ast import sys -import textwrap from dataclasses import dataclass from dataclasses import field from pathlib import Path @@ -80,9 +79,6 @@ def length_a(self) -> Parameter: / 'easydiffraction' ) -# Must match [tool.ruff.lint.pycodestyle] max-doc-length. -_MAX_DOC_LENGTH = 72 - _DESCRIPTOR_TYPES = frozenset( {'Parameter', 'NumericDescriptor', 'StringDescriptor'} ) @@ -166,39 +162,25 @@ def _getter_docstring( """Build the expected getter docstring.""" d = _strip_dot(desc) summary = f'{d} ({units}).' if units else f'{d}.' - summary_lines = textwrap.wrap( - summary, - width=_MAX_DOC_LENGTH, - initial_indent=f'{indent}"""', - subsequent_indent=indent, - ) if has_setter: - body_text = ( - 'Reading this property returns the ' - f'underlying ``{type_name}`` object. ' - 'Assigning to it updates the ' - 'parameter value.' + body = ( + f'Reading this property returns the underlying ' + f'``{type_name}`` object. ' + f'Assigning to it updates the parameter value.' ) else: - body_text = ( - 'Reading this property returns the ' - f'underlying ``{type_name}`` object.' + body = ( + f'Reading this property returns the underlying ' + f'``{type_name}`` object.' ) - body_lines = textwrap.wrap( - body_text, - width=_MAX_DOC_LENGTH, - initial_indent=indent, - subsequent_indent=indent, - ) - parts = ( - summary_lines - + [''] - + body_lines - + [f'{indent}"""'] + return ( + f'{indent}"""{summary}\n' + f'\n' + f'{indent}{body}\n' + f'{indent}"""\n' ) - return '\n'.join(parts) + '\n' def _normalize(text: str) -> str: @@ -484,14 +466,9 @@ def _analyze_property( ) else: actual_src = ''.join(lines[doc_s:doc_e]) - content_ok = _normalize(actual_src) == _normalize( + if _normalize(actual_src) != _normalize( expected_doc - ) - lines_ok = all( - len(line.rstrip('\n')) <= _MAX_DOC_LENGTH - for line in lines[doc_s:doc_e] - ) - if not content_ok: + ): result.issues.append( f'{loc}: getter docstring ' 'does not match template' @@ -499,14 +476,6 @@ def _analyze_property( result.edits.append( Edit(doc_s, doc_e, expected_doc) ) - elif not lines_ok: - result.issues.append( - f'{loc}: getter docstring ' - 'has lines exceeding max-doc-length' - ) - result.edits.append( - Edit(doc_s, doc_e, expected_doc) - ) # --- Setter --- if prop.setter is None: From 6878cb9111763c223e88919d5744d9d3942b9b07 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 12:54:18 +0100 Subject: [PATCH 20/48] Apply formatting --- docs/architecture/architecture.md | 14 +++++++------- pixi.lock | 4 ++-- pixi.toml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index 4c7c5498..884ed4f1 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -1100,14 +1100,14 @@ def length_a(self) -> Parameter: **Quick-reference table:** -| Element | Text | -| ---------------------- | ------------------------------------------------------------------------------------------- | -| Getter summary line | `"""{desc} ({units}).` (or `"""{desc}.` when unitless) | +| Element | Text | +| ---------------------- | -------------------------------------------------------------------------------------------------------------- | +| Getter summary line | `"""{desc} ({units}).` (or `"""{desc}.` when unitless) | | Getter body (writable) | `Reading this property returns the underlying ``{Type}`` object. Assigning to it updates the parameter value.` | -| Getter body (readonly) | `Reading this property returns the underlying ``{Type}`` object.` | -| Setter docstring | *(none — not rendered by griffe / MkDocs)* | -| Getter annotation | `-> {Type}` | -| Setter annotation | `value: {ann}` and `-> None` | +| Getter body (readonly) | `Reading this property returns the underlying ``{Type}`` object.` | +| Setter docstring | _(none — not rendered by griffe / MkDocs)_ | +| Getter annotation | `-> {Type}` | +| Setter annotation | `value: {ann}` and `-> None` | **Notes:** diff --git a/pixi.lock b/pixi.lock index 6496a69e..86e195fc 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty21 - sha256: 1bd07edc728222b4af7a1fe3486a1ccc1c874950a04196b9867479292e440d76 + version: 0.10.2+devdirty22 + sha256: cfe9bdf49d0c7b3f762a8cb40ace74c7d0a992552690484f5d3515ff747e9b32 requires_dist: - asciichartpy - asteval diff --git a/pixi.toml b/pixi.toml index 95961594..7c3bf288 100644 --- a/pixi.toml +++ b/pixi.toml @@ -128,7 +128,7 @@ nonpy-format-fix-modified = "python tools/nonpy_prettier_modified.py --write" success-message = 'echo "✅ All auto-formatting steps completed successfully!"' fix = { depends-on = [ - 'param-docstring-fix', # ED only + 'param-docstring-fix', # ED only 'docstring-format-fix', 'py-format-fix', 'py-lint-fix', From 891096cb8569e0bdfff40ba80e7bcc84d20e1beb Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 13:01:24 +0100 Subject: [PATCH 21/48] Apply formatting --- pixi.lock | 4 ++-- pixi.toml | 30 +++++++++++++++--------------- pyproject.toml | 3 ++- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/pixi.lock b/pixi.lock index 86e195fc..63fe6698 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty22 - sha256: cfe9bdf49d0c7b3f762a8cb40ace74c7d0a992552690484f5d3515ff747e9b32 + version: 0.10.2+devdirty23 + sha256: 39c132bdffe72192390de6d182198d9531506a7e745e26a1a274b6a807de449f requires_dist: - asciichartpy - asteval diff --git a/pixi.toml b/pixi.toml index 7c3bf288..1cbf7fbb 100644 --- a/pixi.toml +++ b/pixi.toml @@ -5,7 +5,7 @@ # Platform-independent [activation.env] -PYTHONIOENCODING = "utf-8" +PYTHONIOENCODING = 'utf-8' # Platform-specific @@ -58,7 +58,7 @@ gsl = '*' # GNU Scientific Library; required for pdffit2 [pypi-dependencies] # == [feature.default.pypi-dependencies] #pip = '*' # Native package installer -easydiffraction = { path = ".", editable = true, extras = ['dev'] } +easydiffraction = { path = '.', editable = true, extras = ['dev'] } # Specific features: Set specific Python versions @@ -108,9 +108,9 @@ param-docstring-check = 'python tools/param_consistency.py src/ --check' docstring-lint-check = 'pydoclint --quiet src/' notebook-lint-check = 'nbqa ruff docs/docs/tutorials/' py-lint-check = 'ruff check src/ tests/ docs/docs/tutorials/' -py-format-check = "ruff format --check src/ tests/ docs/docs/tutorials/" -nonpy-format-check = "npx prettier --list-different --config=prettierrc.toml --ignore-unknown ." -nonpy-format-check-modified = "python tools/nonpy_prettier_modified.py" +py-format-check = 'ruff format --check src/ tests/ docs/docs/tutorials/' +nonpy-format-check = 'npx prettier --list-different --config=prettierrc.toml --ignore-unknown .' +nonpy-format-check-modified = 'python tools/nonpy_prettier_modified.py' check = 'pre-commit run --hook-stage manual --all-files' @@ -122,9 +122,9 @@ param-docstring-fix = 'python tools/param_consistency.py src/ --fix' docstring-format-fix = 'format-docstring src/ docs/docs/tutorials/' notebook-lint-fix = 'nbqa ruff --fix docs/docs/tutorials/' py-lint-fix = 'ruff check --fix src/ tests/ docs/docs/tutorials/' -py-format-fix = "ruff format src/ tests/ docs/docs/tutorials/" +py-format-fix = 'ruff format src/ tests/ docs/docs/tutorials/' nonpy-format-fix = 'npx prettier --write --list-different --config=prettierrc.toml --ignore-unknown .' -nonpy-format-fix-modified = "python tools/nonpy_prettier_modified.py --write" +nonpy-format-fix-modified = 'python tools/nonpy_prettier_modified.py --write' success-message = 'echo "✅ All auto-formatting steps completed successfully!"' fix = { depends-on = [ @@ -182,11 +182,11 @@ notebook-prepare = { depends-on = [ ######################## docs-vars = "JUPYTER_PLATFORM_DIRS=1 PYTHONWARNINGS='ignore::RuntimeWarning'" -docs-pre = "pixi run docs-vars python -m mkdocs" -docs-serve = "pixi run docs-pre serve -f docs/mkdocs.yml" -docs-serve-dirty = "pixi run docs-serve --dirty" -docs-build = "pixi run docs-pre build -f docs/mkdocs.yml" -docs-build-local = "pixi run docs-build --no-directory-urls" +docs-pre = 'pixi run docs-vars python -m mkdocs' +docs-serve = 'pixi run docs-pre serve -f docs/mkdocs.yml' +docs-serve-dirty = 'pixi run docs-serve --dirty' +docs-build = 'pixi run docs-pre build -f docs/mkdocs.yml' +docs-build-local = 'pixi run docs-build --no-directory-urls' docs-deploy-pre = 'mike deploy -F docs/mkdocs.yml --push --branch gh-pages --update-aliases --alias-type redirect' docs-set-default-pre = 'mike set-default -F docs/mkdocs.yml --push --branch gh-pages' @@ -197,9 +197,9 @@ docs-update-assets = 'python tools/update_docs_assets.py' # 📦 Template Management Tasks ############################## -copier-copy = "copier copy gh:easyscience/templates . --data-file ../diffraction/.copier-answers.yml --data template_type=lib" -copier-recopy = "copier recopy --data-file ../diffraction/.copier-answers.yml --data template_type=lib" -copier-update = "copier update --data-file ../diffraction/.copier-answers.yml --data template_type=lib" +copier-copy = 'copier copy gh:easyscience/templates . --data-file ../diffraction/.copier-answers.yml --data template_type=lib' +copier-recopy = 'copier recopy --data-file ../diffraction/.copier-answers.yml --data template_type=lib' +copier-update = 'copier update --data-file ../diffraction/.copier-answers.yml --data template_type=lib' ##################### # 🪝 Pre-commit Hooks diff --git a/pyproject.toml b/pyproject.toml index 18d5e7f1..cbcd46cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -354,6 +354,7 @@ max-doc-length = 72 [tool.pydoclint] #exclude = '\.' # Temporarily disable pydoclint until we are ready +exclude = 'src/easydiffraction/utils/_vendored/jupyter_dark_detect' # ED only style = 'numpy' check-style-mismatch = true check-arg-defaults = true @@ -370,5 +371,5 @@ allow-init-docstring = true docstring_style = 'numpy' line_length = 72 fix_rst_backticks = true -exclude = "src/easydiffraction/utils/_vendored/jupyter_dark_detect" # ED only +exclude = 'src/easydiffraction/utils/_vendored/jupyter_dark_detect' # ED only verbose = 'diff' From 903cb84cd226224cf67f351c259b131208eee43a Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 13:01:52 +0100 Subject: [PATCH 22/48] Convert docstrings from google to numpy style: Step 1 --- src/easydiffraction/analysis/analysis.py | 72 +++++++--- .../analysis/calculators/crysfml.py | 28 +++- .../analysis/calculators/cryspy.py | 35 +++-- .../categories/constraints/default.py | 12 +- .../analysis/fit_helpers/metrics.py | 17 ++- .../analysis/fit_helpers/tracking.py | 9 +- .../analysis/minimizers/base.py | 25 +++- .../analysis/minimizers/dfols.py | 28 +++- .../analysis/minimizers/lmfit.py | 61 ++++++-- src/easydiffraction/core/category.py | 18 ++- src/easydiffraction/core/collection.py | 14 +- src/easydiffraction/core/factory.py | 9 +- src/easydiffraction/core/guard.py | 16 ++- src/easydiffraction/core/metadata.py | 16 ++- src/easydiffraction/core/variable.py | 51 +++++-- .../categories/experiment_type/default.py | 14 +- .../datablocks/experiment/collection.py | 18 ++- .../datablocks/experiment/item/base.py | 104 +++++++++++--- .../datablocks/experiment/item/bragg_pd.py | 9 +- .../datablocks/experiment/item/factory.py | 28 +++- .../categories/atom_sites/default.py | 29 +++- .../categories/space_group/default.py | 29 +++- .../datablocks/structure/collection.py | 27 +++- .../datablocks/structure/item/base.py | 72 +++++++--- .../datablocks/structure/item/factory.py | 58 ++++++-- .../display/plotters/plotly.py | 18 ++- src/easydiffraction/display/plotting.py | 27 +++- src/easydiffraction/display/tablers/base.py | 16 ++- src/easydiffraction/summary/summary.py | 9 +- src/easydiffraction/utils/environment.py | 40 ++++-- src/easydiffraction/utils/logging.py | 56 ++++++-- src/easydiffraction/utils/utils.py | 133 +++++++++++++----- tools/convert_google_docstrings_to_numpy.py | 57 +++++++- 33 files changed, 894 insertions(+), 261 deletions(-) diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index e88b12a8..232d8285 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -47,9 +47,14 @@ class Analysis: """ def __init__(self, project) -> None: - """Create a new Analysis instance bound to a project. + """ + Create a new Analysis instance bound to a project. + - Args: project: The project that owns models and experiments. + Parameters + ---------- + project + The project that owns models and experiments. """ self.project = project self._aliases_type: str = AliasesFactory.default_tag() @@ -134,9 +139,14 @@ def aliases_type(self) -> str: @aliases_type.setter def aliases_type(self, new_type: str) -> None: - """Switch to a different aliases collection type. + """ + Switch to a different aliases collection type. + - Args: new_type: Aliases tag (e.g. ``'default'``). + Parameters + ---------- + new_type + Aliases tag (e.g. ``'default'``). """ supported_tags = AliasesFactory.supported_tags() if new_type not in supported_tags: @@ -171,9 +181,14 @@ def constraints_type(self) -> str: @constraints_type.setter def constraints_type(self, new_type: str) -> None: - """Switch to a different constraints collection type. + """ + Switch to a different constraints collection type. + - Args: new_type: Constraints tag (e.g. ``'default'``). + Parameters + ---------- + new_type + Constraints tag (e.g. ``'default'``). """ supported_tags = ConstraintsFactory.supported_tags() if new_type not in supported_tags: @@ -201,12 +216,20 @@ def _get_params_as_dataframe( self, params: List[Union[NumericDescriptor, Parameter]], ) -> pd.DataFrame: - """Convert a list of parameters to a DataFrame. + """ + Convert a list of parameters to a DataFrame. + - Args: params: List of DescriptorFloat or Parameter objects. + Parameters + ---------- + params + List of DescriptorFloat or Parameter objects. - Returns: A pandas DataFrame containing parameter - information. + Returns + ------- + + A pandas DataFrame containing parameter + information. """ records = [] for param in params: @@ -479,9 +502,14 @@ def current_minimizer(self) -> Optional[str]: @current_minimizer.setter def current_minimizer(self, selection: str) -> None: - """Switch to a different minimizer implementation. + """ + Switch to a different minimizer implementation. - Args: selection: Minimizer selection string, e.g. 'lmfit'. + + Parameters + ---------- + selection + Minimizer selection string, e.g. 'lmfit'. """ self.fitter = Fitter(selection) console.paragraph('Current minimizer changed to') @@ -503,9 +531,14 @@ def fit_mode_type(self) -> str: @fit_mode_type.setter def fit_mode_type(self, new_type: str) -> None: - """Switch to a different fit-mode category type. + """ + Switch to a different fit-mode category type. - Args: new_type: Fit-mode tag (e.g. ``'default'``). + + Parameters + ---------- + new_type + Fit-mode tag (e.g. ``'default'``). """ supported_tags = FitModeFactory.supported_tags() if new_type not in supported_tags: @@ -694,10 +727,15 @@ def _update_categories(self, called_by_minimizer=False) -> None: category._update(called_by_minimizer=called_by_minimizer) def as_cif(self): - """Serialize the analysis section to a CIF string. + """ + Serialize the analysis section to a CIF string. + + + Returns + ------- - Returns: The analysis section represented as a CIF document - string. + The analysis section represented as a CIF document + string. """ from easydiffraction.io.cif.serialize import analysis_to_cif diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 6514bd96..f90675eb 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -128,11 +128,19 @@ def _convert_structure_to_dict( self, structure: Structure, ) -> Dict[str, Any]: - """Converts a structure into a dictionary format. + """ + Converts a structure into a dictionary format. + + + Parameters + ---------- + structure + The structure to convert. - Args: structure: The structure to convert. + Returns + ------- - Returns: A dictionary representation of the structure. + A dictionary representation of the structure. """ structure_dict = { structure.name: { @@ -166,11 +174,19 @@ def _convert_experiment_to_dict( self, experiment: ExperimentBase, ) -> Dict[str, Any]: - """Converts an experiment into a dictionary format. + """ + Converts an experiment into a dictionary format. + + + Parameters + ---------- + experiment + The experiment to convert. - Args: experiment: The experiment to convert. + Returns + ------- - Returns: A dictionary representation of the experiment. + A dictionary representation of the experiment. """ expt_type = getattr(experiment, 'type', None) instrument = getattr(experiment, 'instrument', None) diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 88d16009..c32d683d 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -329,12 +329,20 @@ def _convert_structure_to_cryspy_cif( self, structure: Structure, ) -> str: - """Converts a structure to a Cryspy CIF string. + """ + Converts a structure to a Cryspy CIF string. + - Args: structure: The structure to convert. + Parameters + ---------- + structure + The structure to convert. - Returns: The Cryspy CIF string representation of the - structure. + Returns + ------- + + The Cryspy CIF string representation of the + structure. """ return structure.as_cif @@ -343,13 +351,22 @@ def _convert_experiment_to_cryspy_cif( experiment: ExperimentBase, linked_structure: Any, ) -> str: - """Converts an experiment to a Cryspy CIF string. + """ + Converts an experiment to a Cryspy CIF string. - Args: experiment: The experiment to convert. - linked_structure: The structure linked to the experiment. - Returns: The Cryspy CIF string representation of the - experiment. + Parameters + ---------- + experiment + The experiment to convert. + linked_structure + The structure linked to the experiment. + + Returns + ------- + + The Cryspy CIF string representation of the + experiment. """ # Try to get experiment attributes expt_type = getattr(experiment, 'type', None) diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 1ec65b42..39ec944b 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -20,10 +20,16 @@ class Constraint(CategoryItem): - """Single constraint item. + """ + Single constraint item. + - Args: lhs_alias: Left-hand side alias name being constrained. - rhs_expr: Right-hand side expression as a string. + Parameters + ---------- + lhs_alias + Left-hand side alias name being constrained. + rhs_expr + Right-hand side expression as a string. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index 1e488c5a..0925e7e0 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -92,12 +92,21 @@ def calculate_reduced_chi_square( residuals: np.ndarray, num_parameters: int, ) -> float: - """Calculate the reduced chi-square statistic. + """ + Calculate the reduced chi-square statistic. + + + Parameters + ---------- + residuals + Residuals between observed and calculated data. + num_parameters + Number of free parameters used in the model. - Args: residuals: Residuals between observed and calculated data. - num_parameters: Number of free parameters used in the model. + Returns + ------- - Returns: Reduced chi-square value. + Reduced chi-square value. """ residuals = np.asarray(residuals) chi_square = np.sum(residuals**2) diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index c9d4c530..2560d53d 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -218,9 +218,14 @@ def start_tracking(self, minimizer_name: str) -> None: ) def add_tracking_info(self, row: List[str]) -> None: - """Append a formatted row to the progress display. + """ + Append a formatted row to the progress display. + - Args: row: Columns corresponding to DEFAULT_HEADERS. + Parameters + ---------- + row + Columns corresponding to DEFAULT_HEADERS. """ # Append and update via the active handle (Jupyter or # terminal live) diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index ec1c880c..2560f0a0 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -42,9 +42,14 @@ def __init__( self.tracker: FitProgressTracker = FitProgressTracker() def _start_tracking(self, minimizer_name: str) -> None: - """Initialize progress tracking and timer. + """ + Initialize progress tracking and timer. + - Args: minimizer_name: Human-readable name shown in progress. + Parameters + ---------- + minimizer_name + Human-readable name shown in progress. """ self.tracker.reset() self.tracker.start_tracking(minimizer_name) @@ -57,12 +62,20 @@ def _stop_tracking(self) -> None: @abstractmethod def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: - """Prepare keyword-arguments for the underlying solver. + """ + Prepare keyword-arguments for the underlying solver. + + + Parameters + ---------- + parameters + List of free parameters to be fitted. - Args: parameters: List of free parameters to be fitted. + Returns + ------- - Returns: Mapping of keyword arguments to pass into - ``_run_solver``. + Mapping of keyword arguments to pass into + ``_run_solver``. """ pass diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index da5268b4..95894eb0 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -57,10 +57,16 @@ def _sync_result_to_parameters( parameters: List[Any], raw_result: Any, ) -> None: - """Synchronizes the result from the solver to the parameters. + """ + Synchronizes the result from the solver to the parameters. + - Args: parameters: List of parameters being optimized. - raw_result: The result object returned by the solver. + Parameters + ---------- + parameters + List of parameters being optimized. + raw_result + The result object returned by the solver. """ # Ensure compatibility with raw_result coming from dfols.solve() result_values = raw_result.x if hasattr(raw_result, 'x') else raw_result @@ -74,11 +80,19 @@ def _sync_result_to_parameters( param.uncertainty = None def _check_success(self, raw_result: Any) -> bool: - """Determines success from DFO-LS result dictionary. + """ + Determines success from DFO-LS result dictionary. + + + Parameters + ---------- + raw_result + The result object returned by the solver. - Args: raw_result: The result object returned by the solver. + Returns + ------- - Returns: True if the optimization was successful, False - otherwise. + True if the optimization was successful, False + otherwise. """ return raw_result.flag == raw_result.EXIT_SUCCESS diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index c326ff2f..7792b39a 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -40,12 +40,20 @@ def _prepare_solver_args( self, parameters: List[Any], ) -> Dict[str, Any]: - """Prepares the solver arguments for the lmfit minimizer. + """ + Prepares the solver arguments for the lmfit minimizer. + + + Parameters + ---------- + parameters + List of parameters to be optimized. - Args: parameters: List of parameters to be optimized. + Returns + ------- - Returns: A dictionary containing the prepared lmfit. - Parameters object. + A dictionary containing the prepared lmfit. + Parameters object. """ engine_parameters = lmfit.Parameters() for param in parameters: @@ -59,12 +67,21 @@ def _prepare_solver_args( return {'engine_parameters': engine_parameters} def _run_solver(self, objective_function: Any, **kwargs: Any) -> Any: - """Runs the lmfit solver. + """ + Runs the lmfit solver. + + + Parameters + ---------- + objective_function + The objective function to + minimize. **kwargs + Additional arguments for the solver. - Args: objective_function: The objective function to - minimize. **kwargs: Additional arguments for the solver. + Returns + ------- - Returns: The result of the lmfit minimization. + The result of the lmfit minimization. """ engine_parameters = kwargs.get('engine_parameters') @@ -81,10 +98,16 @@ def _sync_result_to_parameters( parameters: List[Any], raw_result: Any, ) -> None: - """Synchronizes the result from the solver to the parameters. + """ + Synchronizes the result from the solver to the parameters. + - Args: parameters: List of parameters being optimized. - raw_result: The result object returned by the solver. + Parameters + ---------- + parameters + List of parameters being optimized. + raw_result + The result object returned by the solver. """ param_values = raw_result.params if hasattr(raw_result, 'params') else raw_result @@ -97,12 +120,20 @@ def _sync_result_to_parameters( param.uncertainty = getattr(param_result, 'stderr', None) def _check_success(self, raw_result: Any) -> bool: - """Determines success from lmfit MinimizerResult. + """ + Determines success from lmfit MinimizerResult. + + + Parameters + ---------- + raw_result + The result object returned by the solver. - Args: raw_result: The result object returned by the solver. + Returns + ------- - Returns: True if the optimization was successful, False - otherwise. + True if the optimization was successful, False + otherwise. """ return getattr(raw_result, 'success', False) diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index 14e7bed7..6f60b8c0 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -216,20 +216,30 @@ def from_cif(self, block): category_collection_from_cif(self, block) def add(self, item) -> None: - """Insert or replace a pre-built item into the collection. + """ + Insert or replace a pre-built item into the collection. + - Args: item: A ``CategoryItem`` instance to add. + Parameters + ---------- + item + A ``CategoryItem`` instance to add. """ self[item._identity.category_entry_name] = item self._mark_parent_dirty() def create(self, **kwargs) -> None: - """Create a new item with the given attributes and add it. + """ + Create a new item with the given attributes and add it. A default instance of the collection's item type is created, then each keyword argument is applied via ``setattr``. - Args: **kwargs: Attribute names and values for the new item. + + Parameters + ---------- + **kwargs + Attribute names and values for the new item. """ child_obj = self._item_type() diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index f0e29bbf..1e3381ec 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -74,11 +74,19 @@ def __len__(self) -> int: return len(self._items) def remove(self, name: str) -> None: - """Remove an item by its key. + """ + Remove an item by its key. + - Args: name: Identity key of the item to remove. + Parameters + ---------- + name + Identity key of the item to remove. - Raises: KeyError: If no item with the given key exists. + Raises + ------ + KeyError + If no item with the given key exists. """ del self[name] diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index 8f228c79..18508d2c 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -129,11 +129,16 @@ def create(cls, tag: str, **kwargs) -> Any: @classmethod def create_default_for(cls, **conditions) -> Any: - """Instantiate the default class for a given context. + """ + Instantiate the default class for a given context. Combines ``default_tag(**conditions)`` with ``create(tag)``. - Args: **conditions: Experimental-axis values. + + Parameters + ---------- + **conditions + Experimental-axis values. """ tag = cls.default_tag(**conditions) return cls.create(tag) diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index 3f72a458..a98c2f5c 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -78,10 +78,14 @@ def _assign_attr(self, key, value): @classmethod def _iter_properties(cls): - """Iterate over all public properties defined in the class + """ + Iterate over all public properties defined in the class hierarchy. - Yields: tuple[str, property]: Each (key, property) pair for + + Yields + ------ + Each (key, property) pair for public attributes. """ for base in cls.mro(): @@ -158,9 +162,13 @@ def _first_sentence(docstring: str | None) -> str: @classmethod def _iter_methods(cls): - """Iterate over public methods in the class hierarchy. + """ + Iterate over public methods in the class hierarchy. + - Yields: tuple[str, callable]: Each (name, function) pair. + Yields + ------ + Each (name, function) pair. """ seen: set = set() for base in cls.mro(): diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index 53c678ba..f9a4d6cb 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -89,12 +89,20 @@ class CalculatorSupport: calculators: FrozenSet = frozenset() def supports(self, calculator) -> bool: - """Check if a specific calculator can handle this class. + """ + Check if a specific calculator can handle this class. + + + Parameters + ---------- + calculator + A ``CalculatorEnum`` value. - Args: calculator: A ``CalculatorEnum`` value. + Returns + ------- - Returns: ``True`` if the calculator is in the set, or if the - set is empty (meaning any calculator is accepted). + ``True`` if the calculator is in the set, or if the + set is empty (meaning any calculator is accepted). """ if not self.calculators: return True diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 7ea699d0..59da095b 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -52,11 +52,18 @@ def __init__( name: str, description: str = None, ): - """Initialize the descriptor with validation and identity. + """ + Initialize the descriptor with validation and identity. + - Args: value_spec: Validation specification for the value. - name: Local name of the descriptor within its category. - description: Optional human-readable description. + Parameters + ---------- + value_spec + Validation specification for the value. + name + Local name of the descriptor within its category. + description + Optional human-readable description. """ super().__init__() @@ -386,10 +393,16 @@ def __init__( cif_handler: CifHandler, **kwargs: Any, ) -> None: - """String descriptor bound to a CIF handler. + """ + String descriptor bound to a CIF handler. + - Args: cif_handler: Object that tracks CIF identifiers. - **kwargs: Forwarded to GenericStringDescriptor. + Parameters + ---------- + cif_handler + Object that tracks CIF identifiers. + **kwargs + Forwarded to GenericStringDescriptor. """ super().__init__(**kwargs) self._cif_handler = cif_handler @@ -406,10 +419,16 @@ def __init__( cif_handler: CifHandler, **kwargs: Any, ) -> None: - """Numeric descriptor bound to a CIF handler. + """ + Numeric descriptor bound to a CIF handler. + - Args: cif_handler: Object that tracks CIF identifiers. - **kwargs: Forwarded to GenericNumericDescriptor. + Parameters + ---------- + cif_handler + Object that tracks CIF identifiers. + **kwargs + Forwarded to GenericNumericDescriptor. """ super().__init__(**kwargs) self._cif_handler = cif_handler @@ -426,10 +445,16 @@ def __init__( cif_handler: CifHandler, **kwargs: Any, ) -> None: - """Fittable parameter bound to a CIF handler. + """ + Fittable parameter bound to a CIF handler. + - Args: cif_handler: Object that tracks CIF identifiers. - **kwargs: Forwarded to GenericParameter. + Parameters + ---------- + cif_handler + Object that tracks CIF identifiers. + **kwargs + Forwarded to GenericParameter. """ super().__init__(**kwargs) self._cif_handler = cif_handler diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index c2176f29..91ff3647 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -26,11 +26,17 @@ @ExperimentTypeFactory.register class ExperimentType(CategoryItem): - """Container of categorical attributes defining experiment flavor. + """ + Container of categorical attributes defining experiment flavor. + - Args: sample_form: Powder or Single crystal. beam_mode: - Constant wavelength (CW) or time-of-flight (TOF). radiation_probe: - Neutrons or X-rays. scattering_type: Bragg or Total. + Parameters + ---------- + sample_form + Powder or Single crystal. beam_mode: + Constant wavelength : CW) or time-of-flight (TOF + Neutrons or X-rays. scattering_type + Bragg or Total. """ type_info = TypeInfo( diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index 404886bb..1d9fbe67 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -58,9 +58,14 @@ def add_from_cif_str( self, cif_str: str, ) -> None: - """Add an experiment from a CIF string. + """ + Add an experiment from a CIF string. + - Args: cif_str: Full CIF document as a string. + Parameters + ---------- + cif_str + Full CIF document as a string. """ experiment = ExperimentFactory.from_cif_str(cif_str) self.add(experiment) @@ -71,9 +76,14 @@ def add_from_cif_path( self, cif_path: str, ) -> None: - """Add an experiment from a CIF file path. + """ + Add an experiment from a CIF file path. + - Args: cif_path(str): Path to a CIF document. + Parameters + ---------- + cif_path : str + Path to a CIF document. """ experiment = ExperimentFactory.from_cif_path(cif_path) self.add(experiment) diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 19075a94..4d06be50 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -58,9 +58,14 @@ def name(self) -> str: @name.setter def name(self, new: str) -> None: - """Rename the experiment. + """ + Rename the experiment. + - Args: new: New name for this experiment. + Parameters + ---------- + new + New name for this experiment. """ self._name = new @@ -85,9 +90,14 @@ def show_as_cif(self) -> None: @abstractmethod def _load_ascii_data_to_experiment(self, data_path: str) -> None: - """Load ASCII data from file into the experiment data category. + """ + Load ASCII data from file into the experiment data category. - Args: data_path: Path to the ASCII file to load. + + Parameters + ---------- + data_path + Path to the ASCII file to load. """ raise NotImplementedError() @@ -251,9 +261,14 @@ def extinction_type(self) -> str: @extinction_type.setter def extinction_type(self, new_type: str) -> None: - """Switch to a different extinction correction model. + """ + Switch to a different extinction correction model. + - Args: new_type: Extinction tag (e.g. ``'shelx'``). + Parameters + ---------- + new_type + Extinction tag (e.g. ``'shelx'``). """ supported_tags = ExtinctionFactory.supported_tags() if new_type not in supported_tags: @@ -294,9 +309,14 @@ def linked_crystal_type(self) -> str: @linked_crystal_type.setter def linked_crystal_type(self, new_type: str) -> None: - """Switch to a different linked-crystal reference type. + """ + Switch to a different linked-crystal reference type. + - Args: new_type: Linked-crystal tag (e.g. ``'default'``). + Parameters + ---------- + new_type + Linked-crystal tag (e.g. ``'default'``). """ supported_tags = LinkedCrystalFactory.supported_tags() if new_type not in supported_tags: @@ -337,9 +357,14 @@ def instrument_type(self) -> str: @instrument_type.setter def instrument_type(self, new_type: str) -> None: - """Switch to a different instrument type. + """ + Switch to a different instrument type. - Args: new_type: Instrument tag (e.g. ``'cwl-sc'``). + + Parameters + ---------- + new_type + Instrument tag (e.g. ``'cwl-sc'``). """ supported = InstrumentFactory.supported_for( scattering_type=self.type.scattering_type.value, @@ -388,9 +413,14 @@ def data_type(self) -> str: @data_type.setter def data_type(self, new_type: str) -> None: - """Switch to a different data collection type. + """ + Switch to a different data collection type. + - Args: new_type: Data tag (e.g. ``'bragg-sc'``). + Parameters + ---------- + new_type + Data tag (e.g. ``'bragg-sc'``). """ supported_tags = DataFactory.supported_tags() if new_type not in supported_tags: @@ -446,11 +476,19 @@ def _get_valid_linked_phases( self, structures: Structures, ) -> List[Any]: - """Get valid linked phases for this experiment. + """ + Get valid linked phases for this experiment. + + + Parameters + ---------- + structures + Collection of structures. - Args: structures: Collection of structures. + Returns + ------- - Returns: A list of valid linked phases. + A list of valid linked phases. """ if not self.linked_phases: print('Warning: No linked phases defined. Returning empty pattern.') @@ -495,9 +533,14 @@ def linked_phases_type(self) -> str: @linked_phases_type.setter def linked_phases_type(self, new_type: str) -> None: - """Switch to a different linked-phases collection type. + """ + Switch to a different linked-phases collection type. + - Args: new_type: Linked-phases tag (e.g. ``'default'``). + Parameters + ---------- + new_type + Linked-phases tag (e.g. ``'default'``). """ supported_tags = LinkedPhasesFactory.supported_tags() if new_type not in supported_tags: @@ -534,9 +577,14 @@ def excluded_regions_type(self) -> str: @excluded_regions_type.setter def excluded_regions_type(self, new_type: str) -> None: - """Switch to a different excluded-regions collection type. + """ + Switch to a different excluded-regions collection type. - Args: new_type: Excluded-regions tag (e.g. ``'default'``). + + Parameters + ---------- + new_type + Excluded-regions tag (e.g. ``'default'``). """ supported_tags = ExcludedRegionsFactory.supported_tags() if new_type not in supported_tags: @@ -579,9 +627,14 @@ def data_type(self) -> str: @data_type.setter def data_type(self, new_type: str) -> None: - """Switch to a different data collection type. + """ + Switch to a different data collection type. + - Args: new_type: Data tag (e.g. ``'bragg-pd-cwl'``). + Parameters + ---------- + new_type + Data tag (e.g. ``'bragg-pd-cwl'``). """ supported_tags = DataFactory.supported_tags() if new_type not in supported_tags: @@ -617,9 +670,14 @@ def peak_profile_type(self): @peak_profile_type.setter def peak_profile_type(self, new_type: str): - """Change the active peak profile type, if supported. + """ + Change the active peak profile type, if supported. + - Args: new_type: New profile type as tag string. + Parameters + ---------- + new_type + New profile type as tag string. """ supported = PeakFactory.supported_for( scattering_type=self.type.scattering_type.value, diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index ca331fc8..c54ce3a9 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -116,9 +116,14 @@ def instrument_type(self) -> str: @instrument_type.setter def instrument_type(self, new_type: str) -> None: - """Switch to a different instrument type. + """ + Switch to a different instrument type. + - Args: new_type: Instrument tag (e.g. ``'cwl-pd'``). + Parameters + ---------- + new_type + Instrument tag (e.g. ``'cwl-pd'``). """ supported = InstrumentFactory.supported_for( scattering_type=self.type.scattering_type.value, diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 650d86d4..4d127922 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -167,11 +167,19 @@ def from_cif_str( cls, cif_str: str, ) -> ExperimentBase: - """Create an experiment from a CIF string. + """ + Create an experiment from a CIF string. + + + Parameters + ---------- + cif_str + Full CIF document as a string. - Args: cif_str: Full CIF document as a string. + Returns + ------- - Returns: A populated experiment instance. + A populated experiment instance. """ doc = document_from_string(cif_str) block = pick_sole_block(doc) @@ -184,11 +192,19 @@ def from_cif_path( cls, cif_path: str, ) -> ExperimentBase: - """Create an experiment from a CIF file path. + """ + Create an experiment from a CIF file path. + + + Parameters + ---------- + cif_path + Path to a CIF file. - Args: cif_path: Path to a CIF file. + Returns + ------- - Returns: A populated experiment instance. + A populated experiment instance. """ doc = document_from_path(cif_path) block = pick_sole_block(doc) diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index 4d790f5e..7f569a0e 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -138,18 +138,28 @@ def __init__(self) -> None: @property def _type_symbol_allowed_values(self) -> list[str]: - """Return chemical symbols accepted by *cryspy*. + """ + Return chemical symbols accepted by *cryspy*. + - Returns: list[str]: Unique element/isotope symbols from the - database. + Returns + ------- + list[str] + Unique element/isotope symbols from the + database. """ return list({key[1] for key in DATABASE['Isotopes']}) @property def _wyckoff_letter_allowed_values(self) -> list[str]: - """Return allowed Wyckoff-letter symbols. + """ + Return allowed Wyckoff-letter symbols. - Returns: list[str]: Currently a hard-coded placeholder list. + + Returns + ------- + list[str] + Currently a hard-coded placeholder list. """ # TODO: Need to now current space group. How to access it? Via # parent Cell? Then letters = @@ -159,9 +169,14 @@ def _wyckoff_letter_allowed_values(self) -> list[str]: @property def _wyckoff_letter_default_value(self) -> str: - """Return the default Wyckoff letter. + """ + Return the default Wyckoff letter. + - Returns: str: First element of the allowed values list. + Returns + ------- + str + First element of the allowed values list. """ # TODO: What to pass as default? return self._wyckoff_letter_allowed_values[0] diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 4b55b1c7..5e5a0439 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -91,19 +91,29 @@ def _reset_it_coordinate_system_code(self) -> None: @property def _name_h_m_allowed_values(self) -> list[str]: - """Return the list of recognised Hermann–Mauguin short symbols. + """ + Return the list of recognised Hermann–Mauguin short symbols. + - Returns: list[str]: All short H-M symbols known to *cryspy*. + Returns + ------- + list[str] + All short H-M symbols known to *cryspy*. """ return ACCESIBLE_NAME_HM_SHORT @property def _it_coordinate_system_code_allowed_values(self) -> list[str]: - """Return allowed IT coordinate system codes for the current + """ + Return allowed IT coordinate system codes for the current group. - Returns: list[str]: Coordinate-system codes, or ``['']`` - when none are defined. + + Returns + ------- + list[str] + Coordinate-system codes, or ``['']`` + when none are defined. """ name = self.name_h_m.value it_number = get_it_number_by_name_hm_short(name) @@ -113,9 +123,14 @@ def _it_coordinate_system_code_allowed_values(self) -> list[str]: @property def _it_coordinate_system_code_default_value(self) -> str: - """Return the default IT coordinate system code. + """ + Return the default IT coordinate system code. + - Returns: str: First element of the allowed codes list. + Returns + ------- + str + First element of the allowed codes list. """ return self._it_coordinate_system_code_allowed_values[0] diff --git a/src/easydiffraction/datablocks/structure/collection.py b/src/easydiffraction/datablocks/structure/collection.py index 052b16db..1aedd334 100644 --- a/src/easydiffraction/datablocks/structure/collection.py +++ b/src/easydiffraction/datablocks/structure/collection.py @@ -33,9 +33,14 @@ def create( *, name: str, ) -> None: - """Create a minimal structure and add it to the collection. + """ + Create a minimal structure and add it to the collection. + - Args: name (str): Identifier for the new structure. + Parameters + ---------- + name : str + Identifier for the new structure. """ structure = StructureFactory.from_scratch(name=name) self.add(structure) @@ -46,9 +51,14 @@ def add_from_cif_str( self, cif_str: str, ) -> None: - """Create a structure from CIF content and add it. + """ + Create a structure from CIF content and add it. - Args: cif_str (str): CIF file content as a string. + + Parameters + ---------- + cif_str : str + CIF file content as a string. """ structure = StructureFactory.from_cif_str(cif_str) self.add(structure) @@ -59,9 +69,14 @@ def add_from_cif_path( self, cif_path: str, ) -> None: - """Create a structure from a CIF file and add it. + """ + Create a structure from a CIF file and add it. + - Args: cif_path (str): Filesystem path to a CIF file. + Parameters + ---------- + cif_path : str + Filesystem path to a CIF file. """ structure = StructureFactory.from_cif_path(cif_path) self.add(structure) diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index 0f42e968..9a20f40a 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -40,18 +40,28 @@ def __init__( @property def name(self) -> str: - """Name identifier for this structure. + """ + Name identifier for this structure. + - Returns: str: The structure's name. + Returns + ------- + str + The structure's name. """ return self._name @name.setter @typechecked def name(self, new: str) -> None: - """Set the name identifier for this structure. + """ + Set the name identifier for this structure. + - Args: new (str): New name string. + Parameters + ---------- + new : str + New name string. """ self._name = new @@ -67,9 +77,14 @@ def cell(self) -> Cell: @cell.setter @typechecked def cell(self, new: Cell) -> None: - """Replace the unit-cell category for this structure. + """ + Replace the unit-cell category for this structure. + - Args: new (Cell): New unit-cell instance. + Parameters + ---------- + new : Cell + New unit-cell instance. """ self._cell = new @@ -80,9 +95,14 @@ def cell_type(self) -> str: @cell_type.setter def cell_type(self, new_type: str) -> None: - """Switch to a different unit-cell type. + """ + Switch to a different unit-cell type. + - Args: new_type: Cell tag (e.g. ``'default'``). + Parameters + ---------- + new_type + Cell tag (e.g. ``'default'``). """ supported_tags = CellFactory.supported_tags() if new_type not in supported_tags: @@ -118,9 +138,14 @@ def space_group(self) -> SpaceGroup: @space_group.setter @typechecked def space_group(self, new: SpaceGroup) -> None: - """Replace the space-group category for this structure. + """ + Replace the space-group category for this structure. + - Args: new (SpaceGroup): New space-group instance. + Parameters + ---------- + new : SpaceGroup + New space-group instance. """ self._space_group = new @@ -131,9 +156,14 @@ def space_group_type(self) -> str: @space_group_type.setter def space_group_type(self, new_type: str) -> None: - """Switch to a different space-group type. + """ + Switch to a different space-group type. + - Args: new_type: Space-group tag (e.g. ``'default'``). + Parameters + ---------- + new_type + Space-group tag (e.g. ``'default'``). """ supported_tags = SpaceGroupFactory.supported_tags() if new_type not in supported_tags: @@ -169,9 +199,14 @@ def atom_sites(self) -> AtomSites: @atom_sites.setter @typechecked def atom_sites(self, new: AtomSites) -> None: - """Replace the atom-sites collection for this structure. + """ + Replace the atom-sites collection for this structure. + - Args: new (AtomSites): New atom-sites collection. + Parameters + ---------- + new : AtomSites + New atom-sites collection. """ self._atom_sites = new @@ -182,9 +217,14 @@ def atom_sites_type(self) -> str: @atom_sites_type.setter def atom_sites_type(self, new_type: str) -> None: - """Switch to a different atom-sites collection type. + """ + Switch to a different atom-sites collection type. + - Args: new_type: Atom-sites tag (e.g. ``'default'``). + Parameters + ---------- + new_type + Atom-sites tag (e.g. ``'default'``). """ supported_tags = AtomSitesFactory.supported_tags() if new_type not in supported_tags: diff --git a/src/easydiffraction/datablocks/structure/item/factory.py b/src/easydiffraction/datablocks/structure/item/factory.py index b39164f3..a1d43fe6 100644 --- a/src/easydiffraction/datablocks/structure/item/factory.py +++ b/src/easydiffraction/datablocks/structure/item/factory.py @@ -42,11 +42,19 @@ def _from_gemmi_block( cls, block: gemmi.cif.Block, ) -> Structure: - """Build a structure from a single *gemmi* CIF block. + """ + Build a structure from a single *gemmi* CIF block. + - Args: block (gemmi.cif.Block): Parsed CIF data block. + Parameters + ---------- + block : gemmi.cif.Block + Parsed CIF data block. - Returns: Structure: A fully populated structure instance. + Returns + ------- + Structure + A fully populated structure instance. """ name = name_from_block(block) structure = Structure(name=name) @@ -65,12 +73,20 @@ def from_scratch( *, name: str, ) -> Structure: - """Create a minimal default structure. + """ + Create a minimal default structure. + - Args: name (str): Identifier for the new structure. + Parameters + ---------- + name : str + Identifier for the new structure. - Returns: Structure: An empty structure with default - categories. + Returns + ------- + Structure + An empty structure with default + categories. """ return Structure(name=name) @@ -81,11 +97,19 @@ def from_cif_str( cls, cif_str: str, ) -> Structure: - """Create a structure by parsing a CIF string. + """ + Create a structure by parsing a CIF string. + - Args: cif_str (str): Raw CIF content. + Parameters + ---------- + cif_str : str + Raw CIF content. - Returns: Structure: A populated structure instance. + Returns + ------- + Structure + A populated structure instance. """ doc = document_from_string(cif_str) block = pick_sole_block(doc) @@ -98,11 +122,19 @@ def from_cif_path( cls, cif_path: str, ) -> Structure: - """Create a structure by reading and parsing a CIF file. + """ + Create a structure by reading and parsing a CIF file. + - Args: cif_path (str): Filesystem path to a CIF file. + Parameters + ---------- + cif_path : str + Filesystem path to a CIF file. - Returns: Structure: A populated structure instance. + Returns + ------- + Structure + A populated structure instance. """ doc = document_from_path(cif_path) block = pick_sole_block(doc) diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index 09a5504f..53c520ec 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -102,12 +102,17 @@ def _get_single_crystal_trace( return trace def _get_diagonal_shape(self): - """Create a diagonal reference line shape. + """ + Create a diagonal reference line shape. Returns a y=x diagonal line spanning the plot area using paper coordinates (0,0) to (1,1). - Returns: A dict configuring a diagonal line shape. + + Returns + ------- + + A dict configuring a diagonal line shape. """ return dict( type='line', @@ -122,9 +127,14 @@ def _get_diagonal_shape(self): ) def _get_config(self): - """Return the Plotly figure configuration. + """ + Return the Plotly figure configuration. + + + Returns + ------- - Returns: A dict with display and mode bar settings. + A dict with display and mode bar settings. """ return dict( displaylogo=False, diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 84bcef10..a431fe7f 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -241,9 +241,14 @@ def x_min(self): @x_min.setter def x_min(self, value): - """Set the minimum x-axis limit. + """ + Set the minimum x-axis limit. + - Args: value: Minimum limit or ``None`` to reset to default. + Parameters + ---------- + value + Minimum limit or ``None`` to reset to default. """ if value is not None: self._x_min = value @@ -257,9 +262,14 @@ def x_max(self): @x_max.setter def x_max(self, value): - """Set the maximum x-axis limit. + """ + Set the maximum x-axis limit. - Args: value: Maximum limit or ``None`` to reset to default. + + Parameters + ---------- + value + Maximum limit or ``None`` to reset to default. """ if value is not None: self._x_max = value @@ -273,9 +283,14 @@ def height(self): @height.setter def height(self, value): - """Set plot height. + """ + Set plot height. + - Args: value: Height value or ``None`` to reset to default. + Parameters + ---------- + value + Height value or ``None`` to reset to default. """ if value is not None: self._height = value diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index e77f4a93..3f969952 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -34,12 +34,20 @@ def __init__(self) -> None: self._float_fmt = f'{{:.{self.FLOAT_PRECISION}f}}'.format def _format_value(self, value: Any) -> Any: - """Format floats with fixed precision and others as strings. + """ + Format floats with fixed precision and others as strings. + + + Parameters + ---------- + value + Cell value to format. - Args: value: Cell value to format. + Returns + ------- - Returns: A string representation with fixed precision for - floats or ``str(value)`` for other types. + A string representation with fixed precision for + floats or ``str(value)`` for other types. """ return self._float_fmt(value) if isinstance(value, float) else str(value) diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index 67e25d9c..b6f0590c 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -16,9 +16,14 @@ class Summary: """ def __init__(self, project) -> None: - """Initialize the summary with a reference to the project. + """ + Initialize the summary with a reference to the project. + - Args: project: The Project instance this summary belongs to. + Parameters + ---------- + project + The Project instance this summary belongs to. """ self.project = project diff --git a/src/easydiffraction/utils/environment.py b/src/easydiffraction/utils/environment.py index 11ed2816..e773b6df 100644 --- a/src/easydiffraction/utils/environment.py +++ b/src/easydiffraction/utils/environment.py @@ -17,17 +17,27 @@ def in_warp() -> bool: def in_pycharm() -> bool: - """Determines if the current environment is PyCharm. + """ + Determines if the current environment is PyCharm. + - Returns: bool: True if running inside PyCharm, False otherwise. + Returns + ------- + bool + True if running inside PyCharm, False otherwise. """ return os.environ.get('PYCHARM_HOSTED') == '1' def in_colab() -> bool: - """Determines if the current environment is Google Colab. + """ + Determines if the current environment is Google Colab. + - Returns: bool: True if running in Google Colab, False otherwise. + Returns + ------- + bool + True if running in Google Colab, False otherwise. """ try: return find_spec('google.colab') is not None @@ -36,10 +46,15 @@ def in_colab() -> bool: def in_jupyter() -> bool: - """Return True when running inside a Jupyter Notebook. + """ + Return True when running inside a Jupyter Notebook. + - Returns: bool: True if inside a Jupyter Notebook, False - otherwise. + Returns + ------- + bool + True if inside a Jupyter Notebook, False + otherwise. """ try: import IPython # type: ignore[import-not-found] @@ -74,10 +89,15 @@ def in_jupyter() -> bool: def in_github_ci() -> bool: - """Return True when running under GitHub Actions CI. + """ + Return True when running under GitHub Actions CI. + - Returns: bool: True if env var ``GITHUB_ACTIONS`` is set, False - otherwise. + Returns + ------- + bool + True if env var ``GITHUB_ACTIONS`` is set, False + otherwise. """ return os.environ.get('GITHUB_ACTIONS') is not None diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index d368884a..a222c3eb 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -94,10 +94,15 @@ class ConsoleManager: @staticmethod def _detect_width() -> int: - """Detect a suitable console width for the shared Console. + """ + Detect a suitable console width for the shared Console. + - Returns: The detected terminal width, clamped at - ``_MIN_CONSOLE_WIDTH`` to avoid cramped layouts. + Returns + ------- + + The detected terminal width, clamped at + ``_MIN_CONSOLE_WIDTH`` to avoid cramped layouts. """ min_width = ConsoleManager._MIN_CONSOLE_WIDTH try: @@ -200,9 +205,14 @@ class ExceptionHookManager: @staticmethod def install_verbose_hook(logger: logging.Logger) -> None: - """Install a verbose exception hook that prints rich tracebacks. + """ + Install a verbose exception hook that prints rich tracebacks. - Args: logger: Logger used to emit the exception information. + + Parameters + ---------- + logger + Logger used to emit the exception information. """ if not hasattr(Logger, '_orig_excepthook'): Logger._orig_excepthook = sys.excepthook # type: ignore[attr-defined] @@ -228,9 +238,14 @@ def aligned_excepthook( @staticmethod def install_compact_hook(logger: logging.Logger) -> None: - """Install a compact exception hook that logs message-only. + """ + Install a compact exception hook that logs message-only. + - Args: logger: Logger used to emit the error message. + Parameters + ---------- + logger + Logger used to emit the error message. """ if not hasattr(Logger, '_orig_excepthook'): Logger._orig_excepthook = sys.excepthook # type: ignore[attr-defined] @@ -254,14 +269,22 @@ def restore_original_hook(): # Jupyter-specific traceback suppression (inlined here) @staticmethod def _suppress_traceback(logger): - """Build a Jupyter custom exception callback that logs only the + """ + Build a Jupyter custom exception callback that logs only the message. - Args: logger: Logger used to emit error messages. - Returns: A callable suitable for IPython's set_custom_exc - that suppresses full tracebacks and logs only the exception - message. + Parameters + ---------- + logger + Logger used to emit error messages. + + Returns + ------- + + A callable suitable for IPython's set_custom_exc + that suppresses full tracebacks and logs only the exception + message. """ def suppress_jupyter_traceback(*args, **kwargs): @@ -278,10 +301,15 @@ def suppress_jupyter_traceback(*args, **kwargs): @staticmethod def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: - """Install a Jupyter/IPython custom exception handler that + """ + Install a Jupyter/IPython custom exception handler that suppresses tracebacks. - Args: logger: Logger used to emit error messages. + + Parameters + ---------- + logger + Logger used to emit error messages. """ try: from IPython import get_ipython diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index b835201e..fa2b0d59 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -30,11 +30,19 @@ def _validate_url(url: str) -> None: - """Validate that a URL uses only safe HTTP/HTTPS schemes. + """ + Validate that a URL uses only safe HTTP/HTTPS schemes. + - Args: url: The URL to validate. + Parameters + ---------- + url + The URL to validate. - Raises: ValueError: If the URL scheme is not HTTP or HTTPS. + Raises + ------ + ValueError + If the URL scheme is not HTTP or HTTPS. """ parsed = urlparse(url) if parsed.scheme not in ('http', 'https'): @@ -90,7 +98,8 @@ def _fetch_data_index() -> dict: @functools.lru_cache(maxsize=1) def _fetch_tutorials_index() -> dict: - """Fetch & cache the tutorials index.json from gh-pages and return + """ + Fetch & cache the tutorials index.json from gh-pages and return it as dict. The index is fetched from: @@ -99,9 +108,12 @@ def _fetch_tutorials_index() -> dict: For released versions, {version} is the public version string (e.g., '0.8.0.post1'). For development versions, 'dev' is used. - Returns: - dict: The tutorials index as a dictionary, or empty dict if - fetch fails. + + Returns + ------- + dict + The tutorials index as a dictionary, or empty dict if + fetch fails. """ version = _get_version_for_url() index_url = f'https://easyscience.github.io/diffraction-lib/{version}/tutorials/index.json' @@ -188,13 +200,21 @@ def download_data( def package_version(package_name: str) -> str | None: - """Get the installed version string of the specified package. + """ + Get the installed version string of the specified package. - Args: package_name (str): The name of the package to query. - Returns: str | None: The raw version string (may include local - part, e.g., '1.2.3+abc123'), or None if the package is not - installed. + Parameters + ---------- + package_name : str + The name of the package to query. + + Returns + ------- + str | None + The raw version string (may include local + part, e.g., '1.2.3+abc123'), or None if the package is not + installed. """ try: return version(package_name) @@ -203,16 +223,24 @@ def package_version(package_name: str) -> str | None: def stripped_package_version(package_name: str) -> str | None: - """Get the installed version of the specified package, stripped of + """ + Get the installed version of the specified package, stripped of any local version part. Returns only the public version segment (e.g., '1.2.3' or '1.2.3.post4'), omitting any local segment (e.g., '+d136'). - Args: package_name (str): The name of the package to query. - Returns: str | None: The public version string, or None if the - package is not installed. + Parameters + ---------- + package_name : str + The name of the package to query. + + Returns + ------- + str | None + The public version string, or None if the + package is not installed. """ v_str = package_version(package_name) if v_str is None: @@ -225,17 +253,25 @@ def stripped_package_version(package_name: str) -> str | None: def _is_dev_version(package_name: str) -> bool: - """Check if the installed package version is a development/local + """ + Check if the installed package version is a development/local version. A version is considered "dev" if: - The raw version contains '+dev', '+dirty', or '+devdirty' (local suffixes from versioningit) - The public version is '999.0.0' (versioningit default-tag fallback) - Args: package_name (str): The name of the package to query. - Returns: bool: True if the version is a development version, - False otherwise. + Parameters + ---------- + package_name : str + The name of the package to query. + + Returns + ------- + bool + True if the version is a development version, + False otherwise. """ raw_version = package_version(package_name) if raw_version is None: @@ -251,16 +287,24 @@ def _is_dev_version(package_name: str) -> bool: def _get_version_for_url(package_name: str = 'easydiffraction') -> str: - """Get the version string to use in URLs for fetching remote + """ + Get the version string to use in URLs for fetching remote resources. Returns the public version for released versions, or 'dev' for development/local versions. - Args: package_name (str): The name of the package to query. - Returns: str: The version string to use in URLs ('dev' or a - version like '0.8.0.post1'). + Parameters + ---------- + package_name : str + The name of the package to query. + + Returns + ------- + str + The version string to use in URLs ('dev' or a + version like '0.8.0.post1'). """ if _is_dev_version(package_name): return 'dev' @@ -459,10 +503,15 @@ def render_table( def render_cif(cif_text) -> None: - """Display the CIF text as a formatted table in Jupyter Notebook or + """ + Display the CIF text as a formatted table in Jupyter Notebook or terminal. - Args: cif_text: The CIF text to display. + + Parameters + ---------- + cif_text + The CIF text to display. """ # Split into lines lines: List[str] = [line for line in cif_text.splitlines()] @@ -561,12 +610,17 @@ def tof_to_d( def twotheta_to_d(twotheta, wavelength): - """Convert 2-theta to d-spacing using Bragg's law. + """ + Convert 2-theta to d-spacing using Bragg's law. Parameters: twotheta (float or np.ndarray): 2-theta angle in degrees. wavelength (float): Wavelength in Å. - Returns: d (float or np.ndarray): d-spacing in Å. + + Returns + ------- + d (float or np.ndarray) + d-spacing in Å. """ # Convert twotheta from degrees to radians theta_rad = np.radians(twotheta / 2) @@ -578,12 +632,17 @@ def twotheta_to_d(twotheta, wavelength): def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda): - """Convert sin(theta)/lambda to d-spacing. + """ + Convert sin(theta)/lambda to d-spacing. Parameters: sin_theta_over_lambda (float or np.ndarray): sin(theta)/lambda in 1/Å. - Returns: d (float or np.ndarray): d-spacing in Å. + + Returns + ------- + d (float or np.ndarray) + d-spacing in Å. """ # Avoid division by zero with np.errstate(divide='ignore', invalid='ignore'): @@ -594,15 +653,23 @@ def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda): def get_value_from_xye_header(file_path, key): - """Extracts a floating point value from the first line of the file, + """ + Extracts a floating point value from the first line of the file, corresponding to the given key. Parameters: file_path (str): Path to the input file. key (str): The key to extract ('DIFC' or 'two_theta'). - Returns: float: The extracted value. - Raises: ValueError: If the key is not found. + Returns + ------- + float + The extracted value. + + Raises + ------ + ValueError + If the key is not found. """ pattern = rf'{key}\s*=\s*([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)' diff --git a/tools/convert_google_docstrings_to_numpy.py b/tools/convert_google_docstrings_to_numpy.py index 01ebaad0..b56844b2 100644 --- a/tools/convert_google_docstrings_to_numpy.py +++ b/tools/convert_google_docstrings_to_numpy.py @@ -32,6 +32,7 @@ + r'):\s*(?P\S.*)?$' ) SECTION_KINDS_WITH_ITEMS = {'Args', 'Arguments', 'Attributes'} +RST_ROLE_RE = re.compile(r':[A-Za-z_][A-Za-z0-9_]*:`') def _iter_python_files(paths: list[Path]) -> list[Path]: @@ -142,6 +143,47 @@ def _looks_google(docstring: str) -> bool: return bool(GOOGLE_SECTION_RE.search(docstring)) +def _meta_kinds(parsed) -> set[str]: + kinds: set[str] = set() + for meta in parsed.meta: + args = getattr(meta, 'args', None) or [] + if not args: + continue + kinds.add(str(args[0]).lower()) + return kinds + + +def _contains_unparsed_sections(parsed) -> bool: + for text in (parsed.short_description, parsed.long_description): + if text and GOOGLE_SECTION_RE.search(text): + return True + return False + + +def _is_safe_conversion(docstring: str, parsed) -> bool: + if RST_ROLE_RE.search(docstring) or '::' in docstring: + return False + + kinds = _meta_kinds(parsed) + if _contains_unparsed_sections(parsed): + return False + + expectations = { + 'Args': 'param', + 'Arguments': 'param', + 'Attributes': 'attribute', + 'Returns': 'returns', + 'Raises': 'raises', + 'Yields': 'yields', + 'Examples': 'examples', + } + for section, expected_kind in expectations.items(): + if section in docstring and expected_kind not in kinds: + return False + + return True + + def _convert_docstring(docstring: str, names: list[str]) -> str | None: if not _looks_google(docstring): return None @@ -153,10 +195,20 @@ def _convert_docstring(docstring: str, names: list[str]) -> str | None: except Exception: return None + if not _is_safe_conversion(repaired, parsed): + return None + converted = compose(parsed, style=DocstringStyle.NUMPYDOC) return converted if converted != docstring else None +def _format_multiline_docstring(content: str, indent: int) -> str: + indent_str = ' ' * indent + lines = content.strip('\n').splitlines() + body = '\n'.join(f'{indent_str}{line}' if line else '' for line in lines) + return f'\n{body}\n{indent_str}' + + def _convert_file(path: Path) -> bool: source_code = path.read_text() tree = ast.parse(source_code, type_comments=True) @@ -191,7 +243,9 @@ def _convert_file(path: Path) -> bool: start = calc_abs_pos(source_code, line_starts, value.lineno, value.col_offset) end = calc_abs_pos(source_code, line_starts, end_lineno, end_col_offset) original_literal = source_code[start:end] - new_literal = rebuild_literal(original_literal, converted) + leading_indent = getattr(value, 'col_offset', 0) + formatted = _format_multiline_docstring(converted, leading_indent) + new_literal = rebuild_literal(original_literal, formatted) if new_literal is None or new_literal == original_literal: continue @@ -205,6 +259,7 @@ def _convert_file(path: Path) -> bool: for start, end, replacement in replacements: new_source = new_source[:start] + replacement + new_source[end:] + compile(new_source, str(path), 'exec') path.write_text(new_source) return True From f3687759ff447264a0b9433006c4fe049297a158 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 13:08:26 +0100 Subject: [PATCH 23/48] Remove docstring-format-check --- .github/workflows/lint-format.yml | 8 -------- .pre-commit-config.yaml | 7 ------- CONTRIBUTING.md | 1 - 3 files changed, 16 deletions(-) diff --git a/.github/workflows/lint-format.yml b/.github/workflows/lint-format.yml index 457d67f4..f1135fa5 100644 --- a/.github/workflows/lint-format.yml +++ b/.github/workflows/lint-format.yml @@ -79,12 +79,6 @@ jobs: shell: bash run: pixi run docstring-lint-check - - name: Check formatting of docstrings in Python code - id: docstring_format - continue-on-error: true - shell: bash - run: pixi run docstring-format-check - - name: Check formatting of non-Python files (md, toml, etc.) id: nonpy_format continue-on-error: true @@ -112,7 +106,6 @@ jobs: echo "| py lint | ${{ steps.py_lint.outcome == 'success' && '✅' || '❌' }} |" echo "| py format | ${{ steps.py_format.outcome == 'success' && '✅' || '❌' }} |" echo "| docstring lint | ${{ steps.docstring_lint.outcome == 'success' && '✅' || '❌' }} |" - echo "| docstring format | ${{ steps.docstring_format.outcome == 'success' && '✅' || '❌' }} |" echo "| nonpy format | ${{ steps.nonpy_format.outcome == 'success' && '✅' || '❌' }} |" echo "| notebooks lint | ${{ steps.notebook_lint.outcome == 'success' && '✅' || '❌' }} |" } >> "$GITHUB_STEP_SUMMARY" @@ -125,7 +118,6 @@ jobs: || steps.py_lint.outcome == 'failure' || steps.py_format.outcome == 'failure' || steps.docstring_lint.outcome == 'failure' - || steps.docstring_format.outcome == 'failure' || steps.nonpy_format.outcome == 'failure' || steps.notebook_lint.outcome == 'failure' shell: bash diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5fa0c2cb..9a3855f4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,13 +39,6 @@ repos: pass_filenames: false stages: [manual] - - id: pixi-docstring-format-check - name: pixi run docstring-format-check - entry: pixi run docstring-format-check - language: system - pass_filenames: false - stages: [manual] - - id: pixi-nonpy-format-check name: pixi run nonpy-format-check entry: pixi run nonpy-format-check diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3a4a55f..e8dc420f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -230,7 +230,6 @@ pixi run license-check.........................Passed pixi run py-lint-check.........................Passed pixi run py-format-check.......................Passed pixi run docstring-lint-check..................Passed -pixi run docstring-format-check................Passed pixi run nonpy-format-check....................Passed pixi run notebook-lint-check...................Passed pixi run unit-tests............................Passed From 8f5c149fe63e6a88946cab5f05a1515a9e2f5ce5 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 13:10:41 +0100 Subject: [PATCH 24/48] Convert docstrings from google to numpy style: Step 2 --- src/easydiffraction/analysis/analysis.py | 21 +- .../analysis/calculators/crysfml.py | 63 ++++-- .../analysis/calculators/cryspy.py | 65 ++++-- .../analysis/categories/aliases/default.py | 10 +- .../joint_fit_experiments/default.py | 9 +- .../analysis/fit_helpers/metrics.py | 78 ++++++-- .../analysis/fit_helpers/reporting.py | 61 ++++-- .../analysis/fit_helpers/tracking.py | 26 ++- src/easydiffraction/analysis/fitting.py | 76 ++++--- .../analysis/minimizers/base.py | 33 ++- .../analysis/minimizers/lmfit.py | 19 +- src/easydiffraction/core/collection.py | 9 +- src/easydiffraction/core/datablock.py | 9 +- src/easydiffraction/core/factory.py | 76 +++++-- src/easydiffraction/core/metadata.py | 24 ++- src/easydiffraction/core/variable.py | 12 +- .../crystallography/crystallography.py | 38 +++- .../datablocks/experiment/collection.py | 45 +++-- .../datablocks/experiment/item/base.py | 28 ++- .../datablocks/experiment/item/factory.py | 51 +++-- .../categories/atom_sites/default.py | 9 +- .../structure/categories/cell/default.py | 9 +- src/easydiffraction/display/base.py | 19 +- src/easydiffraction/display/plotters/ascii.py | 60 ++++-- src/easydiffraction/display/plotters/base.py | 44 ++-- .../display/plotters/plotly.py | 128 ++++++++---- src/easydiffraction/display/plotting.py | 188 ++++++++++++------ src/easydiffraction/display/tablers/base.py | 36 +++- src/easydiffraction/display/tablers/pandas.py | 85 +++++--- src/easydiffraction/display/tablers/rich.py | 70 +++++-- src/easydiffraction/display/tables.py | 22 +- src/easydiffraction/utils/logging.py | 33 ++- src/easydiffraction/utils/utils.py | 125 ++++++++---- tools/convert_google_docstrings_to_numpy.py | 179 ++++++++++++----- 34 files changed, 1222 insertions(+), 538 deletions(-) diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 232d8285..5be7a6fc 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -27,7 +27,8 @@ class Analysis: - """High-level orchestration of analysis tasks for a Project. + """ + High-level orchestration of analysis tasks for a Project. This class wires calculators and minimizers, exposes a compact interface for parameters, constraints and results, and coordinates @@ -39,11 +40,10 @@ class Analysis: calculator/minimizer implementation. - Calculate patterns and run single or joint fits. - Attributes: project: The parent Project object. aliases: A - registry of human-friendly aliases for parameters. constraints: - Symbolic constraints between parameters. calculator: Active - calculator used for computations. fitter: Active - fitter/minimizer driver. + Attributes + ---------- + project + The parent Project object. aliases: A registry of human-friendly aliases for parameters. constraints: Symbolic constraints between parameters. calculator: Active calculator used for computations. fitter: Active fitter/minimizer driver. """ def __init__(self, project) -> None: @@ -708,13 +708,16 @@ def show_fit_results(self) -> None: self.fitter._process_fit_results(structures, experiments) def _update_categories(self, called_by_minimizer=False) -> None: - """Update all categories owned by Analysis. + """ + Update all categories owned by Analysis. This ensures aliases and constraints are up-to-date before serialization or after parameter changes. - Args: called_by_minimizer: Whether this is called during - fitting. + Parameters + ---------- + called_by_minimizer + Whether this is called during fitting. """ # Apply constraints to sync dependent parameters if self.constraints._items: diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index f90675eb..40de8679 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -48,11 +48,15 @@ def calculate_structure_factors( structures: Structures, experiments: Experiments, ) -> None: - """Call Crysfml to calculate structure factors. + """ + Call Crysfml to calculate structure factors. - Args: structures: The structures to calculate structure - factors for. experiments: The experiments associated with - the sample models. + Parameters + ---------- + structures + The structures to calculate structure factors for. + experiments + The experiments associated with the sample models. """ raise NotImplementedError('HKL calculation is not implemented for CrysfmlCalculator.') @@ -62,16 +66,22 @@ def calculate_pattern( experiment: ExperimentBase, called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: - """Calculates the diffraction pattern using Crysfml for the + """ + Calculates the diffraction pattern using Crysfml for the given structure and experiment. - Args: structure: The structure to calculate the pattern for. - experiment: The experiment associated with the structure. - called_by_minimizer: Whether the calculation is called by a - minimizer. + Parameters + ---------- + structure + The structure to calculate the pattern for. + experiment + The experiment associated with the structure. + called_by_minimizer + Whether the calculation is called by a minimizer. - Returns: The calculated diffraction pattern as a NumPy array - or a list of floats. + Returns + ------- + The calculated diffraction pattern as a NumPy array or a list of floats. """ # Intentionally unused, required by public API/signature del called_by_minimizer @@ -90,12 +100,19 @@ def _adjust_pattern_length( pattern: List[float], target_length: int, ) -> List[float]: - """Adjusts the length of the pattern to match the target length. + """ + Adjusts the length of the pattern to match the target length. - Args: pattern: The pattern to adjust. target_length: The - desired length of the pattern. + Parameters + ---------- + pattern + The pattern to adjust. + target_length + The desired length of the pattern. - Returns: The adjusted pattern. + Returns + ------- + The adjusted pattern. """ # TODO: Check the origin of this discrepancy coming from # PyCrysFML @@ -108,14 +125,20 @@ def _crysfml_dict( structure: Structures, experiment: ExperimentBase, ) -> Dict[str, Union[ExperimentBase, Structure]]: - """Converts the structure and experiment into a dictionary + """ + Converts the structure and experiment into a dictionary format for Crysfml. - Args: structure: The structure to convert. experiment: - The experiment to convert. + Parameters + ---------- + structure + The structure to convert. + experiment + The experiment to convert. - Returns: A dictionary representation of the structure and - experiment. + Returns + ------- + A dictionary representation of the structure and experiment. """ structure_dict = self._convert_structure_to_dict(structure) experiment_dict = self._convert_experiment_to_dict(experiment) diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index c32d683d..34f6aef5 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -61,13 +61,18 @@ def calculate_structure_factors( experiment: ExperimentBase, called_by_minimizer: bool = False, ): - """Raises a NotImplementedError as HKL calculation is not + """ + Raises a NotImplementedError as HKL calculation is not implemented. - Args: structure: The structure to calculate structure - factors for. experiment: The experiment associated with the - sample models. called_by_minimizer: Whether the - calculation is called by a minimizer. + Parameters + ---------- + structure + The structure to calculate structure factors for. + experiment + The experiment associated with the sample models. + called_by_minimizer + Whether the calculation is called by a minimizer. """ combined_name = f'{structure.name}_{experiment.name}' @@ -116,7 +121,8 @@ def calculate_pattern( experiment: ExperimentBase, called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: - """Calculates the diffraction pattern using Cryspy for the given + """ + Calculates the diffraction pattern using Cryspy for the given structure and experiment. We only recreate the cryspy_obj if this method is - NOT called @@ -124,13 +130,18 @@ def calculate_pattern( other cases, we are modifying the existing cryspy_dict This allows significantly speeding up the calculation - Args: structure: The structure to calculate the pattern for. - experiment: The experiment associated with the structure. - called_by_minimizer: Whether the calculation is called by a - minimizer. + Parameters + ---------- + structure + The structure to calculate the pattern for. + experiment + The experiment associated with the structure. + called_by_minimizer + Whether the calculation is called by a minimizer. - Returns: The calculated diffraction pattern as a NumPy array - or a list of floats. + Returns + ------- + The calculated diffraction pattern as a NumPy array or a list of floats. """ combined_name = f'{structure.name}_{experiment.name}' @@ -188,13 +199,20 @@ def _recreate_cryspy_dict( structure: Structure, experiment: ExperimentBase, ) -> Dict[str, Any]: - """Recreates the Cryspy dictionary for the given structure and + """ + Recreates the Cryspy dictionary for the given structure and experiment. - Args: structure: The structure to update. experiment: - The experiment to update. + Parameters + ---------- + structure + The structure to update. + experiment + The experiment to update. - Returns: The updated Cryspy dictionary. + Returns + ------- + The updated Cryspy dictionary. """ combined_name = f'{structure.name}_{experiment.name}' cryspy_dict = copy.deepcopy(self._cryspy_dicts[combined_name]) @@ -300,13 +318,20 @@ def _recreate_cryspy_obj( structure: Structure, experiment: ExperimentBase, ) -> Any: - """Recreates the Cryspy object for the given structure and + """ + Recreates the Cryspy object for the given structure and experiment. - Args: structure: The structure to recreate. experiment: - The experiment to recreate. + Parameters + ---------- + structure + The structure to recreate. + experiment + The experiment to recreate. - Returns: The recreated Cryspy object. + Returns + ------- + The recreated Cryspy object. """ cryspy_obj = str_to_globaln('') diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index c106c59d..55894f80 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -19,14 +19,16 @@ class Alias(CategoryItem): - """Single alias entry. + """ + Single alias entry. Maps a human-readable ``label`` to a concrete ``param_uid`` used by the engine. - Args: label: Alias label. Must match ``^[A-Za- - z_][A-Za-z0-9_]*$``. param_uid: Target parameter uid. Same - identifier pattern as ``label``. + Parameters + ---------- + label + Alias label. Must match ``^[A-Za- z_][A-Za-z0-9_]*$``. param_uid: Target parameter uid. Same identifier pattern as ``label``. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index 3530b1b9..dc079fe8 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -23,10 +23,13 @@ class JointFitExperiment(CategoryItem): - """A single joint-fit entry. + """ + A single joint-fit entry. - Args: id: Experiment identifier used in the fit session. weight: - Relative weight factor in the combined objective. + Parameters + ---------- + id + Experiment identifier used in the fit session. weight: Relative weight factor in the combined objective. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index 0925e7e0..4b4808ea 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -14,13 +14,20 @@ def calculate_r_factor( y_obs: np.ndarray, y_calc: np.ndarray, ) -> float: - """Calculate the R-factor (reliability factor) between observed and + """ + Calculate the R-factor (reliability factor) between observed and calculated data. - Args: y_obs: Observed data points. y_calc: Calculated data - points. + Parameters + ---------- + y_obs + Observed data points. + y_calc + Calculated data points. - Returns: R-factor value. + Returns + ------- + R-factor value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -34,13 +41,22 @@ def calculate_weighted_r_factor( y_calc: np.ndarray, weights: np.ndarray, ) -> float: - """Calculate the weighted R-factor between observed and calculated + """ + Calculate the weighted R-factor between observed and calculated data. - Args: y_obs: Observed data points. y_calc: Calculated data - points. weights: Weights for each data point. + Parameters + ---------- + y_obs + Observed data points. + y_calc + Calculated data points. + weights + Weights for each data point. - Returns: Weighted R-factor value. + Returns + ------- + Weighted R-factor value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -54,13 +70,20 @@ def calculate_rb_factor( y_obs: np.ndarray, y_calc: np.ndarray, ) -> float: - """Calculate the Bragg R-factor between observed and calculated + """ + Calculate the Bragg R-factor between observed and calculated data. - Args: y_obs: Observed data points. y_calc: Calculated data - points. + Parameters + ---------- + y_obs + Observed data points. + y_calc + Calculated data points. - Returns: Bragg R-factor value. + Returns + ------- + Bragg R-factor value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -73,13 +96,20 @@ def calculate_r_factor_squared( y_obs: np.ndarray, y_calc: np.ndarray, ) -> float: - """Calculate the R-factor squared between observed and calculated + """ + Calculate the R-factor squared between observed and calculated data. - Args: y_obs: Observed data points. y_calc: Calculated data - points. + Parameters + ---------- + y_obs + Observed data points. + y_calc + Calculated data points. - Returns: R-factor squared value. + Returns + ------- + R-factor squared value. """ y_obs = np.asarray(y_obs) y_calc = np.asarray(y_calc) @@ -122,14 +152,20 @@ def get_reliability_inputs( structures: Structures, experiments: Experiments, ) -> Tuple[np.ndarray, np.ndarray, Optional[np.ndarray]]: - """Collect observed and calculated data points for reliability + """ + Collect observed and calculated data points for reliability calculations. - Args: structures: Collection of structures. experiments: - Collection of experiments. + Parameters + ---------- + structures + Collection of structures. + experiments + Collection of experiments. - Returns: Tuple containing arrays of (observed values, calculated - values, error values) + Returns + ------- + Tuple containing arrays of (observed values, calculated values, error values) """ y_obs_all = [] y_calc_all = [] diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index b6f5efc5..378c4ebf 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -34,19 +34,31 @@ def __init__( fitting_time: Optional[float] = None, **kwargs: Any, ) -> None: - """Initialize FitResults with the given parameters. - - Args: success: Indicates if the fit was successful. - parameters: List of parameters used in the fit. chi_square: - Chi-square value of the fit. reduced_chi_square: Reduced - chi-square value of the fit. message: Message related to the - fit. iterations: Number of iterations performed. - engine_result: Result from the fitting engine. - starting_parameters: Initial parameters for the fit. - fitting_time: Time taken for the fitting process. **kwargs: - Additional engine-specific fields. If ``redchi`` is - provided and ``reduced_chi_square`` is not set, it is used as - the reduced chi-square value. + """ + Initialize FitResults with the given parameters. + + Parameters + ---------- + success + Indicates if the fit was successful. + parameters + List of parameters used in the fit. + chi_square + Chi-square value of the fit. + reduced_chi_square + Reduced chi-square value of the fit. + message + Message related to the fit. + iterations + Number of iterations performed. + engine_result + Result from the fitting engine. + starting_parameters + Initial parameters for the fit. + fitting_time + Time taken for the fitting process. + **kwargs + Additional engine-specific fields. If ``redchi`` is provided and ``reduced_chi_square`` is not set, it is used as the reduced chi-square value. """ self.success: bool = success self.parameters: List[Any] = parameters if parameters is not None else [] @@ -75,14 +87,21 @@ def display_results( f_obs: Optional[List[float]] = None, f_calc: Optional[List[float]] = None, ) -> None: - """Render a human-readable summary of the fit. - - Args: y_obs: Observed intensities for pattern R-factor - metrics. y_calc: Calculated intensities for pattern R-factor - metrics. y_err: Standard deviations of observed intensities - for wR. f_obs: Observed structure-factor magnitudes for - Bragg R. f_calc: Calculated structure-factor magnitudes for - Bragg R. + """ + Render a human-readable summary of the fit. + + Parameters + ---------- + y_obs + Observed intensities for pattern R-factor metrics. + y_calc + Calculated intensities for pattern R-factor metrics. + y_err + Standard deviations of observed intensities for wR. + f_obs + Observed structure-factor magnitudes for Bragg R. + f_calc + Calculated structure-factor magnitudes for Bragg R. """ status_icon = '✅' if self.success else '❌' rf = rf2 = wr = br = None diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index 2560d53d..629f159e 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -111,12 +111,19 @@ def track( residuals: np.ndarray, parameters: List[float], ) -> np.ndarray: - """Update progress with current residuals and parameters. - - Args: residuals: Residuals between measured and calculated - data. parameters: Current free parameters being fitted. + """ + Update progress with current residuals and parameters. - Returns: Residuals unchanged, for optimizer consumption. + Parameters + ---------- + residuals + Residuals between measured and calculated data. + parameters + Current free parameters being fitted. + + Returns + ------- + Residuals unchanged, for optimizer consumption. """ self._iteration += 1 @@ -197,10 +204,13 @@ def stop_timer(self) -> None: self._fitting_time = self._end_time - self._start_time def start_tracking(self, minimizer_name: str) -> None: - """Initialize display and headers and announce the minimizer. + """ + Initialize display and headers and announce the minimizer. - Args: minimizer_name: Name of the minimizer used for the - run. + Parameters + ---------- + minimizer_name + Name of the minimizer used for the run. """ console.print(f"🚀 Starting fit process with '{minimizer_name}'...") console.print('📈 Goodness-of-fit (reduced χ²) change:') diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index f2dbd2f0..c08d456d 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -35,16 +35,23 @@ def fit( weights: Optional[np.array] = None, analysis=None, ) -> None: - """Run the fitting process. + """ + Run the fitting process. This method performs the optimization but does not display results. Use :meth:`show_fit_results` on the Analysis object to display the fit results after fitting is complete. - Args: structures: Collection of structures. experiments: - Collection of experiments. weights: Optional weights for - joint fitting. analysis: Optional Analysis object to update - its categories during fitting. + Parameters + ---------- + structures + Collection of structures. + experiments + Collection of experiments. + weights + Optional weights for joint fitting. + analysis + Optional Analysis object to update its categories during fitting. """ params = structures.free_parameters + experiments.free_parameters @@ -73,15 +80,20 @@ def _process_fit_results( structures: Structures, experiments: Experiments, ) -> None: - """Collect reliability inputs and display fit results. + """ + Collect reliability inputs and display fit results. This method is typically called by :meth:`Analysis.show_fit_results` rather than directly. It calculates R-factors and other metrics, then renders them to the console. - Args: structures: Collection of structures. experiments: - Collection of experiments. + Parameters + ---------- + structures + Collection of structures. + experiments + Collection of experiments. """ y_obs, y_calc, y_err = get_reliability_inputs( structures, @@ -105,12 +117,19 @@ def _collect_free_parameters( structures: Structures, experiments: Experiments, ) -> List[Parameter]: - """Collect free parameters from structures and experiments. - - Args: structures: Collection of structures. experiments: - Collection of experiments. - - Returns: List of free parameters. + """ + Collect free parameters from structures and experiments. + + Parameters + ---------- + structures + Collection of structures. + experiments + Collection of experiments. + + Returns + ------- + List of free parameters. """ free_params: List[Parameter] = structures.free_parameters + experiments.free_parameters return free_params @@ -124,18 +143,29 @@ def _residual_function( weights: Optional[np.array] = None, analysis=None, ) -> np.ndarray: - """Residual function computes the difference between measured + """ + Residual function computes the difference between measured and calculated patterns. It updates the parameter values according to the optimizer-provided engine_params. - Args: engine_params: Engine-specific parameter dict. - parameters: List of parameters being optimized. structures: - Collection of structures. experiments: Collection of - experiments. weights: Optional weights for joint fitting. - analysis: Optional Analysis object to update its categories - during fitting. - - Returns: Array of weighted residuals. + Parameters + ---------- + engine_params + Engine-specific parameter dict. + parameters + List of parameters being optimized. + structures + Collection of structures. + experiments + Collection of experiments. + weights + Optional weights for joint fitting. + analysis + Optional Analysis object to update its categories during fitting. + + Returns + ------- + Array of weighted residuals. """ # Sync parameters back to objects self.minimizer._sync_result_to_parameters(parameters, engine_params) diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index 2560f0a0..d81b103e 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -104,12 +104,21 @@ def _finalize_fit( parameters: List[Any], raw_result: Any, ) -> FitResults: - """Build :class:`FitResults` and store it on ``self.result``. + """ + Build :class:`FitResults` and store it on ``self.result``. - Args: parameters: Parameters after the solver finished. - raw_result: Backend-specific solver output object. - Returns: FitResults: Aggregated outcome of the fit. + Parameters + ---------- + parameters + Parameters after the solver finished. + raw_result + Backend-specific solver output object. + + Returns + ------- + FitResults + Aggregated outcome of the fit. """ self._sync_result_to_parameters(parameters, raw_result) success = self._check_success(raw_result) @@ -133,13 +142,19 @@ def fit( parameters: List[Any], objective_function: Callable[..., Any], ) -> FitResults: - """Run the full minimization workflow. + """ + Run the full minimization workflow. - Args: parameters: Free parameters to optimize. - objective_function: Callable returning residuals for a given set - of engine arguments. + Parameters + ---------- + parameters + Free parameters to optimize. + objective_function + Callable returning residuals for a given set of engine arguments. - Returns: FitResults with success flag, best chi2 and timing. + Returns + ------- + FitResults with success flag, best chi2 and timing. """ minimizer_name = self.name or 'Unnamed Minimizer' if self.method is not None: diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index 7792b39a..aa420157 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -145,12 +145,21 @@ def _iteration_callback( *args: Any, **kwargs: Any, ) -> None: - """Callback function for each iteration of the minimizer. + """ + Callback function for each iteration of the minimizer. - Args: params: The current parameters. iter: The current - iteration number. resid: The residuals. *args: - Additional positional arguments. **kwargs: Additional - keyword arguments. + Parameters + ---------- + params + The current parameters. + iter + The current iteration number. + resid + The residuals. + *args + Additional positional arguments. + **kwargs + Additional keyword arguments. """ # Intentionally unused, required by callback signature del params, resid, args, kwargs diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index 1e3381ec..0cf2564c 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -13,10 +13,13 @@ class CollectionBase(GuardedBase): - """A minimal collection with stable iteration and name indexing. + """ + A minimal collection with stable iteration and name indexing. - Args: item_type: Type of items accepted by the collection. Used - for validation and tooling; not enforced at runtime here. + Parameters + ---------- + item_type + Type of items accepted by the collection. Used for validation and tooling; not enforced at runtime here. """ def __init__(self, item_type) -> None: diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 57e8fb9e..3321d0ac 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -133,10 +133,13 @@ def _key_for(self, item): return item._identity.datablock_entry_name def add(self, item) -> None: - """Add a pre-built item to the collection. + """ + Add a pre-built item to the collection. - Args: item: A ``DatablockItem`` instance (e.g. a - ``Structure`` or ``ExperimentBase`` subclass). + Parameters + ---------- + item + A ``DatablockItem`` instance (e.g. a ``Structure`` or ``ExperimentBase`` subclass). """ self[item._identity.datablock_entry_name] = item diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index 18508d2c..1fe0f0df 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -80,18 +80,26 @@ def supported_tags(cls) -> List[str]: @classmethod def default_tag(cls, **conditions) -> str: - """Resolve the default tag for a given experimental context. + """ + Resolve the default tag for a given experimental context. Uses *largest-subset matching*: the rule whose key is the biggest subset of the given conditions wins. A rule with an empty key (``frozenset()``) acts as a universal fallback. - Args: **conditions: Experimental-axis values, e.g. - ``scattering_type=ScatteringTypeEnum.BRAGG``. + Parameters + ---------- + **conditions + Experimental-axis values, e.g. ``scattering_type=ScatteringTypeEnum.BRAGG``. - Returns: The resolved default tag string. + Returns + ------- + The resolved default tag string. - Raises: ValueError: If no rule matches the given conditions. + Raises + ------ + ValueError + If no rule matches the given conditions. """ condition_set = frozenset(conditions.items()) best_match_tag: str | None = None @@ -115,12 +123,20 @@ def default_tag(cls, **conditions) -> str: @classmethod def create(cls, tag: str, **kwargs) -> Any: - """Instantiate a registered class by *tag*. - - Args: tag: ``type_info.tag`` value. **kwargs: Forwarded - to the class constructor. + """ + Instantiate a registered class by *tag*. - Raises: ValueError: If *tag* is not in the registry. + Parameters + ---------- + tag + ``type_info.tag`` value. + **kwargs + Forwarded to the class constructor. + + Raises + ------ + ValueError + If *tag* is not in the registry. """ supported = cls._supported_map() if tag not in supported: @@ -157,13 +173,21 @@ def supported_for( beam_mode=None, radiation_probe=None, ) -> List[Type]: - """Return classes matching conditions and/or calculator. + """ + Return classes matching conditions and/or calculator. - Args: calculator: Optional ``CalculatorEnum`` value. - sample_form: Optional ``SampleFormEnum`` value. scattering_type: - Optional ``ScatteringTypeEnum`` value. beam_mode: Optional - ``BeamModeEnum`` value. radiation_probe: Optional - ``RadiationProbeEnum`` value. + Parameters + ---------- + calculator + Optional ``CalculatorEnum`` value. + sample_form + Optional ``SampleFormEnum`` value. + scattering_type + Optional ``ScatteringTypeEnum`` value. + beam_mode + Optional ``BeamModeEnum`` value. + radiation_probe + Optional ``RadiationProbeEnum`` value. """ result = [] for klass in cls._supported_map().values(): @@ -195,13 +219,21 @@ def show_supported( beam_mode=None, radiation_probe=None, ) -> None: - """Pretty-print a table of supported types. + """ + Pretty-print a table of supported types. - Args: calculator: Optional ``CalculatorEnum`` filter. - sample_form: Optional ``SampleFormEnum`` filter. - scattering_type: Optional ``ScatteringTypeEnum`` filter. - beam_mode: Optional ``BeamModeEnum`` filter. radiation_probe: - Optional ``RadiationProbeEnum`` filter. + Parameters + ---------- + calculator + Optional ``CalculatorEnum`` filter. + sample_form + Optional ``SampleFormEnum`` filter. + scattering_type + Optional ``ScatteringTypeEnum`` filter. + beam_mode + Optional ``BeamModeEnum`` filter. + radiation_probe + Optional ``RadiationProbeEnum`` filter. """ matching = cls.supported_for( calculator=calculator, diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index f9a4d6cb..2dd7a654 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -17,15 +17,16 @@ @dataclass(frozen=True) class TypeInfo: - """Stable identity and human-readable description for a factory- + """ + Stable identity and human-readable description for a factory- created class. - Attributes: tag: Short, stable string identifier used for - serialization, user-facing selection, and factory lookup. - Must be unique within a factory's registry. Examples: - ``'line-segment'``, ``'pseudo-voigt'``, ``'cryspy'``. - description: One-line human-readable explanation. Used in - ``show_supported()`` tables and documentation. + Attributes + ---------- + tag + Short, stable string identifier used for serialization, user-facing selection, and factory lookup. Must be unique within a factory's registry. Examples: ``'line-segment'``, ``'pseudo-voigt'``, ``'cryspy'``. + description + One-line human-readable explanation. Used in ``show_supported()`` tables and documentation. """ tag: str @@ -80,10 +81,13 @@ def supports( @dataclass(frozen=True) class CalculatorSupport: - """Which calculation engines can handle this class. + """ + Which calculation engines can handle this class. - Attributes: calculators: Frozenset of ``CalculatorEnum`` values. - Empty means "any calculator" (no restriction). + Attributes + ---------- + calculators + Frozenset of ``CalculatorEnum`` values. Empty means "any calculator" (no restriction). """ calculators: FrozenSet = frozenset() diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 59da095b..04d4f413 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -27,7 +27,8 @@ class GenericDescriptorBase(GuardedBase): - """Base class for all parameter-like descriptors. + """ + Base class for all parameter-like descriptors. A descriptor encapsulates a typed value with validation, human- readable name/description and a globally unique identifier that is @@ -35,9 +36,12 @@ class GenericDescriptorBase(GuardedBase): expected data type and can extend the public API with additional behavior (e.g. units). - Attributes: name: Local parameter name (e.g. 'a', 'b_iso'). - description: Optional human-readable description. uid: Stable - random identifier for external references. + Attributes + ---------- + name + Local parameter name (e.g. 'a', 'b_iso'). + description + Optional human-readable description. uid: Stable random identifier for external references. """ _BOOL_SPEC_TEMPLATE = AttributeSpec( diff --git a/src/easydiffraction/crystallography/crystallography.py b/src/easydiffraction/crystallography/crystallography.py index 472c96ca..4a0e4c0b 100644 --- a/src/easydiffraction/crystallography/crystallography.py +++ b/src/easydiffraction/crystallography/crystallography.py @@ -21,13 +21,20 @@ def apply_cell_symmetry_constraints( cell: Dict[str, float], name_hm: str, ) -> Dict[str, float]: - """Apply symmetry constraints to unit cell parameters based on space + """ + Apply symmetry constraints to unit cell parameters based on space group. - Args: cell: Dictionary containing lattice parameters. name_hm: - Hermann-Mauguin symbol of the space group. + Parameters + ---------- + cell + Dictionary containing lattice parameters. + name_hm + Hermann-Mauguin symbol of the space group. - Returns: The cell dictionary with applied symmetry constraints. + Returns + ------- + The cell dictionary with applied symmetry constraints. """ it_number = get_it_number_by_name_hm_short(name_hm) if it_number is None: @@ -88,15 +95,24 @@ def apply_atom_site_symmetry_constraints( coord_code: int, wyckoff_letter: str, ) -> Dict[str, Any]: - """Apply symmetry constraints to atomic coordinates based on site + """ + Apply symmetry constraints to atomic coordinates based on site symmetry. - Args: atom_site: Dictionary containing atom position data. - name_hm: Hermann-Mauguin symbol of the space group. coord_code: - Coordinate system code. wyckoff_letter: Wyckoff position letter. - - Returns: The atom_site dictionary with applied symmetry - constraints. + Parameters + ---------- + atom_site + Dictionary containing atom position data. + name_hm + Hermann-Mauguin symbol of the space group. + coord_code + Coordinate system code. + wyckoff_letter + Wyckoff position letter. + + Returns + ------- + The atom_site dictionary with applied symmetry constraints. """ it_number = get_it_number_by_name_hm_short(name_hm) if it_number is None: diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index 1d9fbe67..f5a51edf 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -35,13 +35,21 @@ def create( radiation_probe: str | None = None, scattering_type: str | None = None, ) -> None: - """Add an experiment without associating a data file. + """ + Add an experiment without associating a data file. - Args: name: Experiment identifier. sample_form: Sample - form (e.g. ``'powder'``). beam_mode: Beam mode (e.g. - ``'constant wavelength'``). radiation_probe: Radiation probe - (e.g. ``'neutron'``). scattering_type: Scattering type (e.g. - ``'bragg'``). + Parameters + ---------- + name + Experiment identifier. + sample_form + Sample form (e.g. ``'powder'``). + beam_mode + Beam mode (e.g. ``'constant wavelength'``). + radiation_probe + Radiation probe (e.g. ``'neutron'``). + scattering_type + Scattering type (e.g. ``'bragg'``). """ experiment = ExperimentFactory.from_scratch( name=name, @@ -99,14 +107,23 @@ def add_from_data_path( radiation_probe: str | None = None, scattering_type: str | None = None, ) -> None: - """Add an experiment from a data file path. - - Args: name: Experiment identifier. data_path: Path to - the measured data file. sample_form: Sample form (e.g. - ``'powder'``). beam_mode: Beam mode (e.g. ``'constant - wavelength'``). radiation_probe: Radiation probe (e.g. - ``'neutron'``). scattering_type: Scattering type (e.g. - ``'bragg'``). + """ + Add an experiment from a data file path. + + Parameters + ---------- + name + Experiment identifier. + data_path + Path to the measured data file. + sample_form + Sample form (e.g. ``'powder'``). + beam_mode + Beam mode (e.g. ``'constant wavelength'``). + radiation_probe + Radiation probe (e.g. ``'neutron'``). + scattering_type + Scattering type (e.g. ``'bragg'``). """ experiment = ExperimentFactory.from_data_path( name=name, diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 4d06be50..e855d835 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -126,10 +126,13 @@ def calculator_type(self) -> str: @calculator_type.setter def calculator_type(self, tag: str) -> None: - """Switch to a different calculator backend. + """ + Switch to a different calculator backend. - Args: tag: Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``, - ``'pdffit'``). + Parameters + ---------- + tag + Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``, ``'pdffit'``). """ from easydiffraction.analysis.calculators.factory import CalculatorFactory @@ -238,10 +241,13 @@ def __init__( @abstractmethod def _load_ascii_data_to_experiment(self, data_path: str) -> None: - """Load single crystal data from an ASCII file. + """ + Load single crystal data from an ASCII file. - Args: data_path: Path to data file with columns compatible - with the beam mode. + Parameters + ---------- + data_path + Path to data file with columns compatible with the beam mode. """ pass @@ -513,11 +519,13 @@ def _get_valid_linked_phases( @abstractmethod def _load_ascii_data_to_experiment(self, data_path: str) -> None: - """Load powder diffraction data from an ASCII file. + """ + Load powder diffraction data from an ASCII file. - Args: data_path: Path to data file with columns compatible - with the beam mode (e.g. 2θ/I/σ for CWL, TOF/I/σ for - TOF). + Parameters + ---------- + data_path + Path to data file with columns compatible with the beam mode (e.g. 2θ/I/σ for CWL, TOF/I/σ for TOF). """ pass diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 4d127922..d7e90f9f 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -140,15 +140,25 @@ def from_scratch( radiation_probe: str | None = None, scattering_type: str | None = None, ) -> ExperimentBase: - """Create an experiment without measured data. + """ + Create an experiment without measured data. - Args: name: Experiment identifier. sample_form: Sample - form (e.g. ``'powder'``). beam_mode: Beam mode (e.g. - ``'constant wavelength'``). radiation_probe: Radiation probe - (e.g. ``'neutron'``). scattering_type: Scattering type (e.g. - ``'bragg'``). + Parameters + ---------- + name + Experiment identifier. + sample_form + Sample form (e.g. ``'powder'``). + beam_mode + Beam mode (e.g. ``'constant wavelength'``). + radiation_probe + Radiation probe (e.g. ``'neutron'``). + scattering_type + Scattering type (e.g. ``'bragg'``). - Returns: An experiment instance with only metadata. + Returns + ------- + An experiment instance with only metadata. """ expt_type = cls._create_experiment_type( sample_form=sample_form, @@ -222,16 +232,27 @@ def from_data_path( radiation_probe: str | None = None, scattering_type: str | None = None, ) -> ExperimentBase: - """Create an experiment from a raw data ASCII file. + """ + Create an experiment from a raw data ASCII file. - Args: name: Experiment identifier. data_path: Path to - the measured data file. sample_form: Sample form (e.g. - ``'powder'``). beam_mode: Beam mode (e.g. ``'constant - wavelength'``). radiation_probe: Radiation probe (e.g. - ``'neutron'``). scattering_type: Scattering type (e.g. - ``'bragg'``). + Parameters + ---------- + name + Experiment identifier. + data_path + Path to the measured data file. + sample_form + Sample form (e.g. ``'powder'``). + beam_mode + Beam mode (e.g. ``'constant wavelength'``). + radiation_probe + Radiation probe (e.g. ``'neutron'``). + scattering_type + Scattering type (e.g. ``'bragg'``). - Returns: An experiment instance with measured data attached. + Returns + ------- + An experiment instance with measured data attached. """ expt_obj = cls.from_scratch( name=name, diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index 7f569a0e..2ee7a41e 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -365,10 +365,13 @@ def _update( self, called_by_minimizer: bool = False, ) -> None: - """Recalculate atom sites after a change. + """ + Recalculate atom sites after a change. - Args: called_by_minimizer (bool): Whether the update was - triggered by the fitting minimizer. Currently unused. + Parameters + ---------- + called_by_minimizer : bool + Whether the update was triggered by the fitting minimizer. Currently unused. """ del called_by_minimizer diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 2d0f4faa..af5118b3 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -132,10 +132,13 @@ def _update( self, called_by_minimizer: bool = False, ) -> None: - """Recalculate cell parameters after a change. + """ + Recalculate cell parameters after a change. - Args: called_by_minimizer (bool, default=False): Whether the - update was triggered by the fitting minimizer. Currently unused. + Parameters + ---------- + called_by_minimizer : bool, default=False + Whether the update was triggered by the fitting minimizer. Currently unused. """ del called_by_minimizer # TODO: ??? diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index f862a9b4..5a6ac292 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -91,15 +91,22 @@ class RendererFactoryBase(ABC): @classmethod def create(cls, engine_name: str) -> Any: - """Create a backend instance for the given engine. + """ + Create a backend instance for the given engine. - Args: engine_name: Identifier of the engine to instantiate - as listed in ``_registry()``. + Parameters + ---------- + engine_name + Identifier of the engine to instantiate as listed in ``_registry()``. - Returns: A new backend instance corresponding to - ``engine_name``. + Returns + ------- + A new backend instance corresponding to ``engine_name``. - Raises: ValueError: If the engine name is not supported. + Raises + ------ + ValueError + If the engine name is not supported. """ registry = cls._registry() if engine_name not in registry: diff --git a/src/easydiffraction/display/plotters/ascii.py b/src/easydiffraction/display/plotters/ascii.py index 299359b2..268a84fc 100644 --- a/src/easydiffraction/display/plotters/ascii.py +++ b/src/easydiffraction/display/plotters/ascii.py @@ -26,14 +26,22 @@ class AsciiPlotter(PlotterBase): """Terminal-based plotter using ASCII art.""" def _get_legend_item(self, label): - """Return a colored legend entry for a given series label. + """ + Return a colored legend entry for a given series label. The legend uses a colored line matching the series color and the human-readable name from :data:`SERIES_CONFIG`. - Args: label: Series identifier (e.g., ``'meas'``). - Returns: A formatted legend string with color escapes. + Parameters + ---------- + label + Series identifier (e.g., ``'meas'``). + + Returns + ------- + + A formatted legend string with color escapes. """ color_start = DEFAULT_COLORS[label] color_end = asciichartpy.reset @@ -51,18 +59,27 @@ def plot_powder( title, height=None, ): - """Render a line plot for powder diffraction data. + """ + Render a line plot for powder diffraction data. Suitable for powder diffraction data where intensity is plotted against an x-axis variable (2θ, TOF, d-spacing). Uses ASCII characters for terminal display. - Args: x: 1D array-like of x values (only used for range - display). y_series: Sequence of y arrays to plot. labels: - Series identifiers corresponding to y_series. axes_labels: - Ignored; kept for API compatibility. title: Figure title - printed above the chart. height: Number of text rows to - allocate for the chart. + Parameters + ---------- + x + 1D array-like of x values (only used for range display). + y_series + Sequence of y arrays to plot. + labels + Series identifiers corresponding to y_series. + axes_labels + Ignored; kept for API compatibility. + title + Figure title printed above the chart. + height + Number of text rows to allocate for the chart. """ # Intentionally unused; kept for a consistent display API del axes_labels @@ -95,17 +112,26 @@ def plot_single_crystal( title, height=None, ): - """Render a scatter plot for single crystal diffraction data. + """ + Render a scatter plot for single crystal diffraction data. Creates an ASCII scatter plot showing measured vs calculated values with a diagonal reference line. - Args: x_calc: 1D array-like of calculated values (x-axis). - y_meas: 1D array-like of measured values (y-axis). y_meas_su: 1D - array-like of measurement uncertainties (ignored in ASCII mode). - axes_labels: Pair of strings for the x and y titles. title: - Figure title. height: Number of text rows for the chart - (default: 15). + Parameters + ---------- + x_calc + 1D array-like of calculated values (x-axis). + y_meas + 1D array-like of measured values (y-axis). + y_meas_su + 1D array-like of measurement uncertainties (ignored in ASCII mode). + axes_labels + Pair of strings for the x and y titles. + title + Figure title. + height + Number of text rows for the chart (default: 15). """ # Intentionally unused; ASCII scatter doesn't show error bars del y_meas_su diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index cde44c58..19116d8c 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -172,16 +172,26 @@ def plot_powder( title, height, ): - """Render a line plot for powder diffraction data. + """ + Render a line plot for powder diffraction data. Suitable for powder diffraction data where intensity is plotted against an x-axis variable (2θ, TOF, d-spacing). - Args: x: 1D array of x-axis values. y_series: Sequence - of y arrays to plot. labels: Identifiers corresponding to - y_series. axes_labels: Pair of strings for the x and y - titles. title: Figure title. height: Backend-specific - height (text rows or pixels). + Parameters + ---------- + x + 1D array of x-axis values. + y_series + Sequence of y arrays to plot. + labels + Identifiers corresponding to y_series. + axes_labels + Pair of strings for the x and y titles. + title + Figure title. + height + Backend-specific height (text rows or pixels). """ pass @@ -195,15 +205,25 @@ def plot_single_crystal( title, height, ): - """Render a scatter plot for single crystal diffraction data. + """ + Render a scatter plot for single crystal diffraction data. Suitable for single crystal diffraction data where measured values are plotted against calculated values with error bars. - Args: x_calc: 1D array of calculated values (x-axis). - y_meas: 1D array of measured values (y-axis). y_meas_su: 1D - array of measurement uncertainties. axes_labels: Pair of - strings for the x and y titles. title: Figure title. height: - Backend-specific height (text rows or pixels). + Parameters + ---------- + x_calc + 1D array of calculated values (x-axis). + y_meas + 1D array of measured values (y-axis). + y_meas_su + 1D array of measurement uncertainties. + axes_labels + Pair of strings for the x and y titles. + title + Figure title. + height + Backend-specific height (text rows or pixels). """ pass diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index 53c520ec..b042f2a0 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -42,14 +42,22 @@ def _get_powder_trace( y, label, ): - """Create a Plotly trace for powder diffraction data. + """ + Create a Plotly trace for powder diffraction data. - Args: x: 1D array-like of x-axis values. y: 1D array- - like of y-axis values. label: Series identifier (``'meas'``, - ``'calc'``, or ``'resid'``). + Parameters + ---------- + x + 1D array-like of x-axis values. + y + 1D array- like of y-axis values. + label + Series identifier (``'meas'``, ``'calc'``, or ``'resid'``). - Returns: A configured :class:`plotly.graph_objects.Scatter` - trace. + Returns + ------- + A configured + class:`plotly.graph_objects.Scatter` trace. """ mode = SERIES_CONFIG[label]['mode'] name = SERIES_CONFIG[label]['name'] @@ -72,14 +80,22 @@ def _get_single_crystal_trace( y_meas, y_meas_su, ): - """Create a Plotly trace for single crystal diffraction data. + """ + Create a Plotly trace for single crystal diffraction data. - Args: x_calc: 1D array-like of calculated values (x-axis). - y_meas: 1D array-like of measured values (y-axis). y_meas_su: 1D - array-like of measurement uncertainties. + Parameters + ---------- + x_calc + 1D array-like of calculated values (x-axis). + y_meas + 1D array-like of measured values (y-axis). + y_meas_su + 1D array-like of measurement uncertainties. - Returns: A configured :class:`plotly.graph_objects.Scatter` - trace with markers and error bars. + Returns + ------- + A configured + class:`plotly.graph_objects.Scatter` trace with markers and error bars. """ trace = go.Scatter( x=x_calc, @@ -152,12 +168,20 @@ def _get_figure( data, layout, ): - """Create and configure a Plotly figure. + """ + Create and configure a Plotly figure. - Args: data: List of traces to include in the figure. layout: - Layout configuration dict. + Parameters + ---------- + data + List of traces to include in the figure. + layout + Layout configuration dict. - Returns: A configured :class:`plotly.graph_objects.Figure`. + Returns + ------- + A configured + class:`plotly.graph_objects.Figure`. """ fig = go.Figure(data=data, layout=layout) # Format axis ticks: @@ -170,13 +194,16 @@ def _show_figure( self, fig, ): - """Display a Plotly figure. + """ + Display a Plotly figure. Renders the figure using the appropriate method for the current environment (browser for PyCharm, inline HTML for Jupyter). - Args: fig: A :class:`plotly.graph_objects.Figure` to - display. + Parameters + ---------- + fig + A :class:`plotly.graph_objects.Figure` to display. """ config = self._get_config() @@ -197,13 +224,22 @@ def _get_layout( axes_labels, **kwargs, ): - """Create a Plotly layout configuration. + """ + Create a Plotly layout configuration. - Args: title: Figure title. axes_labels: Pair of strings - for the x and y titles. **kwargs: Additional layout - parameters (e.g., shapes). + Parameters + ---------- + title + Figure title. + axes_labels + Pair of strings for the x and y titles. + **kwargs + Additional layout parameters (e.g., shapes). - Returns: A configured :class:`plotly.graph_objects.Layout`. + Returns + ------- + A configured + class:`plotly.graph_objects.Layout`. """ return go.Layout( margin=dict( @@ -245,16 +281,26 @@ def plot_powder( title, height=None, ): - """Render a line plot for powder diffraction data. + """ + Render a line plot for powder diffraction data. Suitable for powder diffraction data where intensity is plotted against an x-axis variable (2θ, TOF, d-spacing). - Args: x: 1D array-like of x-axis values. y_series: - Sequence of y arrays to plot. labels: Series identifiers - corresponding to y_series. axes_labels: Pair of strings for - the x and y titles. title: Figure title. height: - Ignored; Plotly auto-sizes based on renderer. + Parameters + ---------- + x + 1D array-like of x-axis values. + y_series + Sequence of y arrays to plot. + labels + Series identifiers corresponding to y_series. + axes_labels + Pair of strings for the x and y titles. + title + Figure title. + height + Ignored; Plotly auto-sizes based on renderer. """ # Intentionally unused; accepted for API compatibility del height @@ -282,17 +328,27 @@ def plot_single_crystal( title, height=None, ): - """Render a scatter plot for single crystal diffraction data. + """ + Render a scatter plot for single crystal diffraction data. Suitable for single crystal diffraction data where measured values are plotted against calculated values with error bars and a diagonal reference line. - Args: x_calc: 1D array-like of calculated values (x-axis). - y_meas: 1D array-like of measured values (y-axis). y_meas_su: 1D - array-like of measurement uncertainties. axes_labels: Pair of - strings for the x and y titles. title: Figure title. height: - Ignored; Plotly auto-sizes based on renderer. + Parameters + ---------- + x_calc + 1D array-like of calculated values (x-axis). + y_meas + 1D array-like of measured values (y-axis). + y_meas_su + 1D array-like of measurement uncertainties. + axes_labels + Pair of strings for the x and y titles. + title + Figure title. + height + Ignored; Plotly auto-sizes based on renderer. """ # Intentionally unused; accepted for API compatibility del height diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index a431fe7f..790dcc2a 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -81,15 +81,24 @@ def _default_engine(cls) -> str: # ------------------------------------------------------------------ def _auto_x_range_for_ascii(self, pattern, x_array, x_min, x_max): - """For the ASCII engine, narrow the range around the tallest + """ + For the ASCII engine, narrow the range around the tallest peak. - Args: pattern: Data pattern object (needs - ``intensity_meas``). x_array: Full x-axis array. x_min: - Current minimum (may be ``None``). x_max: Current maximum - (may be ``None``). - - Returns: Tuple of ``(x_min, x_max)``, possibly narrowed. + Parameters + ---------- + pattern + Data pattern object (needs ``intensity_meas``). + x_array + Full x-axis array. + x_min + Current minimum (may be ``None``). + x_max + Current maximum (may be ``None``). + + Returns + ------- + Tuple of ``(x_min, x_max)``, possibly narrowed. """ if self._engine == 'asciichartpy' and (x_min is None or x_max is None): max_intensity_pos = np.argmax(pattern.intensity_meas) @@ -107,15 +116,23 @@ def _filtered_y_array( x_min, x_max, ): - """Filter an array by the inclusive x-range limits. - - Args: y_array: 1D array-like of y values. x_array: 1D - array-like of x values (same length as ``y_array``). - x_min: Minimum x limit (or ``None`` to use default). x_max: - Maximum x limit (or ``None`` to use default). + """ + Filter an array by the inclusive x-range limits. - Returns: Filtered ``y_array`` values where ``x_array`` lies - within ``[x_min, x_max]``. + Parameters + ---------- + y_array + 1D array-like of y values. + x_array + 1D array-like of x values (same length as ``y_array``). + x_min + Minimum x limit (or ``None`` to use default). + x_max + Maximum x limit (or ``None`` to use default). + + Returns + ------- + Filtered ``y_array`` values where ``x_array`` lies within ``[x_min, x_max]``. """ if x_min is None: x_min = self.x_min @@ -145,20 +162,33 @@ def _prepare_powder_data( need_calc=False, show_residual=False, ): - """Validate, resolve axes, auto-range, and filter arrays. - - Args: pattern: Data pattern object with intensity arrays. - expt_name: Experiment name for error messages. expt_type: - Experiment type with sample_form, scattering, and beam - enums. x_min: Optional minimum x-axis limit. x_max: - Optional maximum x-axis limit. x: Explicit x-axis type or - ``None``. need_meas: Whether ``intensity_meas`` is required. - need_calc: Whether ``intensity_calc`` is required. - show_residual: If ``True``, compute meas − calc residual. - - Returns: A dict with keys ``x_filtered``, ``y_series``, - ``y_labels``, ``axes_labels``, and ``x_axis``; or ``None`` - when a required array is missing. + """ + Validate, resolve axes, auto-range, and filter arrays. + + Parameters + ---------- + pattern + Data pattern object with intensity arrays. + expt_name + Experiment name for error messages. + expt_type + Experiment type with sample_form, scattering, and beam enums. + x_min + Optional minimum x-axis limit. + x_max + Optional maximum x-axis limit. + x + Explicit x-axis type or ``None``. + need_meas + Whether ``intensity_meas`` is required. + need_calc + Whether ``intensity_calc`` is required. + show_residual + If ``True``, compute meas − calc residual. + + Returns + ------- + A dict with keys ``x_filtered``, ``y_series``, ``y_labels``, ``axes_labels``, and ``x_axis``; or ``None`` when a required array is missing. """ x_axis, x_name, sample_form, scattering_type, _ = self._resolve_x_axis(expt_type, x) @@ -214,14 +244,19 @@ def _prepare_powder_data( } def _resolve_x_axis(self, expt_type, x): - """Determine the x-axis type from experiment metadata. - - Args: expt_type: Experiment type with sample_form, - scattering_type, and beam_mode enums. x: Explicit x-axis - type or ``None`` to auto-detect. + """ + Determine the x-axis type from experiment metadata. - Returns: Tuple of ``(x_axis, x_name, sample_form, - scattering_type, beam_mode)``. + Parameters + ---------- + expt_type + Experiment type with sample_form, scattering_type, and beam_mode enums. + x + Explicit x-axis type or ``None`` to auto-detect. + + Returns + ------- + Tuple of ``(x_axis, x_name, sample_form, scattering_type, beam_mode)``. """ sample_form = expt_type.sample_form.value scattering_type = expt_type.scattering_type.value @@ -325,16 +360,23 @@ def plot_meas( x_max=None, x=None, ): - """Plot measured pattern using the current engine. - - Args: pattern: Object with x-axis arrays (``two_theta``, - ``time_of_flight``, ``d_spacing``) and ``meas`` array. - expt_name: Experiment name for the title. expt_type: - Experiment type with scattering/beam enums. x_min: Optional - minimum x-axis limit. x_max: Optional maximum x-axis limit. - x: X-axis type (``'two_theta'``, ``'time_of_flight'``, or - ``'d_spacing'``). If ``None``, auto-detected from beam - mode. + """ + Plot measured pattern using the current engine. + + Parameters + ---------- + pattern + Object with x-axis arrays (``two_theta``, ``time_of_flight``, ``d_spacing``) and ``meas`` array. + expt_name + Experiment name for the title. + expt_type + Experiment type with scattering/beam enums. + x_min + Optional minimum x-axis limit. + x_max + Optional maximum x-axis limit. + x + X-axis type (``'two_theta'``, ``'time_of_flight'``, or ``'d_spacing'``). If ``None``, auto-detected from beam mode. """ ctx = self._prepare_powder_data( pattern, @@ -366,16 +408,23 @@ def plot_calc( x_max=None, x=None, ): - """Plot calculated pattern using the current engine. - - Args: pattern: Object with x-axis arrays (``two_theta``, - ``time_of_flight``, ``d_spacing``) and ``calc`` array. - expt_name: Experiment name for the title. expt_type: - Experiment type with scattering/beam enums. x_min: Optional - minimum x-axis limit. x_max: Optional maximum x-axis limit. - x: X-axis type (``'two_theta'``, ``'time_of_flight'``, or - ``'d_spacing'``). If ``None``, auto-detected from beam - mode. + """ + Plot calculated pattern using the current engine. + + Parameters + ---------- + pattern + Object with x-axis arrays (``two_theta``, ``time_of_flight``, ``d_spacing``) and ``calc`` array. + expt_name + Experiment name for the title. + expt_type + Experiment type with scattering/beam enums. + x_min + Optional minimum x-axis limit. + x_max + Optional maximum x-axis limit. + x + X-axis type (``'two_theta'``, ``'time_of_flight'``, or ``'d_spacing'``). If ``None``, auto-detected from beam mode. """ ctx = self._prepare_powder_data( pattern, @@ -408,7 +457,8 @@ def plot_meas_vs_calc( show_residual=False, x=None, ): - """Plot measured and calculated series and optional residual. + """ + Plot measured and calculated series and optional residual. Supports both powder and single crystal data with a unified API. @@ -420,14 +470,22 @@ def plot_meas_vs_calc( (default): scatter plot - x='d_spacing' or 'sin_theta_over_lambda': line plot - Args: pattern: Data pattern object with meas/calc arrays. - expt_name: Experiment name for the title. expt_type: - Experiment type with sample_form, scattering, and beam - enums. x_min: Optional minimum x-axis limit. x_max: - Optional maximum x-axis limit. show_residual: If ``True``, - add residual series (powder only). x: X-axis type. - If ``None``, auto-detected from sample form and beam - mode. + Parameters + ---------- + pattern + Data pattern object with meas/calc arrays. + expt_name + Experiment name for the title. + expt_type + Experiment type with sample_form, scattering, and beam enums. + x_min + Optional minimum x-axis limit. + x_max + Optional maximum x-axis limit. + show_residual + If ``True``, add residual series (powder only). + x + X-axis type. If ``None``, auto-detected from sample form and beam mode. """ x_axis, _, sample_form, scattering_type, _ = self._resolve_x_axis(expt_type, x) diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index 3f969952..b5d73f85 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -68,12 +68,21 @@ def _is_dark_theme(self) -> bool: return is_dark() def _rich_to_hex(self, color): - """Convert a Rich color name to a CSS-style hex string. + """ + Convert a Rich color name to a CSS-style hex string. + - Args: color: Rich color name or specification parsable by - :mod:`rich`. + Parameters + ---------- + color + Rich color name or specification parsable by - Returns: Hex color string in the form ``#RRGGBB``. + mod:`rich`. + + Returns + ------- + + Hex color string in the form ``#RRGGBB``. """ c = Color.parse(color) rgb = c.get_truecolor() @@ -97,13 +106,20 @@ def render( df, display_handle: Any | None = None, ) -> Any: - """Render the provided DataFrame with backend-specific styling. + """ + Render the provided DataFrame with backend-specific styling. - Args: alignments: Iterable of column justifications (e.g., - ``'left'`` or ``'center'``) corresponding to the data columns. - df: Index-aware DataFrame with data to render. display_handle: - Optional environment-specific handle to enable in-place updates. + Parameters + ---------- + alignments + Iterable of column justifications (e.g., ``'left'`` or ``'center'``) corresponding to the data columns. + df + Index-aware DataFrame with data to render. + display_handle + Optional environment-specific handle to enable in-place updates. - Returns: Backend-defined return value (commonly ``None``). + Returns + ------- + Backend-defined return value (commonly ``None``). """ pass diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index 4f063fe4..dc65422c 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -22,12 +22,17 @@ class PandasTableBackend(TableBackendBase): """Render tables using the pandas Styler in Jupyter environments.""" def _build_base_styles(self, color: str) -> list[dict]: - """Return base CSS table styles for a given border color. + """ + Return base CSS table styles for a given border color. - Args: color: CSS color value (e.g., ``#RRGGBB``) to use for - borders and header accents. + Parameters + ---------- + color + CSS color value (e.g., ``#RRGGBB``) to use for borders and header accents. - Returns: A list of ``Styler.set_table_styles`` dictionaries. + Returns + ------- + A list of ``Styler.set_table_styles`` dictionaries. """ return [ # Margins and outer border on the entire table @@ -75,13 +80,19 @@ def _build_base_styles(self, color: str) -> list[dict]: ] def _build_header_alignment_styles(self, df, alignments) -> list[dict]: - """Generate header cell alignment styles per column. - - Args: df: DataFrame whose columns are being rendered. - alignments: Iterable of text alignment values (e.g., ``'left'``, - ``'center'``) matching ``df`` columns. - - Returns: A list of CSS rules for header cell alignment. + """ + Generate header cell alignment styles per column. + + Parameters + ---------- + df + DataFrame whose columns are being rendered. + alignments + Iterable of text alignment values (e.g., ``'left'``, ``'center'``) matching ``df`` columns. + + Returns + ------- + A list of CSS rules for header cell alignment. """ return [ { @@ -92,13 +103,21 @@ def _build_header_alignment_styles(self, df, alignments) -> list[dict]: ] def _apply_styling(self, df, alignments, color: str): - """Build a configured Styler with alignments and base styles. - - Args: df: DataFrame to style. alignments: Iterable of - text alignment values for columns. color: CSS color value - used for borders/header. - - Returns: A configured pandas Styler ready for display. + """ + Build a configured Styler with alignments and base styles. + + Parameters + ---------- + df + DataFrame to style. + alignments + Iterable of text alignment values for columns. + color + CSS color value used for borders/header. + + Returns + ------- + A configured pandas Styler ready for display. """ table_styles = self._build_base_styles(color) header_alignment_styles = self._build_header_alignment_styles(df, alignments) @@ -115,15 +134,19 @@ def _apply_styling(self, df, alignments, color: str): return styler def _update_display(self, styler, display_handle) -> None: - """Single, consistent update path for Jupyter. + """ + Single, consistent update path for Jupyter. If a handle with ``update()`` is provided and it's a DisplayHandle, update the output area in-place using HTML. Otherwise, display once via IPython ``display()``. - Args: styler: Configured DataFrame Styler to be rendered. - display_handle: Optional IPython DisplayHandle used for in-place - updates. + Parameters + ---------- + styler + Configured DataFrame Styler to be rendered. + display_handle + Optional IPython DisplayHandle used for in-place updates. """ # Handle with update() method if display_handle is not None and hasattr(display_handle, 'update'): @@ -149,13 +172,17 @@ def render( df, display_handle: Any | None = None, ) -> Any: - """Render a styled DataFrame. - - Args: alignments: Iterable of column justifications (e.g. - 'left'). df: DataFrame whose index is displayed as the first - column. display_handle: Optional IPython DisplayHandle to - update an existing output area in place when running in - Jupyter. + """ + Render a styled DataFrame. + + Parameters + ---------- + alignments + Iterable of column justifications (e.g. 'left'). + df + DataFrame whose index is displayed as the first column. + display_handle + Optional IPython DisplayHandle to update an existing output area in place when running in Jupyter. """ color = self._pandas_border_color styler = self._apply_styling(df, alignments, color) diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index d592ef82..7fa615ef 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -41,15 +41,23 @@ class RichTableBackend(TableBackendBase): """Render tables to terminal or Jupyter using the Rich library.""" def _to_html(self, table: Table) -> str: - """Render a Rich table to HTML using an off-screen console. + """ + Render a Rich table to HTML using an off-screen console. A fresh ``Console(record=True, file=StringIO())`` avoids private attribute access and guarantees no visible output in notebooks. - Args: table: Rich :class:`~rich.table.Table` to export. - Returns: HTML string with inline styles for notebook - display. + Parameters + ---------- + table + Rich :class:`~rich.table.Table` to export. + + Returns + ------- + + HTML string with inline styles for notebook + display. """ tmp = Console(force_jupyter=False, record=True, file=io.StringIO()) tmp.print(table) @@ -62,14 +70,24 @@ def _to_html(self, table: Table) -> str: return html def _build_table(self, df, alignments, color: str) -> Table: - """Construct a Rich Table with formatted data and alignment. - - Args: df: DataFrame-like object providing rows to render. - alignments: Iterable of text alignment values for columns. - color: Rich color name used for borders/index style. - - Returns: A :class:`~rich.table.Table` configured for - display. + """ + Construct a Rich Table with formatted data and alignment. + + + Parameters + ---------- + df + DataFrame-like object providing rows to render. + alignments + Iterable of text alignment values for columns. + color + Rich color name used for borders/index style. + + Returns + ------- + A + class:`~rich.table.Table` configured for + display. """ table = Table( title=None, @@ -94,7 +112,8 @@ def _build_table(self, df, alignments, color: str) -> Table: return table def _update_display(self, table: Table, display_handle) -> None: - """Single, consistent update path for Jupyter and terminal. + """ + Single, consistent update path for Jupyter and terminal. - With a handle that has ``update()``: * If it's an IPython DisplayHandle, export to HTML and update. * Otherwise, @@ -102,9 +121,12 @@ def _update_display(self, table: Table, display_handle) -> None: Rich renderable. - Without a handle, print once to the shared console. - Args: table: Rich :class:`~rich.table.Table` to display. - display_handle: Optional environment-specific handle for in- - place updates (IPython or terminal live). + Parameters + ---------- + table + Rich :class:`~rich.table.Table` to display. + display_handle + Optional environment-specific handle for in- place updates (IPython or terminal live). """ # Handle with update() method if display_handle is not None and hasattr(display_handle, 'update'): @@ -135,11 +157,17 @@ def render( df, display_handle=None, ) -> Any: - """Render a styled table using Rich. - - Args: alignments: Iterable of text-align values for columns. - df: Index-aware DataFrame to render. display_handle: - Optional environment handle for in-place updates. + """ + Render a styled table using Rich. + + Parameters + ---------- + alignments + Iterable of text-align values for columns. + df + Index-aware DataFrame to render. + display_handle + Optional environment handle for in-place updates. """ color = self._rich_border_color table = self._build_table(df, alignments, color) diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index bea9ed0a..e2a2084d 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -66,15 +66,19 @@ def show_config(self) -> None: TableRenderer.get().render(df) def render(self, df, display_handle: Any | None = None) -> Any: - """Render a DataFrame as a table using the active backend. - - Args: df: DataFrame with a two-level column index where the - second level provides per-column alignment. display_handle: - Optional environment-specific handle used to update an - existing output area in-place (e.g., an IPython - DisplayHandle or a terminal live handle). - - Returns: Backend-specific return value (usually ``None``). + """ + Render a DataFrame as a table using the active backend. + + Parameters + ---------- + df + DataFrame with a two-level column index where the second level provides per-column alignment. + display_handle + Optional environment-specific handle used to update an existing output area in-place (e.g., an IPython DisplayHandle or a terminal live handle). + + Returns + ------- + Backend-specific return value (usually ``None``). """ # Work on a copy to avoid mutating the original DataFrame df = df.copy() diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index a222c3eb..7aa7e975 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -138,12 +138,19 @@ def setup_handlers( rich_tracebacks: bool, mode: str = 'compact', ) -> None: - """Install Rich handler and optional Jupyter traceback support. + """ + Install Rich handler and optional Jupyter traceback support. - Args: logger: Logger instance to attach handlers to. level: - Minimum log level to emit. rich_tracebacks: Whether to - enable Rich tracebacks. mode: Output mode name ("compact" or - "verbose"). + Parameters + ---------- + logger + Logger instance to attach handlers to. + level + Minimum log level to emit. + rich_tracebacks + Whether to enable Rich tracebacks. + mode + Output mode name ("compact" or "verbose"). """ logger.handlers.clear() logger.propagate = False @@ -178,11 +185,19 @@ def configure( level: 'Logger.Level', rich_tracebacks: bool, ) -> None: - """Configure the logger with RichHandler and exception hooks. + """ + Configure the logger with RichHandler and exception hooks. - Args: logger: Logger instance to configure. mode: Output - mode (compact or verbose). level: Minimum log level to emit. - rich_tracebacks: Whether to enable Rich tracebacks. + Parameters + ---------- + logger + Logger instance to configure. + mode + Output mode (compact or verbose). + level + Minimum log level to emit. + rich_tracebacks + Whether to enable Rich tracebacks. """ LoggerConfig.setup_handlers( logger, diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index fa2b0d59..37fbf231 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -135,19 +135,31 @@ def download_data( destination: str = 'data', overwrite: bool = False, ) -> str: - """Download a dataset by numeric ID using the remote diffraction + """ + Download a dataset by numeric ID using the remote diffraction index. Example: path = download_data(id=12, destination="data") - Args: id: Numeric dataset id (e.g. 12). destination: - Directory to save the file into (created if missing). - overwrite: Whether to overwrite the file if it already exists. + Parameters + ---------- + id + Numeric dataset id (e.g. 12). + destination + Directory to save the file into (created if missing). + overwrite + Whether to overwrite the file if it already exists. - Returns: str: Full path to the downloaded file as string. + Returns + ------- + str + Full path to the downloaded file as string. - Raises: KeyError: If the id is not found in the index. - ValueError: If the resolved URL is not HTTP/HTTPS. + Raises + ------ + KeyError + If the id is not found in the index. ValueError + If the resolved URL is not HTTP/HTTPS. """ index = _fetch_data_index() key = str(id) @@ -329,14 +341,19 @@ def _safe_urlopen(request_or_url): # type: ignore[no-untyped-def] def _resolve_tutorial_url(url_template: str) -> str: - """Replace {version} placeholder in URL template with actual + """ + Replace {version} placeholder in URL template with actual version. - Args: url_template (str): URL template containing {version} - placeholder. + Parameters + ---------- + url_template : str + URL template containing {version} placeholder. - Returns: str: URL with {version} replaced by actual version - string. + Returns + ------- + str + URL with {version} replaced by actual version string. """ version = _get_version_for_url() return url_template.replace('{version}', version) @@ -380,18 +397,30 @@ def download_tutorial( destination: str = 'tutorials', overwrite: bool = False, ) -> str: - """Download a tutorial notebook by numeric ID. + """ + Download a tutorial notebook by numeric ID. Example: path = download_tutorial(id=1, destination="tutorials") - Args: id: Numeric tutorial id (e.g. 1). destination: - Directory to save the file into (created if missing). - overwrite: Whether to overwrite the file if it already exists. + Parameters + ---------- + id + Numeric tutorial id (e.g. 1). + destination + Directory to save the file into (created if missing). + overwrite + Whether to overwrite the file if it already exists. - Returns: str: Full path to the downloaded file as string. + Returns + ------- + str + Full path to the downloaded file as string. - Raises: KeyError: If the id is not found in the index. - ValueError: If the resolved URL is not HTTP/HTTPS. + Raises + ------ + KeyError + If the id is not found in the index. ValueError + If the resolved URL is not HTTP/HTTPS. """ index = _fetch_tutorials_index() key = str(id) @@ -442,15 +471,22 @@ def download_all_tutorials( destination: str = 'tutorials', overwrite: bool = False, ) -> list[str]: - """Download all available tutorial notebooks. + """ + Download all available tutorial notebooks. Example: paths = download_all_tutorials(destination="tutorials") - Args: destination: Directory to save the files into (created if - missing). overwrite: Whether to overwrite files if they already - exist. + Parameters + ---------- + destination + Directory to save the files into (created if missing). + overwrite + Whether to overwrite files if they already exist. - Returns: list[str]: List of full paths to the downloaded files. + Returns + ------- + list[str] + List of full paths to the downloaded files. """ index = _fetch_tutorials_index() if not index: @@ -534,7 +570,8 @@ def tof_to_d( quad: float, quad_eps=1e-20, ) -> np.ndarray: - """Convert time-of-flight (TOF) to d-spacing using a quadratic + """ + Convert time-of-flight (TOF) to d-spacing using a quadratic calibration. Model: TOF = offset + linear * d + quad * d² @@ -545,17 +582,28 @@ def tof_to_d( solution exists. - Expects ``tof`` as a NumPy array; output matches its shape. - Args: tof (np.ndarray): Time-of-flight values (µs). Must be a - NumPy array. offset (float): Calibration offset (µs). - linear (float): Linear calibration coefficient (µs/Å). quad - (float): Quadratic calibration coefficient (µs/Ų). quad_eps - (float, optional): Threshold to treat ``quad`` as zero. Defaults to - 1e-20. + Parameters + ---------- + tof : np.ndarray + Time-of-flight values (µs). Must be a NumPy array. + offset : float + Calibration offset (µs). + linear : float + Linear calibration coefficient (µs/Å). + quad : float + Quadratic calibration coefficient (µs/Ų). + quad_eps : float, optional + Threshold to treat ``quad`` as zero. Defaults to 1e-20. - Returns: np.ndarray: d-spacing values (Å), NaN where invalid. + Returns + ------- + np.ndarray + d-spacing values (Å), NaN where invalid. - Raises: TypeError: If ``tof`` is not a NumPy array or - coefficients are not real numbers. + Raises + ------ + TypeError + If ``tof`` is not a NumPy array or coefficients are not real numbers. """ # Type checks if not isinstance(tof, np.ndarray): @@ -684,7 +732,8 @@ def get_value_from_xye_header(file_path, key): def str_to_ufloat(s: Optional[str], default: Optional[float] = None) -> UFloat: - """Parse a CIF-style numeric string into a `ufloat` with an optional + """ + Parse a CIF-style numeric string into a `ufloat` with an optional uncertainty. Examples of supported input: - "3.566" → ufloat(3.566, nan) - @@ -703,9 +752,9 @@ def str_to_ufloat(s: Optional[str], default: Optional[float] = None) -> UFloat: optional Default value to use if `s` is None or parsing fails. Defaults to None. - Returns: ------- UFloat An `uncertainties.UFloat` object with - the parsed value and uncertainty. The uncertainty will be NaN if - not specified or parsing failed. + Returns + ------- + ------- UFloat An `uncertainties.UFloat` object with the parsed value and uncertainty. The uncertainty will be NaN if not specified or parsing failed. """ if s is None: return ufloat(default, np.nan) diff --git a/tools/convert_google_docstrings_to_numpy.py b/tools/convert_google_docstrings_to_numpy.py index b56844b2..fec28853 100644 --- a/tools/convert_google_docstrings_to_numpy.py +++ b/tools/convert_google_docstrings_to_numpy.py @@ -4,6 +4,7 @@ from __future__ import annotations import ast +import inspect import re import sys from pathlib import Path @@ -32,7 +33,11 @@ + r'):\s*(?P\S.*)?$' ) SECTION_KINDS_WITH_ITEMS = {'Args', 'Arguments', 'Attributes'} -RST_ROLE_RE = re.compile(r':[A-Za-z_][A-Za-z0-9_]*:`') +PRESERVE_BLOCK_SECTIONS = {'Examples', 'Notes'} +GENERIC_ITEM_SECTIONS = {'Raises', 'Returns', 'Yields'} +GENERIC_ITEM_RE = re.compile( + r'(?[A-Za-z_][A-Za-z0-9_\.\[\], \|\(\)]{0,80}?)\s*:' +) def _iter_python_files(paths: list[Path]) -> list[Path]: @@ -91,50 +96,122 @@ def _collect_names(node: ast.AST) -> list[str]: return list(dict.fromkeys(names)) +def _strip_blank_edges(lines: list[str]) -> list[str]: + start = 0 + end = len(lines) + while start < end and not lines[start].strip(): + start += 1 + while end > start and not lines[end - 1].strip(): + end -= 1 + return lines[start:end] + + +def _collapse_whitespace(lines: list[str]) -> str: + return ' '.join(line.strip() for line in lines if line.strip()) + + +def _repair_named_items(block_lines: list[str], names: list[str]) -> list[str] | None: + flat = _collapse_whitespace(block_lines) + if not flat or not names: + return None + + label_pattern = '|'.join(re.escape(name) for name in sorted(set(names), key=len, reverse=True)) + item_re = re.compile( + rf'(?\*{{0,2}}(?:{label_pattern})(?:\s*\([^)]*\))?)\s*:' + ) + matches = list(item_re.finditer(flat)) + if not matches or matches[0].start() != 0: + return None + + repaired: list[str] = [] + for index, match in enumerate(matches): + start = match.end() + end = matches[index + 1].start() if index + 1 < len(matches) else len(flat) + description = flat[start:end].strip() + repaired.append(f' {match.group("label")}: {description}' if description else f' {match.group("label")}:') + return repaired + + +def _repair_generic_items(block_lines: list[str]) -> list[str] | None: + flat = _collapse_whitespace(block_lines) + if not flat: + return None + + matches = list(GENERIC_ITEM_RE.finditer(flat)) + if not matches or matches[0].start() != 0: + return None + + repaired: list[str] = [] + for index, match in enumerate(matches): + start = match.end() + end = matches[index + 1].start() if index + 1 < len(matches) else len(flat) + description = flat[start:end].strip() + repaired.append(f' {match.group("label")}: {description}' if description else f' {match.group("label")}:') + return repaired + + +def _repair_section(section: str, block_lines: list[str], names: list[str]) -> list[str]: + stripped = _strip_blank_edges(block_lines) + if not stripped: + return [] + + if section in SECTION_KINDS_WITH_ITEMS: + repaired = _repair_named_items(stripped, names) + if repaired is not None: + return repaired + + if section in GENERIC_ITEM_SECTIONS: + repaired = _repair_generic_items(stripped) + if repaired is not None: + return repaired + + if section in PRESERVE_BLOCK_SECTIONS: + return [f' {line}' if line else '' for line in stripped] + + flat = _collapse_whitespace(stripped) + return [f' {flat}'] if flat else [] + + def _repair_inline_sections(docstring: str, names: list[str]) -> str: - repaired = docstring.replace('\r\n', '\n') - lines = repaired.split('\n') + cleaned = inspect.cleandoc(docstring.replace('\r\n', '\n')) + lines = cleaned.split('\n') out: list[str] = [] - current_section: str | None = None - section_indent = '' + index = 0 - for raw_line in lines: + while index < len(lines): + raw_line = lines[index] heading = GOOGLE_SECTION_RE.match(raw_line) - if heading: - current_section = heading.group('section') - section_indent = heading.group('indent') - section_name = 'Args' if current_section == 'Arguments' else current_section - out.append(f'{section_indent}{section_name}:') - rest = heading.group('rest') - if rest: - out.append(f'{section_indent} {rest.strip()}') + if heading is None: + out.append(raw_line.rstrip()) + index += 1 continue - if current_section is not None and raw_line.strip(): - stripped = raw_line.strip() - if current_section in SECTION_KINDS_WITH_ITEMS: - for name in sorted(names, key=len, reverse=True): - stripped = re.sub( - rf'([ \t]{{2,}})({re.escape(name)}(?:\s*\([^)]*\))?:)', - rf'\n{section_indent} \2', - stripped, - ) - out.extend( - ( - line - if line.startswith(f'{section_indent} ') - else f'{section_indent} {line.strip()}' - ) - for line in stripped.split('\n') - ) - continue - - out.append(raw_line) - if not raw_line.strip(): - continue - - current_section = None - section_indent = '' + section = heading.group('section') + section_name = 'Args' if section == 'Arguments' else section + out.append(f'{section_name}:') + + block_lines: list[str] = [] + rest = heading.group('rest') + if rest: + block_lines.append(rest) + + index += 1 + while index < len(lines): + next_line = lines[index] + if GOOGLE_SECTION_RE.match(next_line): + break + if ( + section_name not in PRESERVE_BLOCK_SECTIONS + and not next_line.strip() + and index + 1 < len(lines) + and lines[index + 1].strip() + and GOOGLE_SECTION_RE.match(lines[index + 1]) is None + ): + break + block_lines.append(next_line.rstrip()) + index += 1 + + out.extend(_repair_section(section_name, block_lines, names)) return '\n'.join(out) @@ -160,8 +237,12 @@ def _contains_unparsed_sections(parsed) -> bool: return False +def _has_section_heading(docstring: str, section: str) -> bool: + return re.search(rf'(?m)^[ \t]*{re.escape(section)}:\s*(?:\S.*)?$', docstring) is not None + + def _is_safe_conversion(docstring: str, parsed) -> bool: - if RST_ROLE_RE.search(docstring) or '::' in docstring: + if '::' in docstring: return False kinds = _meta_kinds(parsed) @@ -178,17 +259,25 @@ def _is_safe_conversion(docstring: str, parsed) -> bool: 'Examples': 'examples', } for section, expected_kind in expectations.items(): - if section in docstring and expected_kind not in kinds: + if _has_section_heading(docstring, section) and expected_kind not in kinds: return False return True +def _tidy_numpydoc_output(docstring: str) -> str: + tidied = docstring.strip('\n') + tidied = re.sub(r'\n{3,}', '\n\n', tidied) + tidied = re.sub(r'(?m)^([^\n]+)\n(-+)\n\n( +\S)', r'\1\n\2\n\3', tidied) + return tidied + + def _convert_docstring(docstring: str, names: list[str]) -> str | None: - if not _looks_google(docstring): + cleaned = inspect.cleandoc(docstring) + if not _looks_google(cleaned): return None - repaired = _repair_inline_sections(docstring, names) + repaired = _repair_inline_sections(cleaned, names) try: parsed = parse(repaired, style=DocstringStyle.GOOGLE) @@ -198,8 +287,8 @@ def _convert_docstring(docstring: str, names: list[str]) -> str | None: if not _is_safe_conversion(repaired, parsed): return None - converted = compose(parsed, style=DocstringStyle.NUMPYDOC) - return converted if converted != docstring else None + converted = _tidy_numpydoc_output(compose(parsed, style=DocstringStyle.NUMPYDOC)) + return converted if converted != cleaned else None def _format_multiline_docstring(content: str, indent: int) -> str: From 98a168cc0bd94c5d9826c1627d44e656f954a6c2 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 13:15:00 +0100 Subject: [PATCH 25/48] Convert docstrings from google to numpy style: Step 3 --- src/easydiffraction/analysis/analysis.py | 35 ++-- .../analysis/calculators/base.py | 4 +- .../analysis/calculators/crysfml.py | 17 +- .../analysis/calculators/cryspy.py | 38 ++-- .../analysis/categories/aliases/default.py | 8 +- .../categories/constraints/default.py | 1 - .../joint_fit_experiments/default.py | 3 +- .../analysis/fit_helpers/metrics.py | 14 +- .../analysis/fit_helpers/reporting.py | 4 +- .../analysis/fit_helpers/tracking.py | 1 - src/easydiffraction/analysis/fitting.py | 25 +-- .../analysis/minimizers/base.py | 10 +- .../analysis/minimizers/dfols.py | 6 +- .../analysis/minimizers/lmfit.py | 13 +- src/easydiffraction/core/category.py | 6 +- src/easydiffraction/core/collection.py | 4 +- src/easydiffraction/core/datablock.py | 3 +- src/easydiffraction/core/factory.py | 10 +- src/easydiffraction/core/guard.py | 6 +- src/easydiffraction/core/metadata.py | 21 +- src/easydiffraction/core/variable.py | 16 +- .../crystallography/crystallography.py | 6 +- .../categories/experiment_type/default.py | 4 +- .../datablocks/experiment/collection.py | 2 - .../datablocks/experiment/item/base.py | 17 +- .../datablocks/experiment/item/bragg_pd.py | 1 - .../datablocks/experiment/item/factory.py | 4 - .../categories/atom_sites/default.py | 9 +- .../structure/categories/cell/default.py | 3 +- .../categories/space_group/default.py | 9 +- .../datablocks/structure/collection.py | 3 - .../datablocks/structure/item/base.py | 8 - .../datablocks/structure/item/factory.py | 7 +- src/easydiffraction/display/base.py | 3 +- src/easydiffraction/display/plotters/ascii.py | 15 +- src/easydiffraction/display/plotters/base.py | 8 +- .../display/plotters/plotly.py | 19 +- src/easydiffraction/display/plotting.py | 52 ++--- src/easydiffraction/display/tablers/base.py | 14 +- src/easydiffraction/display/tablers/pandas.py | 15 +- src/easydiffraction/display/tablers/rich.py | 25 +-- src/easydiffraction/display/tables.py | 7 +- src/easydiffraction/summary/summary.py | 1 - src/easydiffraction/utils/environment.py | 10 +- src/easydiffraction/utils/logging.py | 23 +-- src/easydiffraction/utils/utils.py | 134 ++++++------- tools/convert_google_docstrings_to_numpy.py | 183 +++++++++++++++++- 47 files changed, 437 insertions(+), 390 deletions(-) diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 5be7a6fc..ab24f879 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -30,27 +30,28 @@ class Analysis: """ High-level orchestration of analysis tasks for a Project. - This class wires calculators and minimizers, exposes a compact - interface for parameters, constraints and results, and coordinates - computations across the project's structures and experiments. + This class wires calculators and minimizers, exposes a compact interface + for parameters, constraints and results, and coordinates computations + across the project's structures and experiments. Typical usage: - - Display or filter parameters to fit. - Select a - calculator/minimizer implementation. - Calculate patterns and run - single or joint fits. + - Display or filter parameters to fit. - Select a calculator/minimizer + implementation. - Calculate patterns and run single or joint fits. Attributes ---------- project - The parent Project object. aliases: A registry of human-friendly aliases for parameters. constraints: Symbolic constraints between parameters. calculator: Active calculator used for computations. fitter: Active fitter/minimizer driver. + The parent Project object. aliases: A registry of human-friendly + aliases for parameters. constraints: Symbolic constraints + between parameters. calculator: Active calculator used for + computations. fitter: Active fitter/minimizer driver. """ def __init__(self, project) -> None: """ Create a new Analysis instance bound to a project. - Parameters ---------- project @@ -142,7 +143,6 @@ def aliases_type(self, new_type: str) -> None: """ Switch to a different aliases collection type. - Parameters ---------- new_type @@ -184,7 +184,6 @@ def constraints_type(self, new_type: str) -> None: """ Switch to a different constraints collection type. - Parameters ---------- new_type @@ -219,7 +218,6 @@ def _get_params_as_dataframe( """ Convert a list of parameters to a DataFrame. - Parameters ---------- params @@ -227,9 +225,7 @@ def _get_params_as_dataframe( Returns ------- - - A pandas DataFrame containing parameter - information. + A pandas DataFrame containing parameter information. """ records = [] for param in params: @@ -505,7 +501,6 @@ def current_minimizer(self, selection: str) -> None: """ Switch to a different minimizer implementation. - Parameters ---------- selection @@ -534,7 +529,6 @@ def fit_mode_type(self, new_type: str) -> None: """ Switch to a different fit-mode category type. - Parameters ---------- new_type @@ -711,8 +705,8 @@ def _update_categories(self, called_by_minimizer=False) -> None: """ Update all categories owned by Analysis. - This ensures aliases and constraints are up-to-date before - serialization or after parameter changes. + This ensures aliases and constraints are up-to-date before serialization + or after parameter changes. Parameters ---------- @@ -733,12 +727,9 @@ def as_cif(self): """ Serialize the analysis section to a CIF string. - Returns ------- - - The analysis section represented as a CIF document - string. + The analysis section represented as a CIF document string. """ from easydiffraction.io.cif.serialize import analysis_to_cif diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index 9521ebce..ae0c1b4c 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -45,9 +45,7 @@ def calculate_pattern( called_by_minimizer: bool, ) -> np.ndarray: """ - Calculate the diffraction pattern for a single structure and - experiment. - + Calculate the diffraction pattern for a single structure and experiment. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 40de8679..02d78d90 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -56,7 +56,7 @@ def calculate_structure_factors( structures The structures to calculate structure factors for. experiments - The experiments associated with the sample models. + The experiments associated with the sample models. """ raise NotImplementedError('HKL calculation is not implemented for CrysfmlCalculator.') @@ -67,8 +67,8 @@ def calculate_pattern( called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: """ - Calculates the diffraction pattern using Crysfml for the - given structure and experiment. + Calculates the diffraction pattern using Crysfml for the given structure + and experiment. Parameters ---------- @@ -81,7 +81,8 @@ def calculate_pattern( Returns ------- - The calculated diffraction pattern as a NumPy array or a list of floats. + The calculated diffraction pattern as a NumPy array or a list of + floats. """ # Intentionally unused, required by public API/signature del called_by_minimizer @@ -126,8 +127,8 @@ def _crysfml_dict( experiment: ExperimentBase, ) -> Dict[str, Union[ExperimentBase, Structure]]: """ - Converts the structure and experiment into a dictionary - format for Crysfml. + Converts the structure and experiment into a dictionary format for + Crysfml. Parameters ---------- @@ -154,7 +155,6 @@ def _convert_structure_to_dict( """ Converts a structure into a dictionary format. - Parameters ---------- structure @@ -162,7 +162,6 @@ def _convert_structure_to_dict( Returns ------- - A dictionary representation of the structure. """ structure_dict = { @@ -200,7 +199,6 @@ def _convert_experiment_to_dict( """ Converts an experiment into a dictionary format. - Parameters ---------- experiment @@ -208,7 +206,6 @@ def _convert_experiment_to_dict( Returns ------- - A dictionary representation of the experiment. """ expt_type = getattr(experiment, 'type', None) diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 34f6aef5..0b28da1d 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -62,17 +62,16 @@ def calculate_structure_factors( called_by_minimizer: bool = False, ): """ - Raises a NotImplementedError as HKL calculation is not - implemented. + Raises a NotImplementedError as HKL calculation is not implemented. Parameters ---------- structure The structure to calculate structure factors for. experiment - The experiment associated with the sample models. + The experiment associated with the sample models. called_by_minimizer - Whether the calculation is called by a minimizer. + Whether the calculation is called by a minimizer. """ combined_name = f'{structure.name}_{experiment.name}' @@ -122,13 +121,13 @@ def calculate_pattern( called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: """ - Calculates the diffraction pattern using Cryspy for the given - structure and experiment. + Calculates the diffraction pattern using Cryspy for the given structure + and experiment. - We only recreate the cryspy_obj if this method is - NOT called - by the minimizer, or - the cryspy_dict is NOT yet created. In - other cases, we are modifying the existing cryspy_dict This - allows significantly speeding up the calculation + We only recreate the cryspy_obj if this method is - NOT called by the + minimizer, or - the cryspy_dict is NOT yet created. In other cases, we + are modifying the existing cryspy_dict This allows significantly + speeding up the calculation Parameters ---------- @@ -141,7 +140,8 @@ def calculate_pattern( Returns ------- - The calculated diffraction pattern as a NumPy array or a list of floats. + The calculated diffraction pattern as a NumPy array or a list of + floats. """ combined_name = f'{structure.name}_{experiment.name}' @@ -200,8 +200,7 @@ def _recreate_cryspy_dict( experiment: ExperimentBase, ) -> Dict[str, Any]: """ - Recreates the Cryspy dictionary for the given structure and - experiment. + Recreates the Cryspy dictionary for the given structure and experiment. Parameters ---------- @@ -319,8 +318,7 @@ def _recreate_cryspy_obj( experiment: ExperimentBase, ) -> Any: """ - Recreates the Cryspy object for the given structure and - experiment. + Recreates the Cryspy object for the given structure and experiment. Parameters ---------- @@ -357,7 +355,6 @@ def _convert_structure_to_cryspy_cif( """ Converts a structure to a Cryspy CIF string. - Parameters ---------- structure @@ -365,9 +362,7 @@ def _convert_structure_to_cryspy_cif( Returns ------- - - The Cryspy CIF string representation of the - structure. + The Cryspy CIF string representation of the structure. """ return structure.as_cif @@ -379,7 +374,6 @@ def _convert_experiment_to_cryspy_cif( """ Converts an experiment to a Cryspy CIF string. - Parameters ---------- experiment @@ -389,9 +383,7 @@ def _convert_experiment_to_cryspy_cif( Returns ------- - - The Cryspy CIF string representation of the - experiment. + The Cryspy CIF string representation of the experiment. """ # Try to get experiment attributes expt_type = getattr(experiment, 'type', None) diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 55894f80..35ae417b 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -22,13 +22,15 @@ class Alias(CategoryItem): """ Single alias entry. - Maps a human-readable ``label`` to a concrete ``param_uid`` used by - the engine. + Maps a human-readable ``label`` to a concrete ``param_uid`` used by the + engine. Parameters ---------- label - Alias label. Must match ``^[A-Za- z_][A-Za-z0-9_]*$``. param_uid: Target parameter uid. Same identifier pattern as ``label``. + Alias label. Must match ``^[A-Za- z_][A-Za-z0-9_]*$``. + param_uid: Target parameter uid. Same identifier pattern as + ``label``. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 39ec944b..685f1cd6 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -23,7 +23,6 @@ class Constraint(CategoryItem): """ Single constraint item. - Parameters ---------- lhs_alias diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index dc079fe8..fc1b2a2b 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -29,7 +29,8 @@ class JointFitExperiment(CategoryItem): Parameters ---------- id - Experiment identifier used in the fit session. weight: Relative weight factor in the combined objective. + Experiment identifier used in the fit session. weight: Relative + weight factor in the combined objective. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index 4b4808ea..2f9c61cc 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -42,8 +42,7 @@ def calculate_weighted_r_factor( weights: np.ndarray, ) -> float: """ - Calculate the weighted R-factor between observed and calculated - data. + Calculate the weighted R-factor between observed and calculated data. Parameters ---------- @@ -71,8 +70,7 @@ def calculate_rb_factor( y_calc: np.ndarray, ) -> float: """ - Calculate the Bragg R-factor between observed and calculated - data. + Calculate the Bragg R-factor between observed and calculated data. Parameters ---------- @@ -97,8 +95,7 @@ def calculate_r_factor_squared( y_calc: np.ndarray, ) -> float: """ - Calculate the R-factor squared between observed and calculated - data. + Calculate the R-factor squared between observed and calculated data. Parameters ---------- @@ -125,7 +122,6 @@ def calculate_reduced_chi_square( """ Calculate the reduced chi-square statistic. - Parameters ---------- residuals @@ -135,7 +131,6 @@ def calculate_reduced_chi_square( Returns ------- - Reduced chi-square value. """ residuals = np.asarray(residuals) @@ -165,7 +160,8 @@ def get_reliability_inputs( Returns ------- - Tuple containing arrays of (observed values, calculated values, error values) + Tuple containing arrays of (observed values, calculated values, + error values) """ y_obs_all = [] y_calc_all = [] diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index 378c4ebf..65788cc3 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -58,7 +58,9 @@ def __init__( fitting_time Time taken for the fitting process. **kwargs - Additional engine-specific fields. If ``redchi`` is provided and ``reduced_chi_square`` is not set, it is used as the reduced chi-square value. + Additional engine-specific fields. If ``redchi`` is provided and + ``reduced_chi_square`` is not set, it is used as the reduced + chi-square value. """ self.success: bool = success self.parameters: List[Any] = parameters if parameters is not None else [] diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index 629f159e..dafb35fa 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -231,7 +231,6 @@ def add_tracking_info(self, row: List[str]) -> None: """ Append a formatted row to the progress display. - Parameters ---------- row diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index c08d456d..99ed4eee 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -38,9 +38,9 @@ def fit( """ Run the fitting process. - This method performs the optimization but does not display - results. Use :meth:`show_fit_results` on the Analysis object to - display the fit results after fitting is complete. + This method performs the optimization but does not display results. Use + :meth:`show_fit_results` on the Analysis object to display the fit + results after fitting is complete. Parameters ---------- @@ -51,7 +51,8 @@ def fit( weights Optional weights for joint fitting. analysis - Optional Analysis object to update its categories during fitting. + Optional Analysis object to update its categories during + fitting. """ params = structures.free_parameters + experiments.free_parameters @@ -83,10 +84,9 @@ def _process_fit_results( """ Collect reliability inputs and display fit results. - This method is typically called by - :meth:`Analysis.show_fit_results` rather than directly. It - calculates R-factors and other metrics, then renders them to the - console. + This method is typically called by :meth:`Analysis.show_fit_results` + rather than directly. It calculates R-factors and other metrics, then + renders them to the console. Parameters ---------- @@ -144,9 +144,9 @@ def _residual_function( analysis=None, ) -> np.ndarray: """ - Residual function computes the difference between measured - and calculated patterns. It updates the parameter values - according to the optimizer-provided engine_params. + Residual function computes the difference between measured and + calculated patterns. It updates the parameter values according to the + optimizer-provided engine_params. Parameters ---------- @@ -161,7 +161,8 @@ def _residual_function( weights Optional weights for joint fitting. analysis - Optional Analysis object to update its categories during fitting. + Optional Analysis object to update its categories during + fitting. Returns ------- diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index d81b103e..5e47beae 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -45,7 +45,6 @@ def _start_tracking(self, minimizer_name: str) -> None: """ Initialize progress tracking and timer. - Parameters ---------- minimizer_name @@ -65,7 +64,6 @@ def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: """ Prepare keyword-arguments for the underlying solver. - Parameters ---------- parameters @@ -73,9 +71,7 @@ def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: Returns ------- - - Mapping of keyword arguments to pass into - ``_run_solver``. + Mapping of keyword arguments to pass into ``_run_solver``. """ pass @@ -107,7 +103,6 @@ def _finalize_fit( """ Build :class:`FitResults` and store it on ``self.result``. - Parameters ---------- parameters @@ -150,7 +145,8 @@ def fit( parameters Free parameters to optimize. objective_function - Callable returning residuals for a given set of engine arguments. + Callable returning residuals for a given set of engine + arguments. Returns ------- diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index 95894eb0..45e42eba 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -60,7 +60,6 @@ def _sync_result_to_parameters( """ Synchronizes the result from the solver to the parameters. - Parameters ---------- parameters @@ -83,7 +82,6 @@ def _check_success(self, raw_result: Any) -> bool: """ Determines success from DFO-LS result dictionary. - Parameters ---------- raw_result @@ -91,8 +89,6 @@ def _check_success(self, raw_result: Any) -> bool: Returns ------- - - True if the optimization was successful, False - otherwise. + True if the optimization was successful, False otherwise. """ return raw_result.flag == raw_result.EXIT_SUCCESS diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index aa420157..d9cbaf2f 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -43,7 +43,6 @@ def _prepare_solver_args( """ Prepares the solver arguments for the lmfit minimizer. - Parameters ---------- parameters @@ -51,9 +50,7 @@ def _prepare_solver_args( Returns ------- - - A dictionary containing the prepared lmfit. - Parameters object. + A dictionary containing the prepared lmfit. Parameters object. """ engine_parameters = lmfit.Parameters() for param in parameters: @@ -70,7 +67,6 @@ def _run_solver(self, objective_function: Any, **kwargs: Any) -> Any: """ Runs the lmfit solver. - Parameters ---------- objective_function @@ -80,7 +76,6 @@ def _run_solver(self, objective_function: Any, **kwargs: Any) -> Any: Returns ------- - The result of the lmfit minimization. """ engine_parameters = kwargs.get('engine_parameters') @@ -101,7 +96,6 @@ def _sync_result_to_parameters( """ Synchronizes the result from the solver to the parameters. - Parameters ---------- parameters @@ -123,7 +117,6 @@ def _check_success(self, raw_result: Any) -> bool: """ Determines success from lmfit MinimizerResult. - Parameters ---------- raw_result @@ -131,9 +124,7 @@ def _check_success(self, raw_result: Any) -> bool: Returns ------- - - True if the optimization was successful, False - otherwise. + True if the optimization was successful, False otherwise. """ return getattr(raw_result, 'success', False) diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index 6f60b8c0..64579a70 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -219,7 +219,6 @@ def add(self, item) -> None: """ Insert or replace a pre-built item into the collection. - Parameters ---------- item @@ -232,9 +231,8 @@ def create(self, **kwargs) -> None: """ Create a new item with the given attributes and add it. - A default instance of the collection's item type is created, - then each keyword argument is applied via ``setattr``. - + A default instance of the collection's item type is created, then each + keyword argument is applied via ``setattr``. Parameters ---------- diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index 0cf2564c..5dc89e0a 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -19,7 +19,8 @@ class CollectionBase(GuardedBase): Parameters ---------- item_type - Type of items accepted by the collection. Used for validation and tooling; not enforced at runtime here. + Type of items accepted by the collection. Used for validation + and tooling; not enforced at runtime here. """ def __init__(self, item_type) -> None: @@ -80,7 +81,6 @@ def remove(self, name: str) -> None: """ Remove an item by its key. - Parameters ---------- name diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 3321d0ac..fa9a2083 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -139,7 +139,8 @@ def add(self, item) -> None: Parameters ---------- item - A ``DatablockItem`` instance (e.g. a ``Structure`` or ``ExperimentBase`` subclass). + A ``DatablockItem`` instance (e.g. a ``Structure`` or + ``ExperimentBase`` subclass). """ self[item._identity.datablock_entry_name] = item diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index 1fe0f0df..e1a98d2c 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -83,14 +83,15 @@ def default_tag(cls, **conditions) -> str: """ Resolve the default tag for a given experimental context. - Uses *largest-subset matching*: the rule whose key is the - biggest subset of the given conditions wins. A rule with an - empty key (``frozenset()``) acts as a universal fallback. + Uses *largest-subset matching*: the rule whose key is the biggest subset + of the given conditions wins. A rule with an empty key (``frozenset()``) + acts as a universal fallback. Parameters ---------- **conditions - Experimental-axis values, e.g. ``scattering_type=ScatteringTypeEnum.BRAGG``. + Experimental-axis values, e.g. + ``scattering_type=ScatteringTypeEnum.BRAGG``. Returns ------- @@ -150,7 +151,6 @@ def create_default_for(cls, **conditions) -> Any: Combines ``default_tag(**conditions)`` with ``create(tag)``. - Parameters ---------- **conditions diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index a98c2f5c..96cc5990 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -79,13 +79,12 @@ def _assign_attr(self, key, value): @classmethod def _iter_properties(cls): """ - Iterate over all public properties defined in the class - hierarchy. - + Iterate over all public properties defined in the class hierarchy. Yields ------ Each (key, property) pair for + public attributes. """ for base in cls.mro(): @@ -165,7 +164,6 @@ def _iter_methods(cls): """ Iterate over public methods in the class hierarchy. - Yields ------ Each (name, function) pair. diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index 2dd7a654..da09d859 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -18,15 +18,19 @@ @dataclass(frozen=True) class TypeInfo: """ - Stable identity and human-readable description for a factory- - created class. + Stable identity and human-readable description for a factory- created + class. Attributes ---------- tag - Short, stable string identifier used for serialization, user-facing selection, and factory lookup. Must be unique within a factory's registry. Examples: ``'line-segment'``, ``'pseudo-voigt'``, ``'cryspy'``. + Short, stable string identifier used for serialization, + user-facing selection, and factory lookup. Must be unique within + a factory's registry. Examples: ``'line-segment'``, + ``'pseudo-voigt'``, ``'cryspy'``. description - One-line human-readable explanation. Used in ``show_supported()`` tables and documentation. + One-line human-readable explanation. Used in + ``show_supported()`` tables and documentation. """ tag: str @@ -87,7 +91,8 @@ class CalculatorSupport: Attributes ---------- calculators - Frozenset of ``CalculatorEnum`` values. Empty means "any calculator" (no restriction). + Frozenset of ``CalculatorEnum`` values. Empty means "any + calculator" (no restriction). """ calculators: FrozenSet = frozenset() @@ -96,7 +101,6 @@ def supports(self, calculator) -> bool: """ Check if a specific calculator can handle this class. - Parameters ---------- calculator @@ -104,9 +108,8 @@ def supports(self, calculator) -> bool: Returns ------- - - ``True`` if the calculator is in the set, or if the - set is empty (meaning any calculator is accepted). + ``True`` if the calculator is in the set, or if the set is empty + (meaning any calculator is accepted). """ if not self.calculators: return True diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 04d4f413..89f0f0bb 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -30,18 +30,18 @@ class GenericDescriptorBase(GuardedBase): """ Base class for all parameter-like descriptors. - A descriptor encapsulates a typed value with validation, human- - readable name/description and a globally unique identifier that is - stable across the session. Concrete subclasses specialize the - expected data type and can extend the public API with additional - behavior (e.g. units). + A descriptor encapsulates a typed value with validation, human- readable + name/description and a globally unique identifier that is stable across + the session. Concrete subclasses specialize the expected data type and + can extend the public API with additional behavior (e.g. units). Attributes ---------- name Local parameter name (e.g. 'a', 'b_iso'). description - Optional human-readable description. uid: Stable random identifier for external references. + Optional human-readable description. uid: Stable random + identifier for external references. """ _BOOL_SPEC_TEMPLATE = AttributeSpec( @@ -59,7 +59,6 @@ def __init__( """ Initialize the descriptor with validation and identity. - Parameters ---------- value_spec @@ -400,7 +399,6 @@ def __init__( """ String descriptor bound to a CIF handler. - Parameters ---------- cif_handler @@ -426,7 +424,6 @@ def __init__( """ Numeric descriptor bound to a CIF handler. - Parameters ---------- cif_handler @@ -452,7 +449,6 @@ def __init__( """ Fittable parameter bound to a CIF handler. - Parameters ---------- cif_handler diff --git a/src/easydiffraction/crystallography/crystallography.py b/src/easydiffraction/crystallography/crystallography.py index 4a0e4c0b..da60a762 100644 --- a/src/easydiffraction/crystallography/crystallography.py +++ b/src/easydiffraction/crystallography/crystallography.py @@ -22,8 +22,7 @@ def apply_cell_symmetry_constraints( name_hm: str, ) -> Dict[str, float]: """ - Apply symmetry constraints to unit cell parameters based on space - group. + Apply symmetry constraints to unit cell parameters based on space group. Parameters ---------- @@ -96,8 +95,7 @@ def apply_atom_site_symmetry_constraints( wyckoff_letter: str, ) -> Dict[str, Any]: """ - Apply symmetry constraints to atomic coordinates based on site - symmetry. + Apply symmetry constraints to atomic coordinates based on site symmetry. Parameters ---------- diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 91ff3647..899c8217 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -29,12 +29,12 @@ class ExperimentType(CategoryItem): """ Container of categorical attributes defining experiment flavor. - Parameters ---------- sample_form - Powder or Single crystal. beam_mode: + Powder or Single crystal. beam_mode: Constant wavelength : CW) or time-of-flight (TOF + Neutrons or X-rays. scattering_type Bragg or Total. """ diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index f5a51edf..4cf8375b 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -69,7 +69,6 @@ def add_from_cif_str( """ Add an experiment from a CIF string. - Parameters ---------- cif_str @@ -87,7 +86,6 @@ def add_from_cif_path( """ Add an experiment from a CIF file path. - Parameters ---------- cif_path : str diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index e855d835..86ddf119 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -61,7 +61,6 @@ def name(self, new: str) -> None: """ Rename the experiment. - Parameters ---------- new @@ -93,7 +92,6 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: """ Load ASCII data from file into the experiment data category. - Parameters ---------- data_path @@ -247,7 +245,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: Parameters ---------- data_path - Path to data file with columns compatible with the beam mode. + Path to data file with columns compatible with the beam mode. """ pass @@ -270,7 +268,6 @@ def extinction_type(self, new_type: str) -> None: """ Switch to a different extinction correction model. - Parameters ---------- new_type @@ -318,7 +315,6 @@ def linked_crystal_type(self, new_type: str) -> None: """ Switch to a different linked-crystal reference type. - Parameters ---------- new_type @@ -366,7 +362,6 @@ def instrument_type(self, new_type: str) -> None: """ Switch to a different instrument type. - Parameters ---------- new_type @@ -422,7 +417,6 @@ def data_type(self, new_type: str) -> None: """ Switch to a different data collection type. - Parameters ---------- new_type @@ -485,7 +479,6 @@ def _get_valid_linked_phases( """ Get valid linked phases for this experiment. - Parameters ---------- structures @@ -493,7 +486,6 @@ def _get_valid_linked_phases( Returns ------- - A list of valid linked phases. """ if not self.linked_phases: @@ -525,7 +517,8 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: Parameters ---------- data_path - Path to data file with columns compatible with the beam mode (e.g. 2θ/I/σ for CWL, TOF/I/σ for TOF). + Path to data file with columns compatible with the beam mode + (e.g. 2θ/I/σ for CWL, TOF/I/σ for TOF). """ pass @@ -544,7 +537,6 @@ def linked_phases_type(self, new_type: str) -> None: """ Switch to a different linked-phases collection type. - Parameters ---------- new_type @@ -588,7 +580,6 @@ def excluded_regions_type(self, new_type: str) -> None: """ Switch to a different excluded-regions collection type. - Parameters ---------- new_type @@ -638,7 +629,6 @@ def data_type(self, new_type: str) -> None: """ Switch to a different data collection type. - Parameters ---------- new_type @@ -681,7 +671,6 @@ def peak_profile_type(self, new_type: str): """ Change the active peak profile type, if supported. - Parameters ---------- new_type diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index c54ce3a9..24dbf9c3 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -119,7 +119,6 @@ def instrument_type(self, new_type: str) -> None: """ Switch to a different instrument type. - Parameters ---------- new_type diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index d7e90f9f..3d38cbd3 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -180,7 +180,6 @@ def from_cif_str( """ Create an experiment from a CIF string. - Parameters ---------- cif_str @@ -188,7 +187,6 @@ def from_cif_str( Returns ------- - A populated experiment instance. """ doc = document_from_string(cif_str) @@ -205,7 +203,6 @@ def from_cif_path( """ Create an experiment from a CIF file path. - Parameters ---------- cif_path @@ -213,7 +210,6 @@ def from_cif_path( Returns ------- - A populated experiment instance. """ doc = document_from_path(cif_path) diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index 2ee7a41e..dfb616e6 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -141,12 +141,10 @@ def _type_symbol_allowed_values(self) -> list[str]: """ Return chemical symbols accepted by *cryspy*. - Returns ------- list[str] - Unique element/isotope symbols from the - database. + Unique element/isotope symbols from the database. """ return list({key[1] for key in DATABASE['Isotopes']}) @@ -155,7 +153,6 @@ def _wyckoff_letter_allowed_values(self) -> list[str]: """ Return allowed Wyckoff-letter symbols. - Returns ------- list[str] @@ -172,7 +169,6 @@ def _wyckoff_letter_default_value(self) -> str: """ Return the default Wyckoff letter. - Returns ------- str @@ -371,7 +367,8 @@ def _update( Parameters ---------- called_by_minimizer : bool - Whether the update was triggered by the fitting minimizer. Currently unused. + Whether the update was triggered by the fitting minimizer. + Currently unused. """ del called_by_minimizer diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index af5118b3..903b9b57 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -138,7 +138,8 @@ def _update( Parameters ---------- called_by_minimizer : bool, default=False - Whether the update was triggered by the fitting minimizer. Currently unused. + Whether the update was triggered by the fitting minimizer. + Currently unused. """ del called_by_minimizer # TODO: ??? diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 5e5a0439..47e29aa2 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -94,7 +94,6 @@ def _name_h_m_allowed_values(self) -> list[str]: """ Return the list of recognised Hermann–Mauguin short symbols. - Returns ------- list[str] @@ -105,15 +104,12 @@ def _name_h_m_allowed_values(self) -> list[str]: @property def _it_coordinate_system_code_allowed_values(self) -> list[str]: """ - Return allowed IT coordinate system codes for the current - group. - + Return allowed IT coordinate system codes for the current group. Returns ------- list[str] - Coordinate-system codes, or ``['']`` - when none are defined. + Coordinate-system codes, or ``['']`` when none are defined. """ name = self.name_h_m.value it_number = get_it_number_by_name_hm_short(name) @@ -126,7 +122,6 @@ def _it_coordinate_system_code_default_value(self) -> str: """ Return the default IT coordinate system code. - Returns ------- str diff --git a/src/easydiffraction/datablocks/structure/collection.py b/src/easydiffraction/datablocks/structure/collection.py index 1aedd334..79e4bd0f 100644 --- a/src/easydiffraction/datablocks/structure/collection.py +++ b/src/easydiffraction/datablocks/structure/collection.py @@ -36,7 +36,6 @@ def create( """ Create a minimal structure and add it to the collection. - Parameters ---------- name : str @@ -54,7 +53,6 @@ def add_from_cif_str( """ Create a structure from CIF content and add it. - Parameters ---------- cif_str : str @@ -72,7 +70,6 @@ def add_from_cif_path( """ Create a structure from a CIF file and add it. - Parameters ---------- cif_path : str diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index 9a20f40a..3283886e 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -43,7 +43,6 @@ def name(self) -> str: """ Name identifier for this structure. - Returns ------- str @@ -57,7 +56,6 @@ def name(self, new: str) -> None: """ Set the name identifier for this structure. - Parameters ---------- new : str @@ -80,7 +78,6 @@ def cell(self, new: Cell) -> None: """ Replace the unit-cell category for this structure. - Parameters ---------- new : Cell @@ -98,7 +95,6 @@ def cell_type(self, new_type: str) -> None: """ Switch to a different unit-cell type. - Parameters ---------- new_type @@ -141,7 +137,6 @@ def space_group(self, new: SpaceGroup) -> None: """ Replace the space-group category for this structure. - Parameters ---------- new : SpaceGroup @@ -159,7 +154,6 @@ def space_group_type(self, new_type: str) -> None: """ Switch to a different space-group type. - Parameters ---------- new_type @@ -202,7 +196,6 @@ def atom_sites(self, new: AtomSites) -> None: """ Replace the atom-sites collection for this structure. - Parameters ---------- new : AtomSites @@ -220,7 +213,6 @@ def atom_sites_type(self, new_type: str) -> None: """ Switch to a different atom-sites collection type. - Parameters ---------- new_type diff --git a/src/easydiffraction/datablocks/structure/item/factory.py b/src/easydiffraction/datablocks/structure/item/factory.py index a1d43fe6..3148639a 100644 --- a/src/easydiffraction/datablocks/structure/item/factory.py +++ b/src/easydiffraction/datablocks/structure/item/factory.py @@ -45,7 +45,6 @@ def _from_gemmi_block( """ Build a structure from a single *gemmi* CIF block. - Parameters ---------- block : gemmi.cif.Block @@ -76,7 +75,6 @@ def from_scratch( """ Create a minimal default structure. - Parameters ---------- name : str @@ -85,8 +83,7 @@ def from_scratch( Returns ------- Structure - An empty structure with default - categories. + An empty structure with default categories. """ return Structure(name=name) @@ -100,7 +97,6 @@ def from_cif_str( """ Create a structure by parsing a CIF string. - Parameters ---------- cif_str : str @@ -125,7 +121,6 @@ def from_cif_path( """ Create a structure by reading and parsing a CIF file. - Parameters ---------- cif_path : str diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index 5a6ac292..54ff5dd1 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -97,7 +97,8 @@ def create(cls, engine_name: str) -> Any: Parameters ---------- engine_name - Identifier of the engine to instantiate as listed in ``_registry()``. + Identifier of the engine to instantiate as listed in + ``_registry()``. Returns ------- diff --git a/src/easydiffraction/display/plotters/ascii.py b/src/easydiffraction/display/plotters/ascii.py index 268a84fc..44c1c13e 100644 --- a/src/easydiffraction/display/plotters/ascii.py +++ b/src/easydiffraction/display/plotters/ascii.py @@ -32,7 +32,6 @@ def _get_legend_item(self, label): The legend uses a colored line matching the series color and the human-readable name from :data:`SERIES_CONFIG`. - Parameters ---------- label @@ -40,7 +39,6 @@ def _get_legend_item(self, label): Returns ------- - A formatted legend string with color escapes. """ color_start = DEFAULT_COLORS[label] @@ -62,9 +60,9 @@ def plot_powder( """ Render a line plot for powder diffraction data. - Suitable for powder diffraction data where intensity is plotted - against an x-axis variable (2θ, TOF, d-spacing). Uses ASCII - characters for terminal display. + Suitable for powder diffraction data where intensity is plotted against + an x-axis variable (2θ, TOF, d-spacing). Uses ASCII characters for + terminal display. Parameters ---------- @@ -115,8 +113,8 @@ def plot_single_crystal( """ Render a scatter plot for single crystal diffraction data. - Creates an ASCII scatter plot showing measured vs calculated - values with a diagonal reference line. + Creates an ASCII scatter plot showing measured vs calculated values with + a diagonal reference line. Parameters ---------- @@ -125,7 +123,8 @@ def plot_single_crystal( y_meas 1D array-like of measured values (y-axis). y_meas_su - 1D array-like of measurement uncertainties (ignored in ASCII mode). + 1D array-like of measurement uncertainties (ignored in ASCII + mode). axes_labels Pair of strings for the x and y titles. title diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index 19116d8c..a60fcdd7 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -175,8 +175,8 @@ def plot_powder( """ Render a line plot for powder diffraction data. - Suitable for powder diffraction data where intensity is plotted - against an x-axis variable (2θ, TOF, d-spacing). + Suitable for powder diffraction data where intensity is plotted against + an x-axis variable (2θ, TOF, d-spacing). Parameters ---------- @@ -208,8 +208,8 @@ def plot_single_crystal( """ Render a scatter plot for single crystal diffraction data. - Suitable for single crystal diffraction data where measured - values are plotted against calculated values with error bars. + Suitable for single crystal diffraction data where measured values are + plotted against calculated values with error bars. Parameters ---------- diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index b042f2a0..67fff499 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -52,7 +52,7 @@ def _get_powder_trace( y 1D array- like of y-axis values. label - Series identifier (``'meas'``, ``'calc'``, or ``'resid'``). + Series identifier (``'meas'``, ``'calc'``, or ``'resid'``). Returns ------- @@ -95,7 +95,8 @@ def _get_single_crystal_trace( Returns ------- A configured - class:`plotly.graph_objects.Scatter` trace with markers and error bars. + class:`plotly.graph_objects.Scatter` trace with markers and + error bars. """ trace = go.Scatter( x=x_calc, @@ -124,10 +125,8 @@ def _get_diagonal_shape(self): Returns a y=x diagonal line spanning the plot area using paper coordinates (0,0) to (1,1). - Returns ------- - A dict configuring a diagonal line shape. """ return dict( @@ -146,10 +145,8 @@ def _get_config(self): """ Return the Plotly figure configuration. - Returns ------- - A dict with display and mode bar settings. """ return dict( @@ -284,8 +281,8 @@ def plot_powder( """ Render a line plot for powder diffraction data. - Suitable for powder diffraction data where intensity is plotted - against an x-axis variable (2θ, TOF, d-spacing). + Suitable for powder diffraction data where intensity is plotted against + an x-axis variable (2θ, TOF, d-spacing). Parameters ---------- @@ -331,9 +328,9 @@ def plot_single_crystal( """ Render a scatter plot for single crystal diffraction data. - Suitable for single crystal diffraction data where measured - values are plotted against calculated values with error bars and - a diagonal reference line. + Suitable for single crystal diffraction data where measured values are + plotted against calculated values with error bars and a diagonal + reference line. Parameters ---------- diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 790dcc2a..d1e33950 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -82,8 +82,7 @@ def _default_engine(cls) -> str: def _auto_x_range_for_ascii(self, pattern, x_array, x_min, x_max): """ - For the ASCII engine, narrow the range around the tallest - peak. + For the ASCII engine, narrow the range around the tallest peak. Parameters ---------- @@ -124,7 +123,7 @@ def _filtered_y_array( y_array 1D array-like of y values. x_array - 1D array-like of x values (same length as ``y_array``). + 1D array-like of x values (same length as ``y_array``). x_min Minimum x limit (or ``None`` to use default). x_max @@ -132,7 +131,8 @@ def _filtered_y_array( Returns ------- - Filtered ``y_array`` values where ``x_array`` lies within ``[x_min, x_max]``. + Filtered ``y_array`` values where ``x_array`` lies within + ``[x_min, x_max]``. """ if x_min is None: x_min = self.x_min @@ -172,7 +172,7 @@ def _prepare_powder_data( expt_name Experiment name for error messages. expt_type - Experiment type with sample_form, scattering, and beam enums. + Experiment type with sample_form, scattering, and beam enums. x_min Optional minimum x-axis limit. x_max @@ -188,7 +188,9 @@ def _prepare_powder_data( Returns ------- - A dict with keys ``x_filtered``, ``y_series``, ``y_labels``, ``axes_labels``, and ``x_axis``; or ``None`` when a required array is missing. + A dict with keys ``x_filtered``, ``y_series``, ``y_labels``, + ``axes_labels``, and ``x_axis``; or ``None`` when a required + array is missing. """ x_axis, x_name, sample_form, scattering_type, _ = self._resolve_x_axis(expt_type, x) @@ -250,13 +252,15 @@ def _resolve_x_axis(self, expt_type, x): Parameters ---------- expt_type - Experiment type with sample_form, scattering_type, and beam_mode enums. + Experiment type with sample_form, scattering_type, and beam_mode + enums. x Explicit x-axis type or ``None`` to auto-detect. Returns ------- - Tuple of ``(x_axis, x_name, sample_form, scattering_type, beam_mode)``. + Tuple of ``(x_axis, x_name, sample_form, scattering_type, + beam_mode)``. """ sample_form = expt_type.sample_form.value scattering_type = expt_type.scattering_type.value @@ -279,7 +283,6 @@ def x_min(self, value): """ Set the minimum x-axis limit. - Parameters ---------- value @@ -300,7 +303,6 @@ def x_max(self, value): """ Set the maximum x-axis limit. - Parameters ---------- value @@ -321,7 +323,6 @@ def height(self, value): """ Set plot height. - Parameters ---------- value @@ -366,7 +367,8 @@ def plot_meas( Parameters ---------- pattern - Object with x-axis arrays (``two_theta``, ``time_of_flight``, ``d_spacing``) and ``meas`` array. + Object with x-axis arrays (``two_theta``, ``time_of_flight``, + ``d_spacing``) and ``meas`` array. expt_name Experiment name for the title. expt_type @@ -376,7 +378,8 @@ def plot_meas( x_max Optional maximum x-axis limit. x - X-axis type (``'two_theta'``, ``'time_of_flight'``, or ``'d_spacing'``). If ``None``, auto-detected from beam mode. + X-axis type (``'two_theta'``, ``'time_of_flight'``, or + ``'d_spacing'``). If ``None``, auto-detected from beam mode. """ ctx = self._prepare_powder_data( pattern, @@ -414,7 +417,8 @@ def plot_calc( Parameters ---------- pattern - Object with x-axis arrays (``two_theta``, ``time_of_flight``, ``d_spacing``) and ``calc`` array. + Object with x-axis arrays (``two_theta``, ``time_of_flight``, + ``d_spacing``) and ``calc`` array. expt_name Experiment name for the title. expt_type @@ -424,7 +428,8 @@ def plot_calc( x_max Optional maximum x-axis limit. x - X-axis type (``'two_theta'``, ``'time_of_flight'``, or ``'d_spacing'``). If ``None``, auto-detected from beam mode. + X-axis type (``'two_theta'``, ``'time_of_flight'``, or + ``'d_spacing'``). If ``None``, auto-detected from beam mode. """ ctx = self._prepare_powder_data( pattern, @@ -462,13 +467,11 @@ def plot_meas_vs_calc( Supports both powder and single crystal data with a unified API. - For powder diffraction: - x='two_theta', 'time_of_flight', - or 'd_spacing' - Auto-detected from beam mode if not - specified + For powder diffraction: - x='two_theta', 'time_of_flight', or + 'd_spacing' - Auto-detected from beam mode if not specified - For single crystal diffraction: - x='intensity_calc' - (default): scatter plot - x='d_spacing' or - 'sin_theta_over_lambda': line plot + For single crystal diffraction: - x='intensity_calc' (default): scatter + plot - x='d_spacing' or 'sin_theta_over_lambda': line plot Parameters ---------- @@ -477,15 +480,16 @@ def plot_meas_vs_calc( expt_name Experiment name for the title. expt_type - Experiment type with sample_form, scattering, and beam enums. + Experiment type with sample_form, scattering, and beam enums. x_min Optional minimum x-axis limit. x_max Optional maximum x-axis limit. show_residual - If ``True``, add residual series (powder only). + If ``True``, add residual series (powder only). x - X-axis type. If ``None``, auto-detected from sample form and beam mode. + X-axis type. If ``None``, auto-detected from sample form and + beam mode. """ x_axis, _, sample_form, scattering_type, _ = self._resolve_x_axis(expt_type, x) diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index b5d73f85..00daed6f 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -37,7 +37,6 @@ def _format_value(self, value: Any) -> Any: """ Format floats with fixed precision and others as strings. - Parameters ---------- value @@ -45,9 +44,8 @@ def _format_value(self, value: Any) -> Any: Returns ------- - - A string representation with fixed precision for - floats or ``str(value)`` for other types. + A string representation with fixed precision for floats or + ``str(value)`` for other types. """ return self._float_fmt(value) if isinstance(value, float) else str(value) @@ -71,17 +69,14 @@ def _rich_to_hex(self, color): """ Convert a Rich color name to a CSS-style hex string. - Parameters ---------- color Rich color name or specification parsable by - - mod:`rich`. + mod:`rich`. Returns ------- - Hex color string in the form ``#RRGGBB``. """ c = Color.parse(color) @@ -112,7 +107,8 @@ def render( Parameters ---------- alignments - Iterable of column justifications (e.g., ``'left'`` or ``'center'``) corresponding to the data columns. + Iterable of column justifications (e.g., ``'left'`` or + ``'center'``) corresponding to the data columns. df Index-aware DataFrame with data to render. display_handle diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index dc65422c..b4d5eaa2 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -28,7 +28,8 @@ def _build_base_styles(self, color: str) -> list[dict]: Parameters ---------- color - CSS color value (e.g., ``#RRGGBB``) to use for borders and header accents. + CSS color value (e.g., ``#RRGGBB``) to use for borders and + header accents. Returns ------- @@ -88,7 +89,8 @@ def _build_header_alignment_styles(self, df, alignments) -> list[dict]: df DataFrame whose columns are being rendered. alignments - Iterable of text alignment values (e.g., ``'left'``, ``'center'``) matching ``df`` columns. + Iterable of text alignment values (e.g., ``'left'``, + ``'center'``) matching ``df`` columns. Returns ------- @@ -137,9 +139,9 @@ def _update_display(self, styler, display_handle) -> None: """ Single, consistent update path for Jupyter. - If a handle with ``update()`` is provided and it's a - DisplayHandle, update the output area in-place using HTML. - Otherwise, display once via IPython ``display()``. + If a handle with ``update()`` is provided and it's a DisplayHandle, + update the output area in-place using HTML. Otherwise, display once via + IPython ``display()``. Parameters ---------- @@ -182,7 +184,8 @@ def render( df DataFrame whose index is displayed as the first column. display_handle - Optional IPython DisplayHandle to update an existing output area in place when running in Jupyter. + Optional IPython DisplayHandle to update an existing output area + in place when running in Jupyter. """ color = self._pandas_border_color styler = self._apply_styling(df, alignments, color) diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index 7fa615ef..5c8bbf2a 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -47,7 +47,6 @@ def _to_html(self, table: Table) -> str: A fresh ``Console(record=True, file=StringIO())`` avoids private attribute access and guarantees no visible output in notebooks. - Parameters ---------- table @@ -55,9 +54,7 @@ def _to_html(self, table: Table) -> str: Returns ------- - - HTML string with inline styles for notebook - display. + HTML string with inline styles for notebook display. """ tmp = Console(force_jupyter=False, record=True, file=io.StringIO()) tmp.print(table) @@ -73,7 +70,6 @@ def _build_table(self, df, alignments, color: str) -> Table: """ Construct a Rich Table with formatted data and alignment. - Parameters ---------- df @@ -85,9 +81,8 @@ def _build_table(self, df, alignments, color: str) -> Table: Returns ------- - A - class:`~rich.table.Table` configured for - display. + A + class:`~rich.table.Table` configured for display. """ table = Table( title=None, @@ -115,18 +110,18 @@ def _update_display(self, table: Table, display_handle) -> None: """ Single, consistent update path for Jupyter and terminal. - - With a handle that has ``update()``: * If it's an IPython - DisplayHandle, export to HTML and update. * Otherwise, - treat it as a terminal/live-like handle and update with the - Rich renderable. - Without a handle, print once to the shared - console. + - With a handle that has ``update()``: * If it's an IPython + DisplayHandle, export to HTML and update. * Otherwise, treat it as a + terminal/live-like handle and update with the Rich renderable. - Without + a handle, print once to the shared console. Parameters ---------- table Rich :class:`~rich.table.Table` to display. display_handle - Optional environment-specific handle for in- place updates (IPython or terminal live). + Optional environment-specific handle for in- place updates + (IPython or terminal live). """ # Handle with update() method if display_handle is not None and hasattr(display_handle, 'update'): @@ -167,7 +162,7 @@ def render( df Index-aware DataFrame to render. display_handle - Optional environment handle for in-place updates. + Optional environment handle for in-place updates. """ color = self._rich_border_color table = self._build_table(df, alignments, color) diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index e2a2084d..d12c7dbd 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -72,9 +72,12 @@ def render(self, df, display_handle: Any | None = None) -> Any: Parameters ---------- df - DataFrame with a two-level column index where the second level provides per-column alignment. + DataFrame with a two-level column index where the second level + provides per-column alignment. display_handle - Optional environment-specific handle used to update an existing output area in-place (e.g., an IPython DisplayHandle or a terminal live handle). + Optional environment-specific handle used to update an existing + output area in-place (e.g., an IPython DisplayHandle or a + terminal live handle). Returns ------- diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index b6f0590c..295fcfb9 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -19,7 +19,6 @@ def __init__(self, project) -> None: """ Initialize the summary with a reference to the project. - Parameters ---------- project diff --git a/src/easydiffraction/utils/environment.py b/src/easydiffraction/utils/environment.py index e773b6df..b3308915 100644 --- a/src/easydiffraction/utils/environment.py +++ b/src/easydiffraction/utils/environment.py @@ -20,7 +20,6 @@ def in_pycharm() -> bool: """ Determines if the current environment is PyCharm. - Returns ------- bool @@ -33,7 +32,6 @@ def in_colab() -> bool: """ Determines if the current environment is Google Colab. - Returns ------- bool @@ -49,12 +47,10 @@ def in_jupyter() -> bool: """ Return True when running inside a Jupyter Notebook. - Returns ------- bool - True if inside a Jupyter Notebook, False - otherwise. + True if inside a Jupyter Notebook, False otherwise. """ try: import IPython # type: ignore[import-not-found] @@ -92,12 +88,10 @@ def in_github_ci() -> bool: """ Return True when running under GitHub Actions CI. - Returns ------- bool - True if env var ``GITHUB_ACTIONS`` is set, False - otherwise. + True if env var ``GITHUB_ACTIONS`` is set, False otherwise. """ return os.environ.get('GITHUB_ACTIONS') is not None diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index 7aa7e975..10478a27 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -97,12 +97,10 @@ def _detect_width() -> int: """ Detect a suitable console width for the shared Console. - Returns ------- - - The detected terminal width, clamped at - ``_MIN_CONSOLE_WIDTH`` to avoid cramped layouts. + The detected terminal width, clamped at ``_MIN_CONSOLE_WIDTH`` + to avoid cramped layouts. """ min_width = ConsoleManager._MIN_CONSOLE_WIDTH try: @@ -223,7 +221,6 @@ def install_verbose_hook(logger: logging.Logger) -> None: """ Install a verbose exception hook that prints rich tracebacks. - Parameters ---------- logger @@ -256,7 +253,6 @@ def install_compact_hook(logger: logging.Logger) -> None: """ Install a compact exception hook that logs message-only. - Parameters ---------- logger @@ -285,9 +281,7 @@ def restore_original_hook(): @staticmethod def _suppress_traceback(logger): """ - Build a Jupyter custom exception callback that logs only the - message. - + Build a Jupyter custom exception callback that logs only the message. Parameters ---------- @@ -296,10 +290,8 @@ def _suppress_traceback(logger): Returns ------- - - A callable suitable for IPython's set_custom_exc - that suppresses full tracebacks and logs only the exception - message. + A callable suitable for IPython's set_custom_exc that suppresses + full tracebacks and logs only the exception message. """ def suppress_jupyter_traceback(*args, **kwargs): @@ -317,9 +309,8 @@ def suppress_jupyter_traceback(*args, **kwargs): @staticmethod def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: """ - Install a Jupyter/IPython custom exception handler that - suppresses tracebacks. - + Install a Jupyter/IPython custom exception handler that suppresses + tracebacks. Parameters ---------- diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 37fbf231..0259fc26 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -33,7 +33,6 @@ def _validate_url(url: str) -> None: """ Validate that a URL uses only safe HTTP/HTTPS schemes. - Parameters ---------- url @@ -99,21 +98,20 @@ def _fetch_data_index() -> dict: @functools.lru_cache(maxsize=1) def _fetch_tutorials_index() -> dict: """ - Fetch & cache the tutorials index.json from gh-pages and return - it as dict. + Fetch & cache the tutorials index.json from gh-pages and return it as + dict. The index is fetched from: https://easyscience.github.io/diffraction-lib/{version}/tutorials/index.json - For released versions, {version} is the public version string - (e.g., '0.8.0.post1'). For development versions, 'dev' is used. - + For released versions, {version} is the public version string (e.g., + '0.8.0.post1'). For development versions, 'dev' is used. Returns ------- dict - The tutorials index as a dictionary, or empty dict if - fetch fails. + The tutorials index as a dictionary, or empty dict if fetch + fails. """ version = _get_version_for_url() index_url = f'https://easyscience.github.io/diffraction-lib/{version}/tutorials/index.json' @@ -136,17 +134,16 @@ def download_data( overwrite: bool = False, ) -> str: """ - Download a dataset by numeric ID using the remote diffraction - index. + Download a dataset by numeric ID using the remote diffraction index. - Example: path = download_data(id=12, destination="data") + Example: path = download_data(id=12, destination="data") Parameters ---------- id Numeric dataset id (e.g. 12). destination - Directory to save the file into (created if missing). + Directory to save the file into (created if missing). overwrite Whether to overwrite the file if it already exists. @@ -158,6 +155,7 @@ def download_data( Raises ------ KeyError + If the id is not found in the index. ValueError If the resolved URL is not HTTP/HTTPS. """ @@ -215,7 +213,6 @@ def package_version(package_name: str) -> str | None: """ Get the installed version string of the specified package. - Parameters ---------- package_name : str @@ -224,9 +221,8 @@ def package_version(package_name: str) -> str | None: Returns ------- str | None - The raw version string (may include local - part, e.g., '1.2.3+abc123'), or None if the package is not - installed. + The raw version string (may include local part, e.g., + '1.2.3+abc123'), or None if the package is not installed. """ try: return version(package_name) @@ -236,13 +232,12 @@ def package_version(package_name: str) -> str | None: def stripped_package_version(package_name: str) -> str | None: """ - Get the installed version of the specified package, stripped of - any local version part. + Get the installed version of the specified package, stripped of any + local version part. Returns only the public version segment (e.g., '1.2.3' or '1.2.3.post4'), omitting any local segment (e.g., '+d136'). - Parameters ---------- package_name : str @@ -251,8 +246,8 @@ def stripped_package_version(package_name: str) -> str | None: Returns ------- str | None - The public version string, or None if the - package is not installed. + The public version string, or None if the package is not + installed. """ v_str = package_version(package_name) if v_str is None: @@ -266,13 +261,11 @@ def stripped_package_version(package_name: str) -> str | None: def _is_dev_version(package_name: str) -> bool: """ - Check if the installed package version is a development/local - version. + Check if the installed package version is a development/local version. A version is considered "dev" if: - The raw version contains '+dev', - '+dirty', or '+devdirty' (local suffixes from versioningit) - The - public version is '999.0.0' (versioningit default-tag fallback) - + '+dirty', or '+devdirty' (local suffixes from versioningit) - The public + version is '999.0.0' (versioningit default-tag fallback) Parameters ---------- @@ -282,8 +275,7 @@ def _is_dev_version(package_name: str) -> bool: Returns ------- bool - True if the version is a development version, - False otherwise. + True if the version is a development version, False otherwise. """ raw_version = package_version(package_name) if raw_version is None: @@ -300,13 +292,11 @@ def _is_dev_version(package_name: str) -> bool: def _get_version_for_url(package_name: str = 'easydiffraction') -> str: """ - Get the version string to use in URLs for fetching remote - resources. + Get the version string to use in URLs for fetching remote resources. Returns the public version for released versions, or 'dev' for development/local versions. - Parameters ---------- package_name : str @@ -315,8 +305,8 @@ def _get_version_for_url(package_name: str = 'easydiffraction') -> str: Returns ------- str - The version string to use in URLs ('dev' or a - version like '0.8.0.post1'). + The version string to use in URLs ('dev' or a version like + '0.8.0.post1'). """ if _is_dev_version(package_name): return 'dev' @@ -342,8 +332,7 @@ def _safe_urlopen(request_or_url): # type: ignore[no-untyped-def] def _resolve_tutorial_url(url_template: str) -> str: """ - Replace {version} placeholder in URL template with actual - version. + Replace {version} placeholder in URL template with actual version. Parameters ---------- @@ -400,14 +389,14 @@ def download_tutorial( """ Download a tutorial notebook by numeric ID. - Example: path = download_tutorial(id=1, destination="tutorials") + Example: path = download_tutorial(id=1, destination="tutorials") Parameters ---------- id Numeric tutorial id (e.g. 1). destination - Directory to save the file into (created if missing). + Directory to save the file into (created if missing). overwrite Whether to overwrite the file if it already exists. @@ -419,6 +408,7 @@ def download_tutorial( Raises ------ KeyError + If the id is not found in the index. ValueError If the resolved URL is not HTTP/HTTPS. """ @@ -474,7 +464,7 @@ def download_all_tutorials( """ Download all available tutorial notebooks. - Example: paths = download_all_tutorials(destination="tutorials") + Example: paths = download_all_tutorials(destination="tutorials") Parameters ---------- @@ -513,10 +503,7 @@ def download_all_tutorials( def show_version() -> None: - """Print the installed version of the easydiffraction package. - - Args: None - """ + """Print the installed version of the easydiffraction package.""" current_ed_version = package_version('easydiffraction') console.print(f'Current easydiffraction v{current_ed_version}') @@ -543,7 +530,6 @@ def render_cif(cif_text) -> None: Display the CIF text as a formatted table in Jupyter Notebook or terminal. - Parameters ---------- cif_text @@ -571,21 +557,19 @@ def tof_to_d( quad_eps=1e-20, ) -> np.ndarray: """ - Convert time-of-flight (TOF) to d-spacing using a quadratic - calibration. + Convert time-of-flight (TOF) to d-spacing using a quadratic calibration. - Model: TOF = offset + linear * d + quad * d² + Model: TOF = offset + linear * d + quad * d² - The function: - Uses a linear fallback when the quadratic term is - effectively zero. - Solves the quadratic for d and selects the - smallest positive, finite root. - Returns NaN where no valid - solution exists. - Expects ``tof`` as a NumPy array; output - matches its shape. + The function: - Uses a linear fallback when the quadratic term is + effectively zero. - Solves the quadratic for d and selects the smallest + positive, finite root. - Returns NaN where no valid solution exists. - + Expects ``tof`` as a NumPy array; output matches its shape. Parameters ---------- tof : np.ndarray - Time-of-flight values (µs). Must be a NumPy array. + Time-of-flight values (µs). Must be a NumPy array. offset : float Calibration offset (µs). linear : float @@ -603,7 +587,8 @@ def tof_to_d( Raises ------ TypeError - If ``tof`` is not a NumPy array or coefficients are not real numbers. + If ``tof`` is not a NumPy array or coefficients are not real + numbers. """ # Type checks if not isinstance(tof, np.ndarray): @@ -661,9 +646,8 @@ def twotheta_to_d(twotheta, wavelength): """ Convert 2-theta to d-spacing using Bragg's law. - Parameters: twotheta (float or np.ndarray): 2-theta angle in - degrees. wavelength (float): Wavelength in Å. - + Parameters: twotheta (float or np.ndarray): 2-theta angle in degrees. + wavelength (float): Wavelength in Å. Returns ------- @@ -683,9 +667,8 @@ def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda): """ Convert sin(theta)/lambda to d-spacing. - Parameters: sin_theta_over_lambda (float or np.ndarray): - sin(theta)/lambda in 1/Å. - + Parameters: sin_theta_over_lambda (float or np.ndarray): + sin(theta)/lambda in 1/Å. Returns ------- @@ -705,9 +688,8 @@ def get_value_from_xye_header(file_path, key): Extracts a floating point value from the first line of the file, corresponding to the given key. - Parameters: file_path (str): Path to the input file. key - (str): The key to extract ('DIFC' or 'two_theta'). - + Parameters: file_path (str): Path to the input file. key (str): The key + to extract ('DIFC' or 'two_theta'). Returns ------- @@ -736,25 +718,25 @@ def str_to_ufloat(s: Optional[str], default: Optional[float] = None) -> UFloat: Parse a CIF-style numeric string into a `ufloat` with an optional uncertainty. - Examples of supported input: - "3.566" → ufloat(3.566, nan) - - "3.566(2)" → ufloat(3.566, 0.002) - None → - ufloat(default, nan) + Examples of supported input: - "3.566" → ufloat(3.566, nan) - "3.566(2)" + → ufloat(3.566, 0.002) - None → ufloat(default, nan) - Behavior: - If the input string contains a value with parentheses - (e.g. "3.566(2)"), the number in parentheses is interpreted as an - estimated standard deviation (esd) in the last digit(s). - If the - input string has no parentheses, an uncertainty of NaN is assigned - to indicate "no esd provided". - If parsing fails, the function - falls back to the given `default` value with uncertainty NaN. + Behavior: - If the input string contains a value with parentheses (e.g. + "3.566(2)"), the number in parentheses is interpreted as an estimated + standard deviation (esd) in the last digit(s). - If the input string has + no parentheses, an uncertainty of NaN is assigned to indicate "no esd + provided". - If parsing fails, the function falls back to the given + `default` value with uncertainty NaN. - Parameters ---------- s : str or None Numeric string in CIF - format (e.g. "3.566", "3.566(2)") or None. default : float or None, - optional Default value to use if `s` is None or parsing fails. - Defaults to None. + Parameters ---------- s : str or None Numeric string in CIF format (e.g. + "3.566", "3.566(2)") or None. default : float or None, optional Default + value to use if `s` is None or parsing fails. Defaults to None. Returns ------- - ------- UFloat An `uncertainties.UFloat` object with the parsed value and uncertainty. The uncertainty will be NaN if not specified or parsing failed. + ------- UFloat An `uncertainties.UFloat` object with the parsed + value and uncertainty. The uncertainty will be NaN if not + specified or parsing failed. """ if s is None: return ufloat(default, np.nan) diff --git a/tools/convert_google_docstrings_to_numpy.py b/tools/convert_google_docstrings_to_numpy.py index fec28853..5fcb14e2 100644 --- a/tools/convert_google_docstrings_to_numpy.py +++ b/tools/convert_google_docstrings_to_numpy.py @@ -7,6 +7,7 @@ import inspect import re import sys +import textwrap from pathlib import Path from docstring_parser import DocstringStyle @@ -32,6 +33,7 @@ + '|'.join(SECTION_NAMES) + r'):\s*(?P\S.*)?$' ) +NUMPY_SECTION_RE = re.compile(r'(?m)^[^\n]+\n-+\n') SECTION_KINDS_WITH_ITEMS = {'Args', 'Arguments', 'Attributes'} PRESERVE_BLOCK_SECTIONS = {'Examples', 'Notes'} GENERIC_ITEM_SECTIONS = {'Raises', 'Returns', 'Yields'} @@ -106,8 +108,21 @@ def _strip_blank_edges(lines: list[str]) -> list[str]: return lines[start:end] +def _join_wrapped_lines(lines: list[str]) -> str: + parts: list[str] = [] + for line in lines: + text = re.sub(r'\s+', ' ', line.strip()) + if not text: + continue + if parts and parts[-1].endswith('-') and not parts[-1].endswith(' -'): + parts[-1] = parts[-1][:-1] + text + else: + parts.append(text) + return ' '.join(parts) + + def _collapse_whitespace(lines: list[str]) -> str: - return ' '.join(line.strip() for line in lines if line.strip()) + return _join_wrapped_lines(lines) def _repair_named_items(block_lines: list[str], names: list[str]) -> list[str] | None: @@ -156,6 +171,9 @@ def _repair_section(section: str, block_lines: list[str], names: list[str]) -> l return [] if section in SECTION_KINDS_WITH_ITEMS: + flat = _collapse_whitespace(stripped).lower().rstrip('.') + if flat == 'none': + return [] repaired = _repair_named_items(stripped, names) if repaired is not None: return repaired @@ -220,6 +238,10 @@ def _looks_google(docstring: str) -> bool: return bool(GOOGLE_SECTION_RE.search(docstring)) +def _looks_numpydoc(docstring: str) -> bool: + return bool(NUMPY_SECTION_RE.search(docstring)) + + def _meta_kinds(parsed) -> set[str]: kinds: set[str] = set() for meta in parsed.meta: @@ -265,11 +287,147 @@ def _is_safe_conversion(docstring: str, parsed) -> bool: return True -def _tidy_numpydoc_output(docstring: str) -> str: - tidied = docstring.strip('\n') - tidied = re.sub(r'\n{3,}', '\n\n', tidied) - tidied = re.sub(r'(?m)^([^\n]+)\n(-+)\n\n( +\S)', r'\1\n\2\n\3', tidied) - return tidied +def _is_section_header(lines: list[str], index: int) -> bool: + return index + 1 < len(lines) and bool(lines[index].strip()) and set(lines[index + 1].strip()) == {'-'} + + +def _wrap_paragraph(lines: list[str], width: int, indent: str = '') -> list[str]: + if not lines: + return [] + + text = _join_wrapped_lines(lines) + if not text: + return [''] if lines else [] + + return textwrap.wrap( + text, + width=width, + initial_indent=indent, + subsequent_indent=indent, + break_long_words=False, + break_on_hyphens=False, + ) + + +def _format_freeform_block(lines: list[str], width: int = 72, indent: str = '') -> list[str]: + stripped = _strip_blank_edges(lines) + if not stripped: + return [] + + formatted: list[str] = [] + paragraph: list[str] = [] + for line in stripped: + if not line.strip(): + if paragraph: + formatted.extend(_wrap_paragraph(paragraph, width=width, indent=indent)) + paragraph = [] + if formatted and formatted[-1] != '': + formatted.append('') + continue + + content = line.strip() + if content.startswith(('>>>', '...')): + if paragraph: + formatted.extend(_wrap_paragraph(paragraph, width=width, indent=indent)) + paragraph = [] + formatted.append(f'{indent}{content}') + continue + + paragraph.append(content) + + if paragraph: + formatted.extend(_wrap_paragraph(paragraph, width=width, indent=indent)) + + return formatted + + +def _format_named_section(block_lines: list[str]) -> list[str]: + lines = _strip_blank_edges(block_lines) + if not lines: + return [] + + formatted: list[str] = [] + index = 0 + while index < len(lines): + if not lines[index].strip(): + index += 1 + continue + + header = lines[index].strip() + formatted.append(header) + index += 1 + + description: list[str] = [] + while index < len(lines): + line = lines[index] + if not line.strip(): + index += 1 + if description: + break + continue + if not line.startswith(' ') and not line.startswith('\t'): + break + description.append(line.strip()) + index += 1 + + if description: + formatted.extend(_wrap_paragraph(description, width=68, indent=' ')) + elif formatted and formatted[-1] != '': + formatted.append('') + + if formatted and formatted[-1] == '': + formatted.pop() + return formatted + + +def _format_return_like_section(block_lines: list[str]) -> list[str]: + lines = _strip_blank_edges(block_lines) + if not lines: + return [] + + first = next((line for line in lines if line.strip()), '') + if first.startswith((' ', '\t')): + return _format_freeform_block(lines, width=68, indent=' ') + + return _format_named_section(lines) + + +def _format_numpydoc_output(docstring: str) -> str: + lines = docstring.strip('\n').splitlines() + formatted: list[str] = [] + index = 0 + + preamble: list[str] = [] + while index < len(lines) and not _is_section_header(lines, index): + preamble.append(lines[index]) + index += 1 + formatted.extend(_format_freeform_block(preamble)) + + while index < len(lines): + if not _is_section_header(lines, index): + index += 1 + continue + + if formatted and formatted[-1] != '': + formatted.append('') + heading = lines[index].strip() + underline = lines[index + 1].strip() + formatted.extend([heading, underline]) + index += 2 + + block: list[str] = [] + while index < len(lines) and not _is_section_header(lines, index): + block.append(lines[index]) + index += 1 + + if heading in {'Parameters', 'Attributes'}: + formatted.extend(_format_named_section(block)) + elif heading in {'Returns', 'Raises', 'Yields'}: + formatted.extend(_format_return_like_section(block)) + else: + formatted.extend(_format_freeform_block(block)) + + return '\n'.join(_strip_blank_edges(formatted)) def _convert_docstring(docstring: str, names: list[str]) -> str | None: @@ -287,10 +445,19 @@ def _convert_docstring(docstring: str, names: list[str]) -> str | None: if not _is_safe_conversion(repaired, parsed): return None - converted = _tidy_numpydoc_output(compose(parsed, style=DocstringStyle.NUMPYDOC)) + converted = _format_numpydoc_output(compose(parsed, style=DocstringStyle.NUMPYDOC)) return converted if converted != cleaned else None +def _reformat_numpydoc_docstring(docstring: str) -> str | None: + cleaned = inspect.cleandoc(docstring) + if not _looks_numpydoc(cleaned): + return None + + formatted = _format_numpydoc_output(cleaned) + return formatted if formatted != cleaned else None + + def _format_multiline_docstring(content: str, indent: int) -> str: indent_str = ' ' * indent lines = content.strip('\n').splitlines() @@ -326,6 +493,8 @@ def _convert_file(path: Path) -> bool: continue converted = _convert_docstring(docstring, _collect_names(node)) + if converted is None: + converted = _reformat_numpydoc_docstring(docstring) if converted is None: continue From 71973cc0a13d55421c2766c04b22b98b6f03c80d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 13:18:29 +0100 Subject: [PATCH 26/48] Enable ANN rules --- .gitignore | 1 + pixi.lock | 4 +- pyproject.toml | 6 +- src/easydiffraction/__main__.py | 8 +- src/easydiffraction/analysis/analysis.py | 14 +-- .../analysis/calculators/cryspy.py | 6 +- .../analysis/calculators/pdffit.py | 6 +- .../analysis/categories/aliases/default.py | 2 +- .../categories/constraints/default.py | 4 +- .../joint_fit_experiments/default.py | 2 +- .../analysis/fit_helpers/reporting.py | 17 ++-- .../analysis/fit_helpers/tracking.py | 11 +-- src/easydiffraction/analysis/fitting.py | 4 +- .../analysis/minimizers/base.py | 40 ++++---- .../analysis/minimizers/dfols.py | 13 ++- .../analysis/minimizers/lmfit.py | 19 ++-- src/easydiffraction/core/category.py | 22 ++--- src/easydiffraction/core/collection.py | 21 ++-- src/easydiffraction/core/datablock.py | 18 ++-- src/easydiffraction/core/diagnostic.py | 78 +++++++-------- src/easydiffraction/core/factory.py | 30 +++--- src/easydiffraction/core/guard.py | 25 ++--- src/easydiffraction/core/identity.py | 16 ++-- src/easydiffraction/core/metadata.py | 10 +- src/easydiffraction/core/singleton.py | 8 +- src/easydiffraction/core/validation.py | 84 ++++++++-------- src/easydiffraction/core/variable.py | 59 ++++++------ .../crystallography/space_groups.py | 5 +- .../categories/background/chebyshev.py | 4 +- .../categories/background/line_segment.py | 4 +- .../experiment/categories/data/bragg_pd.py | 36 +++---- .../experiment/categories/data/bragg_sc.py | 20 ++-- .../experiment/categories/data/total_pd.py | 18 ++-- .../categories/excluded_regions/default.py | 6 +- .../categories/experiment_type/default.py | 2 +- .../categories/linked_phases/default.py | 4 +- .../experiment/categories/peak/cwl_mixins.py | 6 +- .../experiment/categories/peak/tof_mixins.py | 4 +- .../categories/peak/total_mixins.py | 2 +- .../datablocks/experiment/item/base.py | 30 +++--- .../datablocks/experiment/item/bragg_pd.py | 12 +-- .../datablocks/experiment/item/factory.py | 4 +- .../datablocks/experiment/item/total_pd.py | 4 +- .../datablocks/structure/item/factory.py | 2 +- src/easydiffraction/display/base.py | 5 +- src/easydiffraction/display/plotters/ascii.py | 30 +++--- src/easydiffraction/display/plotters/base.py | 28 +++--- .../display/plotters/plotly.py | 66 ++++++------- src/easydiffraction/display/plotting.py | 96 +++++++++---------- src/easydiffraction/display/tablers/base.py | 13 ++- src/easydiffraction/display/tablers/pandas.py | 16 ++-- src/easydiffraction/display/tablers/rich.py | 13 ++- src/easydiffraction/display/tables.py | 3 +- src/easydiffraction/io/cif/handler.py | 2 +- src/easydiffraction/io/cif/serialize.py | 24 ++--- src/easydiffraction/project/project.py | 50 +++++----- src/easydiffraction/project/project_info.py | 4 +- src/easydiffraction/summary/summary.py | 2 +- src/easydiffraction/utils/logging.py | 16 ++-- src/easydiffraction/utils/utils.py | 22 ++--- 60 files changed, 538 insertions(+), 543 deletions(-) diff --git a/.gitignore b/.gitignore index 0500ede3..6dc595c7 100644 --- a/.gitignore +++ b/.gitignore @@ -41,5 +41,6 @@ CMakeLists.txt.user* *.dmg # Misc +.cache/ *.log *.zip diff --git a/pixi.lock b/pixi.lock index 63fe6698..32b17e23 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty23 - sha256: 39c132bdffe72192390de6d182198d9531506a7e745e26a1a274b6a807de449f + version: 0.10.2+devdirty29 + sha256: f79ed38b71757cf1a1590713918d3cf0ecf291a1b756edd7c0030cd3981857af requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index cbcd46cd..c650a466 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -237,7 +237,7 @@ select = [ #'PLW', # https://docs.astral.sh/ruff/rules/#warning-plw # flake8 rules #'A', # https://docs.astral.sh/ruff/rules/#flake8-builtins-a - #'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann 'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg #'ASYNC', # https://docs.astral.sh/ruff/rules/#flake8-async-async 'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b @@ -280,6 +280,7 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc # Temporary: 'DTZ005', + 'W505' ] @@ -368,8 +369,9 @@ allow-init-docstring = true # https://github.com/jsh9/format-docstring [tool.format_docstring] +#exclude = '\.' # Temporarily disable format-docstring until we are ready +exclude = 'src/easydiffraction/utils/_vendored/jupyter_dark_detect' # ED only docstring_style = 'numpy' line_length = 72 fix_rst_backticks = true -exclude = 'src/easydiffraction/utils/_vendored/jupyter_dark_detect' # ED only verbose = 'diff' diff --git a/src/easydiffraction/__main__.py b/src/easydiffraction/__main__.py index 33fb4dcd..1a058dd6 100644 --- a/src/easydiffraction/__main__.py +++ b/src/easydiffraction/__main__.py @@ -25,7 +25,7 @@ def main( help='Show easydiffraction version and exit.', is_eager=True, ), -): +) -> None: """EasyDiffraction command-line interface.""" if version: ed.show_version() @@ -38,7 +38,7 @@ def main( @app.command('list-tutorials') -def list_tutorials(): +def list_tutorials() -> None: """List available tutorial notebooks.""" ed.list_tutorials() @@ -58,7 +58,7 @@ def download_tutorial( '-o', help='Overwrite existing file if present.', ), -): +) -> None: """Download a specific tutorial notebook by ID.""" ed.download_tutorial(id=id, destination=destination, overwrite=overwrite) @@ -77,7 +77,7 @@ def download_all_tutorials( '-o', help='Overwrite existing files if present.', ), -): +) -> None: """Download all available tutorial notebooks.""" ed.download_all_tutorials(destination=destination, overwrite=overwrite) diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index ab24f879..cd188df0 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -48,7 +48,7 @@ class Analysis: computations. fitter: Active fitter/minimizer driver. """ - def __init__(self, project) -> None: + def __init__(self, project: object) -> None: """ Create a new Analysis instance bound to a project. @@ -515,7 +515,7 @@ def current_minimizer(self, selection: str) -> None: # ------------------------------------------------------------------ @property - def fit_mode(self): + def fit_mode(self) -> object: """Fit-mode category item holding the active strategy.""" return self._fit_mode @@ -561,7 +561,7 @@ def show_current_fit_mode_type(self) -> None: # ------------------------------------------------------------------ @property - def joint_fit_experiments(self): + def joint_fit_experiments(self) -> object: """Per-experiment weight collection for joint fitting.""" return self._joint_fit_experiments @@ -593,7 +593,7 @@ def show_constraints(self) -> None: columns_data=rows, ) - def apply_constraints(self): + def apply_constraints(self) -> None: """Apply the currently defined constraints to the active project. """ @@ -605,7 +605,7 @@ def apply_constraints(self): self.constraints_handler.set_constraints(self.constraints) self.constraints_handler.apply() - def fit(self): + def fit(self) -> None: """Execute fitting using the selected mode, calculator and minimizer. @@ -701,7 +701,7 @@ def show_fit_results(self) -> None: self.fitter._process_fit_results(structures, experiments) - def _update_categories(self, called_by_minimizer=False) -> None: + def _update_categories(self, called_by_minimizer: bool = False) -> None: """ Update all categories owned by Analysis. @@ -723,7 +723,7 @@ def _update_categories(self, called_by_minimizer=False) -> None: if hasattr(category, '_update'): category._update(called_by_minimizer=called_by_minimizer) - def as_cif(self): + def as_cif(self) -> str: """ Serialize the analysis section to a CIF string. diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 0b28da1d..829ce30c 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -60,7 +60,7 @@ def calculate_structure_factors( structure: Structure, experiment: ExperimentBase, called_by_minimizer: bool = False, - ): + ) -> None: """ Raises a NotImplementedError as HKL calculation is not implemented. @@ -316,7 +316,7 @@ def _recreate_cryspy_obj( self, structure: Structure, experiment: ExperimentBase, - ) -> Any: + ) -> object: """ Recreates the Cryspy object for the given structure and experiment. @@ -369,7 +369,7 @@ def _convert_structure_to_cryspy_cif( def _convert_experiment_to_cryspy_cif( self, experiment: ExperimentBase, - linked_structure: Any, + linked_structure: object, ) -> str: """ Converts an experiment to a Cryspy CIF string. diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py index ecb4ee8f..6fb0880a 100644 --- a/src/easydiffraction/analysis/calculators/pdffit.py +++ b/src/easydiffraction/analysis/calculators/pdffit.py @@ -51,10 +51,10 @@ class PdffitCalculator(CalculatorBase): engine_imported: bool = PdfFit is not None @property - def name(self): + def name(self) -> str: return 'pdffit' - def calculate_structure_factors(self, structures, experiments): + def calculate_structure_factors(self, structures: object, experiments: object) -> list: # PDF doesn't compute HKL but we keep interface consistent # Intentionally unused, required by public API/signature del structures, experiments @@ -66,7 +66,7 @@ def calculate_pattern( structure: Structure, experiment: ExperimentBase, called_by_minimizer: bool = False, - ): + ) -> None: # Intentionally unused, required by public API/signature del called_by_minimizer diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 35ae417b..146bd22f 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -100,6 +100,6 @@ class Aliases(CategoryCollection): description='Parameter alias mappings', ) - def __init__(self): + def __init__(self) -> None: """Create an empty collection of aliases.""" super().__init__(item_type=Alias) diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 685f1cd6..7f5468a5 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -100,11 +100,11 @@ class Constraints(CategoryCollection): _update_priority = 90 # After most others, but before data categories - def __init__(self): + def __init__(self) -> None: """Create an empty constraints collection.""" super().__init__(item_type=Constraint) - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: del called_by_minimizer constraints = ConstraintsHandler.get() diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index fc1b2a2b..edcc4283 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -100,6 +100,6 @@ class JointFitExperiments(CategoryCollection): description='Joint-fit experiment weights', ) - def __init__(self): + def __init__(self) -> None: """Create an empty joint-fit experiments collection.""" super().__init__(item_type=JointFitExperiment) diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index 65788cc3..64abb528 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -1,7 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -from typing import Any from typing import List from typing import Optional @@ -24,15 +23,15 @@ class FitResults: def __init__( self, success: bool = False, - parameters: Optional[List[Any]] = None, + parameters: Optional[List[object]] = None, chi_square: Optional[float] = None, reduced_chi_square: Optional[float] = None, message: str = '', iterations: int = 0, - engine_result: Optional[Any] = None, - starting_parameters: Optional[List[Any]] = None, + engine_result: Optional[object] = None, + starting_parameters: Optional[List[object]] = None, fitting_time: Optional[float] = None, - **kwargs: Any, + **kwargs: object, ) -> None: """ Initialize FitResults with the given parameters. @@ -63,14 +62,14 @@ def __init__( chi-square value. """ self.success: bool = success - self.parameters: List[Any] = parameters if parameters is not None else [] + self.parameters: List[object] = parameters if parameters is not None else [] self.chi_square: Optional[float] = chi_square self.reduced_chi_square: Optional[float] = reduced_chi_square self.message: str = message self.iterations: int = iterations - self.engine_result: Optional[Any] = engine_result - self.result: Optional[Any] = None - self.starting_parameters: List[Any] = ( + self.engine_result: Optional[object] = engine_result + self.result: Optional[object] = None + self.starting_parameters: List[object] = ( starting_parameters if starting_parameters is not None else [] ) self.fitting_time: Optional[float] = fitting_time diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index dafb35fa..2450a931 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -3,7 +3,6 @@ import time from contextlib import suppress -from typing import Any from typing import List from typing import Optional @@ -42,10 +41,10 @@ class _TerminalLiveHandle: the underlying UI mechanism. """ - def __init__(self, live) -> None: + def __init__(self, live: object) -> None: self._live = live - def update(self, renderable) -> None: + def update(self, renderable: object) -> None: self._live.update(renderable, refresh=True) def close(self) -> None: @@ -53,7 +52,7 @@ def close(self) -> None: self._live.stop() -def _make_display_handle() -> Any | None: +def _make_display_handle() -> object | None: """Create and initialize a display/update handle for the environment. @@ -93,8 +92,8 @@ def __init__(self) -> None: self._fitting_time: Optional[float] = None self._df_rows: List[List[str]] = [] - self._display_handle: Optional[Any] = None - self._live: Optional[Any] = None + self._display_handle: Optional[object] = None + self._live: Optional[object] = None def reset(self) -> None: """Reset internal state before a new optimization run.""" diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index 99ed4eee..5d2681fa 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -33,7 +33,7 @@ def fit( structures: Structures, experiments: Experiments, weights: Optional[np.array] = None, - analysis=None, + analysis: object = None, ) -> None: """ Run the fitting process. @@ -141,7 +141,7 @@ def _residual_function( structures: Structures, experiments: Experiments, weights: Optional[np.array] = None, - analysis=None, + analysis: object = None, ) -> np.ndarray: """ Residual function computes the difference between measured and diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index 5e47beae..7702a480 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -78,17 +78,17 @@ def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: @abstractmethod def _run_solver( self, - objective_function: Callable[..., Any], - engine_parameters: Dict[str, Any], - ) -> Any: + objective_function: Callable[..., object], + engine_parameters: Dict[str, object], + ) -> object: """Execute the concrete solver and return its raw result.""" pass @abstractmethod def _sync_result_to_parameters( self, - raw_result: Any, - parameters: List[Any], + raw_result: object, + parameters: List[object], ) -> None: """Copy values from ``raw_result`` back to ``parameters`` in- place. @@ -97,8 +97,8 @@ def _sync_result_to_parameters( def _finalize_fit( self, - parameters: List[Any], - raw_result: Any, + parameters: List[object], + raw_result: object, ) -> FitResults: """ Build :class:`FitResults` and store it on ``self.result``. @@ -128,14 +128,14 @@ def _finalize_fit( return self.result @abstractmethod - def _check_success(self, raw_result: Any) -> bool: + def _check_success(self, raw_result: object) -> bool: """Determine whether the fit was successful.""" pass def fit( self, - parameters: List[Any], - objective_function: Callable[..., Any], + parameters: List[object], + objective_function: Callable[..., object], ) -> FitResults: """ Run the full minimization workflow. @@ -169,11 +169,11 @@ def fit( def _objective_function( self, - engine_params: Dict[str, Any], - parameters: List[Any], - structures: Any, - experiments: Any, - calculator: Any, + engine_params: Dict[str, object], + parameters: List[object], + structures: object, + experiments: object, + calculator: object, ) -> np.ndarray: """Default objective helper computing residuals array.""" return self._compute_residuals( @@ -186,11 +186,11 @@ def _objective_function( def _create_objective_function( self, - parameters: List[Any], - structures: Any, - experiments: Any, - calculator: Any, - ) -> Callable[[Dict[str, Any]], np.ndarray]: + parameters: List[object], + structures: object, + experiments: object, + calculator: object, + ) -> Callable[[Dict[str, object]], np.ndarray]: """Return a closure capturing problem context for the solver.""" return lambda engine_params: self._objective_function( engine_params, diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index 45e42eba..9777ed83 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -1,7 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -from typing import Any from typing import Dict from typing import List @@ -30,13 +29,13 @@ def __init__( self, name: str = 'dfols', max_iterations: int = DEFAULT_MAX_ITERATIONS, - **kwargs: Any, + **kwargs: object, ) -> None: super().__init__(name=name, method=None, max_iterations=max_iterations) # Intentionally unused, accepted for API compatibility del kwargs - def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: + def _prepare_solver_args(self, parameters: List[object]) -> Dict[str, object]: x0 = [] bounds_lower = [] bounds_upper = [] @@ -47,15 +46,15 @@ def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: bounds = (np.array(bounds_lower), np.array(bounds_upper)) return {'x0': np.array(x0), 'bounds': bounds} - def _run_solver(self, objective_function: Any, **kwargs: Any) -> Any: + def _run_solver(self, objective_function: object, **kwargs: object) -> object: x0 = kwargs.get('x0') bounds = kwargs.get('bounds') return solve(objective_function, x0=x0, bounds=bounds, maxfun=self.max_iterations) def _sync_result_to_parameters( self, - parameters: List[Any], - raw_result: Any, + parameters: List[object], + raw_result: object, ) -> None: """ Synchronizes the result from the solver to the parameters. @@ -78,7 +77,7 @@ def _sync_result_to_parameters( # calculate later if needed param.uncertainty = None - def _check_success(self, raw_result: Any) -> bool: + def _check_success(self, raw_result: object) -> bool: """ Determines success from DFO-LS result dictionary. diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index d9cbaf2f..ed57100d 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -1,7 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -from typing import Any from typing import Dict from typing import List @@ -38,8 +37,8 @@ def __init__( def _prepare_solver_args( self, - parameters: List[Any], - ) -> Dict[str, Any]: + parameters: List[object], + ) -> Dict[str, object]: """ Prepares the solver arguments for the lmfit minimizer. @@ -63,7 +62,7 @@ def _prepare_solver_args( ) return {'engine_parameters': engine_parameters} - def _run_solver(self, objective_function: Any, **kwargs: Any) -> Any: + def _run_solver(self, objective_function: object, **kwargs: object) -> object: """ Runs the lmfit solver. @@ -90,8 +89,8 @@ def _run_solver(self, objective_function: Any, **kwargs: Any) -> Any: def _sync_result_to_parameters( self, - parameters: List[Any], - raw_result: Any, + parameters: List[object], + raw_result: object, ) -> None: """ Synchronizes the result from the solver to the parameters. @@ -113,7 +112,7 @@ def _sync_result_to_parameters( param._set_value_from_minimizer(param_result.value) param.uncertainty = getattr(param_result, 'stderr', None) - def _check_success(self, raw_result: Any) -> bool: + def _check_success(self, raw_result: object) -> bool: """ Determines success from lmfit MinimizerResult. @@ -132,9 +131,9 @@ def _iteration_callback( self, params: lmfit.Parameters, iter: int, - resid: Any, - *args: Any, - **kwargs: Any, + resid: object, + *args: object, + **kwargs: object, ) -> None: """ Callback function for each iteration of the minimizer. diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index 64579a70..d0773bfd 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -30,12 +30,12 @@ def __str__(self) -> str: return f'<{name} ({params})>' # TODO: Common for all categories - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: del called_by_minimizer pass @property - def unique_name(self): + def unique_name(self) -> str: parts = [ self._identity.datablock_entry_name, self._identity.category_code, @@ -46,7 +46,7 @@ def unique_name(self): return '.'.join(str_parts) @property - def parameters(self): + def parameters(self) -> list: return [v for v in vars(self).values() if isinstance(v, GenericDescriptorBase)] @property @@ -54,7 +54,7 @@ def as_cif(self) -> str: """Return CIF representation of this object.""" return category_item_to_cif(self) - def from_cif(self, block, idx=0): + def from_cif(self, block: object, idx: int = 0) -> None: """Populate this item from a CIF block.""" category_item_from_cif(self, block, idx) @@ -168,7 +168,7 @@ class CategoryCollection(CollectionBase): # TODO: Common for all categories _update_priority = 10 # Default. Lower values run first. - def _key_for(self, item): + def _key_for(self, item: object) -> str | None: """Return the category-level identity key for *item*.""" return item._identity.category_entry_name @@ -190,16 +190,16 @@ def __str__(self) -> str: return f'<{name} collection ({size} items)>' # TODO: Common for all categories - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: del called_by_minimizer pass @property - def unique_name(self): + def unique_name(self) -> str | None: return None @property - def parameters(self): + def parameters(self) -> list: """All parameters from all items in this collection.""" params = [] for item in self._items: @@ -211,11 +211,11 @@ def as_cif(self) -> str: """Return CIF representation of this object.""" return category_collection_to_cif(self) - def from_cif(self, block): + def from_cif(self, block: object) -> None: """Populate this collection from a CIF block.""" category_collection_from_cif(self, block) - def add(self, item) -> None: + def add(self, item: object) -> None: """ Insert or replace a pre-built item into the collection. @@ -227,7 +227,7 @@ def add(self, item) -> None: self[item._identity.category_entry_name] = item self._mark_parent_dirty() - def create(self, **kwargs) -> None: + def create(self, **kwargs: object) -> None: """ Create a new item with the given attributes and add it. diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index 5dc89e0a..3db3d63c 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -9,6 +9,9 @@ from __future__ import annotations +from typing import Generator +from typing import Iterator + from easydiffraction.core.guard import GuardedBase @@ -23,13 +26,13 @@ class CollectionBase(GuardedBase): and tooling; not enforced at runtime here. """ - def __init__(self, item_type) -> None: + def __init__(self, item_type: type) -> None: super().__init__() self._items: list = [] self._index: dict = {} self._item_type = item_type - def __getitem__(self, name: str): + def __getitem__(self, name: str) -> GuardedBase: """Return an item by its identity key. Rebuilds the internal index on a cache miss to stay consistent @@ -41,7 +44,7 @@ def __getitem__(self, name: str): self._rebuild_index() return self._index[name] - def __setitem__(self, name: str, item) -> None: + def __setitem__(self, name: str, item: GuardedBase) -> None: """Insert or replace an item under the given identity key.""" # Check if item with same key exists; if so, replace it for i, existing_item in enumerate(self._items): @@ -69,7 +72,7 @@ def __contains__(self, name: str) -> bool: self._rebuild_index() return name in self._index - def __iter__(self): + def __iter__(self) -> Iterator[GuardedBase]: """Iterate over items in insertion order.""" return iter(self._items) @@ -93,7 +96,7 @@ def remove(self, name: str) -> None: """ del self[name] - def _key_for(self, item): + def _key_for(self, item: GuardedBase) -> str | None: """Return the identity key for *item*. Subclasses must override to return the appropriate key @@ -109,20 +112,20 @@ def _rebuild_index(self) -> None: if key: self._index[key] = item - def keys(self): + def keys(self) -> Generator[str | None, None, None]: """Yield keys for all items in insertion order.""" return (self._key_for(item) for item in self._items) - def values(self): + def values(self) -> Generator[GuardedBase, None, None]: """Yield items in insertion order.""" return (item for item in self._items) - def items(self): + def items(self) -> Generator[tuple[str | None, GuardedBase], None, None]: """Yield ``(key, item)`` pairs in insertion order.""" return ((self._key_for(item), item) for item in self._items) @property - def names(self): + def names(self) -> list[str | None]: """List of all item keys in the collection.""" return list(self.keys()) diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index fa9a2083..daf557fd 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -13,7 +13,7 @@ class DatablockItem(GuardedBase): """Base class for items in a datablock collection.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._need_categories_update = True @@ -33,7 +33,7 @@ def __repr__(self) -> str: def _update_categories( self, - called_by_minimizer=False, + called_by_minimizer: bool = False, ) -> None: # TODO: Make abstract method and implement in subclasses. # This should call apply_symmetry and apply_constraints in the @@ -62,11 +62,11 @@ def _update_categories( self._need_categories_update = False @property - def unique_name(self): + def unique_name(self) -> str | None: return self._identity.datablock_entry_name @property - def categories(self): + def categories(self) -> list: cats = [ v for v in vars(self).values() if isinstance(v, (CategoryItem, CategoryCollection)) ] @@ -74,7 +74,7 @@ def categories(self): return sorted(cats, key=lambda c: type(c)._update_priority) @property - def parameters(self): + def parameters(self) -> list: """All parameters from all categories contained in this datablock. """ @@ -128,11 +128,11 @@ class DatablockCollection(CollectionBase): :meth:`add` with the resulting item. """ - def _key_for(self, item): + def _key_for(self, item: object) -> str | None: """Return the datablock-level identity key for *item*.""" return item._identity.datablock_entry_name - def add(self, item) -> None: + def add(self, item: object) -> None: """ Add a pre-built item to the collection. @@ -151,11 +151,11 @@ def __str__(self) -> str: return f'<{name} collection ({size} items)>' @property - def unique_name(self): + def unique_name(self) -> str | None: return None @property - def parameters(self): + def parameters(self) -> list: """All parameters from all datablocks in this collection.""" params = [] for db in self._items: diff --git a/src/easydiffraction/core/diagnostic.py b/src/easydiffraction/core/diagnostic.py index 4a1bd9a2..30b7cf14 100644 --- a/src/easydiffraction/core/diagnostic.py +++ b/src/easydiffraction/core/diagnostic.py @@ -19,7 +19,7 @@ class Diagnostics: # ============================================================== @staticmethod - def type_override_error(cls_name: str, expected, got): + def type_override_error(cls_name: str, expected: object, got: object) -> None: """Report an invalid DataTypes override. Used when descriptor and AttributeSpec types conflict. @@ -41,7 +41,7 @@ def type_override_error(cls_name: str, expected, got): def readonly_error( name: str, key: str | None = None, - ): + ) -> None: """Log an attempt to change a read-only attribute.""" Diagnostics._log_error( f"Cannot modify read-only attribute '{key}' of <{name}>.", @@ -53,8 +53,8 @@ def attr_error( name: str, key: str, allowed: set[str], - label='Allowed', - ): + label: str = 'Allowed', + ) -> None: """Log access to an unknown attribute and suggest closest key. """ @@ -73,11 +73,11 @@ def attr_error( @staticmethod def type_mismatch( name: str, - value, - expected_type, - current=None, - default=None, - ): + value: object, + expected_type: object, + current: object = None, + default: object = None, + ) -> None: """Log a type mismatch and keep current or default value.""" got_type = type(value).__name__ msg = ( @@ -91,12 +91,12 @@ def type_mismatch( @staticmethod def range_mismatch( name: str, - value, - ge, - le, - current=None, - default=None, - ): + value: object, + ge: float, + le: float, + current: object = None, + default: object = None, + ) -> None: """Log range violation for a numeric value.""" msg = f'Value mismatch for <{name}>. Provided {value!r} outside [{ge}, {le}].' Diagnostics._log_error_with_fallback( @@ -106,11 +106,11 @@ def range_mismatch( @staticmethod def choice_mismatch( name: str, - value, - allowed, - current=None, - default=None, - ): + value: object, + allowed: object, + current: object = None, + default: object = None, + ) -> None: """Log an invalid choice against allowed values.""" msg = f'Value mismatch for <{name}>. Provided {value!r} is unknown.' if allowed is not None: @@ -122,11 +122,11 @@ def choice_mismatch( @staticmethod def regex_mismatch( name: str, - value, - pattern, - current=None, - default=None, - ): + value: object, + pattern: str, + current: object = None, + default: object = None, + ) -> None: """Log a regex mismatch with the expected pattern.""" msg = ( f"Value mismatch for <{name}>. Provided {value!r} does not match pattern '{pattern}'." @@ -136,24 +136,24 @@ def regex_mismatch( ) @staticmethod - def no_value(name, default): + def no_value(name: str, default: object) -> None: """Log that default will be used due to missing value.""" Diagnostics._log_debug(f'No value provided for <{name}>. Using default {default!r}.') @staticmethod - def none_value(name): + def none_value(name: str) -> None: """Log explicit None provided by a user.""" Diagnostics._log_debug(f'Using `None` explicitly provided for <{name}>.') @staticmethod - def none_value_skip_range(name): + def none_value_skip_range(name: str) -> None: """Log that range validation is skipped due to None.""" Diagnostics._log_debug( f'Skipping range validation as `None` is explicitly provided for <{name}>.' ) @staticmethod - def validated(name, value, stage: str | None = None): + def validated(name: str, value: object, stage: str | None = None) -> None: """Log that a value passed a validation stage.""" stage_info = f' {stage}' if stage else '' Diagnostics._log_debug(f'Value {value!r} for <{name}> passed{stage_info} validation.') @@ -163,17 +163,17 @@ def validated(name, value, stage: str | None = None): # ============================================================== @staticmethod - def _log_error(msg, exc_type=Exception): + def _log_error(msg: str, exc_type: type[Exception] = Exception) -> None: """Emit an error-level message via shared logger.""" log.error(msg, exc_type=exc_type) @staticmethod def _log_error_with_fallback( - msg, - current=None, - default=None, - exc_type=Exception, - ): + msg: str, + current: object = None, + default: object = None, + exc_type: type[Exception] = Exception, + ) -> None: """Emit an error message and mention kept or default value.""" if current is not None: msg += f' Keeping current {current!r}.' @@ -182,7 +182,7 @@ def _log_error_with_fallback( log.error(msg, exc_type=exc_type) @staticmethod - def _log_debug(msg): + def _log_debug(msg: str) -> None: """Emit a debug-level message via shared logger.""" log.debug(msg) @@ -191,7 +191,7 @@ def _log_debug(msg): # ============================================================== @staticmethod - def _suggest(key: str, allowed: set[str]): + def _suggest(key: str, allowed: set[str]) -> str | None: """Suggest closest allowed key using string similarity.""" if not allowed: return None @@ -200,12 +200,12 @@ def _suggest(key: str, allowed: set[str]): return matches[0] if matches else None @staticmethod - def _build_suggestion(key: str, allowed: set[str]): + def _build_suggestion(key: str, allowed: set[str]) -> str: s = Diagnostics._suggest(key, allowed) return f" Did you mean '{s}'?" if s else '' @staticmethod - def _build_allowed(allowed, label='Allowed attributes'): + def _build_allowed(allowed: object, label: str = 'Allowed attributes') -> str: # allowed may be a set, list, or other iterable if allowed: allowed_list = list(allowed) diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index e1a98d2c..6c14bae0 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -35,7 +35,7 @@ class FactoryBase: _registry: List[Type] = [] _default_rules: Dict[FrozenSet[Tuple[str, Any]], str] = {} - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs: object) -> None: """Give each subclass its own independent registry and rules.""" super().__init_subclass__(**kwargs) cls._registry = [] @@ -47,7 +47,7 @@ def __init_subclass__(cls, **kwargs): # ------------------------------------------------------------------ @classmethod - def register(cls, klass): + def register(cls, klass: type) -> type: """Class decorator to register a concrete class. Usage:: @@ -79,7 +79,7 @@ def supported_tags(cls) -> List[str]: # ------------------------------------------------------------------ @classmethod - def default_tag(cls, **conditions) -> str: + def default_tag(cls, **conditions: object) -> str: """ Resolve the default tag for a given experimental context. @@ -123,7 +123,7 @@ def default_tag(cls, **conditions) -> str: # ------------------------------------------------------------------ @classmethod - def create(cls, tag: str, **kwargs) -> Any: + def create(cls, tag: str, **kwargs: object) -> object: """ Instantiate a registered class by *tag*. @@ -145,7 +145,7 @@ def create(cls, tag: str, **kwargs) -> Any: return supported[tag](**kwargs) @classmethod - def create_default_for(cls, **conditions) -> Any: + def create_default_for(cls, **conditions: object) -> object: """ Instantiate the default class for a given context. @@ -167,11 +167,11 @@ def create_default_for(cls, **conditions) -> Any: def supported_for( cls, *, - calculator=None, - sample_form=None, - scattering_type=None, - beam_mode=None, - radiation_probe=None, + calculator: object = None, + sample_form: object = None, + scattering_type: object = None, + beam_mode: object = None, + radiation_probe: object = None, ) -> List[Type]: """ Return classes matching conditions and/or calculator. @@ -213,11 +213,11 @@ def supported_for( def show_supported( cls, *, - calculator=None, - sample_form=None, - scattering_type=None, - beam_mode=None, - radiation_probe=None, + calculator: object = None, + sample_form: object = None, + scattering_type: object = None, + beam_mode: object = None, + radiation_probe: object = None, ) -> None: """ Pretty-print a table of supported types. diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index 96cc5990..1ec403da 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -5,6 +5,7 @@ from abc import ABC from abc import abstractmethod +from typing import Generator from easydiffraction.core.diagnostic import Diagnostics from easydiffraction.core.identity import Identity @@ -17,7 +18,7 @@ class GuardedBase(ABC): _diagnoser = Diagnostics() - def __init__(self): + def __init__(self) -> None: super().__init__() self._identity = Identity(owner=self) @@ -27,7 +28,7 @@ def __str__(self) -> str: def __repr__(self) -> str: return self.__str__() - def __getattr__(self, key: str): + def __getattr__(self, key: str) -> None: cls = type(self) allowed = cls._public_attrs() if key not in allowed: @@ -38,7 +39,7 @@ def __getattr__(self, key: str): label='Allowed readable/writable', ) - def __setattr__(self, key: str, value): + def __setattr__(self, key: str, value: object) -> None: # Always allow private or special attributes without diagnostics if key.startswith('_'): object.__setattr__(self, key, value) @@ -70,14 +71,14 @@ def __setattr__(self, key: str, value): self._assign_attr(key, value) - def _assign_attr(self, key, value): + def _assign_attr(self, key: str, value: object) -> None: """Low-level assignment with parent linkage.""" object.__setattr__(self, key, value) if key != '_parent' and isinstance(value, GuardedBase): object.__setattr__(value, '_parent', self) @classmethod - def _iter_properties(cls): + def _iter_properties(cls) -> Generator[tuple[str, property], None, None]: """ Iterate over all public properties defined in the class hierarchy. @@ -94,12 +95,12 @@ def _iter_properties(cls): yield key, attr @classmethod - def _public_attrs(cls): + def _public_attrs(cls) -> set[str]: """All public properties (read-only + writable).""" return {key for key, _ in cls._iter_properties()} @classmethod - def _public_readonly_attrs(cls): + def _public_readonly_attrs(cls) -> set[str]: """Public properties without a setter.""" return {key for key, prop in cls._iter_properties() if prop.fset is None} @@ -108,18 +109,18 @@ def _public_writable_attrs(cls) -> set[str]: """Public properties with a setter.""" return {key for key, prop in cls._iter_properties() if prop.fset is not None} - def _allowed_attrs(self, writable_only=False): + def _allowed_attrs(self, writable_only: bool = False) -> set[str]: cls = type(self) if writable_only: return cls._public_writable_attrs() return cls._public_attrs() @property - def _log_name(self): + def _log_name(self) -> str: return self.unique_name or type(self).__name__ @property - def unique_name(self): + def unique_name(self) -> str: return type(self).__name__ # @property @@ -133,7 +134,7 @@ def unique_name(self): @property @abstractmethod - def parameters(self): + def parameters(self) -> list: """Return a list of parameter objects (to be implemented by subclasses). """ @@ -160,7 +161,7 @@ def _first_sentence(docstring: str | None) -> str: return ' '.join(line.strip() for line in first_para.splitlines()) @classmethod - def _iter_methods(cls): + def _iter_methods(cls) -> Generator[tuple[str, object], None, None]: """ Iterate over public methods in the class hierarchy. diff --git a/src/easydiffraction/core/identity.py b/src/easydiffraction/core/identity.py index 2f57c163..9cb00d95 100644 --- a/src/easydiffraction/core/identity.py +++ b/src/easydiffraction/core/identity.py @@ -19,13 +19,13 @@ def __init__( datablock_entry: Callable | None = None, category_code: str | None = None, category_entry: Callable | None = None, - ): + ) -> None: self._owner = owner self._datablock_entry = datablock_entry self._category_code = category_code self._category_entry = category_entry - def _resolve_up(self, attr: str, visited=None): + def _resolve_up(self, attr: str, visited: set[int] | None = None) -> str | None: """Resolve attribute by walking up parent chain safely.""" if visited is None: visited = set() @@ -47,31 +47,31 @@ def _resolve_up(self, attr: str, visited=None): return None @property - def datablock_entry_name(self): + def datablock_entry_name(self) -> str | None: """Datablock entry name or None if not set.""" return self._resolve_up('datablock_entry') @datablock_entry_name.setter - def datablock_entry_name(self, func: callable): + def datablock_entry_name(self, func: callable) -> None: """Set callable returning datablock entry name.""" self._datablock_entry = func @property - def category_code(self): + def category_code(self) -> str | None: """Category code like 'atom_site' or 'background'.""" return self._resolve_up('category_code') @category_code.setter - def category_code(self, value: str): + def category_code(self, value: str) -> None: """Set category code value.""" self._category_code = value @property - def category_entry_name(self): + def category_entry_name(self) -> str | None: """Category entry name or None if not set.""" return self._resolve_up('category_entry') @category_entry_name.setter - def category_entry_name(self, func: callable): + def category_entry_name(self, func: callable) -> None: """Set callable returning category entry name.""" self._category_entry = func diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index da09d859..68626640 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -53,10 +53,10 @@ class Compatibility: def supports( self, - sample_form=None, - scattering_type=None, - beam_mode=None, - radiation_probe=None, + sample_form: object = None, + scattering_type: object = None, + beam_mode: object = None, + radiation_probe: object = None, ) -> bool: """Check if this compatibility matches the given conditions. @@ -97,7 +97,7 @@ class CalculatorSupport: calculators: FrozenSet = frozenset() - def supports(self, calculator) -> bool: + def supports(self, calculator: object) -> bool: """ Check if a specific calculator can handle this class. diff --git a/src/easydiffraction/core/singleton.py b/src/easydiffraction/core/singleton.py index d0b0c589..7ab6233c 100644 --- a/src/easydiffraction/core/singleton.py +++ b/src/easydiffraction/core/singleton.py @@ -46,7 +46,7 @@ def get_uid_map(self) -> Dict[str, Any]: """Returns the current UID-to-Parameter map.""" return self._uid_map - def add_to_uid_map(self, parameter): + def add_to_uid_map(self, parameter: object) -> None: """Adds a single Parameter or Descriptor object to the UID map. Only Descriptor or Parameter instances are allowed (not @@ -61,7 +61,7 @@ def add_to_uid_map(self, parameter): ) self._uid_map[parameter.uid] = parameter - def replace_uid(self, old_uid, new_uid): + def replace_uid(self, old_uid: str, new_uid: str) -> None: """Replaces an existing UID key in the UID map with a new UID. Moves the associated parameter from old_uid to new_uid. Raises a @@ -102,7 +102,7 @@ def __init__(self) -> None: # Internally parsed constraints as (lhs_alias, rhs_expr) tuples self._parsed_constraints: List[Tuple[str, str]] = [] - def set_aliases(self, aliases): + def set_aliases(self, aliases: object) -> None: """Sets the alias map (name → parameter wrapper). Called when user registers parameter aliases like: @@ -110,7 +110,7 @@ def set_aliases(self, aliases): """ self._alias_to_param = dict(aliases.items()) - def set_constraints(self, constraints): + def set_constraints(self, constraints: object) -> None: """Sets the constraints and triggers parsing into internal format. diff --git a/src/easydiffraction/core/validation.py b/src/easydiffraction/core/validation.py index 1adf6a12..f08d8152 100644 --- a/src/easydiffraction/core/validation.py +++ b/src/easydiffraction/core/validation.py @@ -37,11 +37,11 @@ class DataTypes(Enum): BOOL = (bool,) ANY = (object,) # fallback for unconstrained - def __str__(self): + def __str__(self) -> str: return self.name.lower() @property - def expected_type(self): + def expected_type(self) -> DataTypes: """Convenience alias for tuple of allowed Python types.""" return self.value @@ -59,7 +59,7 @@ class ValidationStage(Enum): MEMBERSHIP = auto() REGEX = auto() - def __str__(self): + def __str__(self) -> str: return self.name.lower() @@ -72,7 +72,7 @@ class ValidatorBase(ABC): """Abstract base class for all validators.""" @abstractmethod - def validated(self, value, name, default=None, current=None): + def validated(self, value: object, name: str, default: object = None, current: object = None) -> object: """Return a validated value or fallback. Subclasses must implement this method. @@ -81,9 +81,9 @@ def validated(self, value, name, default=None, current=None): def _fallback( self, - current=None, - default=None, - ): + current: object = None, + default: object = None, + ) -> object: """Return current if set, else default.""" return current if current is not None else default @@ -94,7 +94,7 @@ def _fallback( class TypeValidator(ValidatorBase): """Ensure a value is of the expected data type.""" - def __init__(self, expected_type: DataTypes): + def __init__(self, expected_type: DataTypes) -> None: if isinstance(expected_type, DataTypes): self.expected_type = expected_type self.expected_label = str(expected_type) @@ -103,12 +103,12 @@ def __init__(self, expected_type: DataTypes): def validated( self, - value, - name, - default=None, - current=None, - allow_none=False, - ): + value: object, + name: str, + default: object = None, + current: object = None, + allow_none: bool = False, + ) -> object: """Validate type and return value or fallback. If allow_none is True, None bypasses content checks. @@ -151,18 +151,18 @@ class RangeValidator(ValidatorBase): def __init__( self, *, - ge=-np.inf, - le=np.inf, - ): + ge: float = -np.inf, + le: float = np.inf, + ) -> None: self.ge, self.le = ge, le def validated( self, - value, - name, - default=None, - current=None, - ): + value: object, + name: str, + default: object = None, + current: object = None, + ) -> object: """Validate range and return value or fallback.""" if not (self.ge <= value <= self.le): Diagnostics.range_mismatch( @@ -192,17 +192,17 @@ class MembershipValidator(ValidatorBase): `allowed` may be an iterable or a callable returning a collection. """ - def __init__(self, allowed): + def __init__(self, allowed: object) -> None: # Do not convert immediately to list — may be callable self.allowed = allowed def validated( self, - value, - name, - default=None, - current=None, - ): + value: object, + name: str, + default: object = None, + current: object = None, + ) -> object: """Validate membership and return value or fallback.""" # Dynamically evaluate allowed if callable (e.g. lambda) allowed_values = self.allowed() if callable(self.allowed) else self.allowed @@ -231,16 +231,16 @@ def validated( class RegexValidator(ValidatorBase): """Ensure that a string matches a given regular expression.""" - def __init__(self, pattern): + def __init__(self, pattern: str) -> None: self.pattern = re.compile(pattern) def validated( self, - value, - name, - default=None, - current=None, - ): + value: object, + name: str, + default: object = None, + current: object = None, + ) -> object: """Validate regex and return value or fallback.""" if not self.pattern.fullmatch(value): Diagnostics.regex_mismatch( @@ -271,11 +271,11 @@ class AttributeSpec: def __init__( self, *, - default=None, - data_type=None, - validator=None, + default: object = None, + data_type: DataTypes | None = None, + validator: ValidatorBase | None = None, allow_none: bool = False, - ): + ) -> None: self.default = default self.allow_none = allow_none self._data_type_validator = TypeValidator(data_type) if data_type else None @@ -283,10 +283,10 @@ def __init__( def validated( self, - value, - name, - current=None, - ): + value: object, + name: str, + current: object = None, + ) -> object: """Validate through type and content validators. Returns validated value, possibly default or current if errors diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 89f0f0bb..136654b4 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -6,7 +6,6 @@ import secrets import string from typing import TYPE_CHECKING -from typing import Any import numpy as np @@ -55,7 +54,7 @@ def __init__( value_spec: AttributeSpec, name: str, description: str = None, - ): + ) -> None: """ Initialize the descriptor with validation and identity. @@ -114,7 +113,7 @@ def name(self) -> str: return self._name @property - def unique_name(self): + def unique_name(self) -> str: """Fully qualified name including datablock, category and entry name. """ @@ -127,7 +126,7 @@ def unique_name(self): ] return '.'.join(filter(None, parts)) - def _parent_of_type(self, cls): + def _parent_of_type(self, cls: type) -> object | None: """Walk up the parent chain and return the first parent of type `cls`. """ @@ -140,19 +139,19 @@ def _parent_of_type(self, cls): obj = getattr(obj, '_parent', None) return None - def _datablock_item(self): + def _datablock_item(self) -> object | None: """Return the DatablockItem ancestor, if any.""" from easydiffraction.core.datablock import DatablockItem return self._parent_of_type(DatablockItem) @property - def value(self): + def value(self) -> object: """Current validated value.""" return self._value @value.setter - def value(self, v): + def value(self, v: object) -> None: """Set a new value after validating against the spec.""" # Do nothing if the value is unchanged if self._value == v: @@ -171,7 +170,7 @@ def value(self, v): if parent_datablock is not None: parent_datablock._need_categories_update = True - def _set_value_from_minimizer(self, v) -> None: + def _set_value_from_minimizer(self, v: object) -> None: """Set the value from a minimizer, bypassing validation. Writes ``_value`` directly — no type or range checks — but still @@ -191,12 +190,12 @@ def _set_value_from_minimizer(self, v) -> None: parent_datablock._need_categories_update = True @property - def description(self): + def description(self) -> str | None: """Optional human-readable description.""" return self._description @property - def parameters(self): + def parameters(self) -> list[GenericDescriptorBase]: """Return a flat list of parameters contained by this object. For a single descriptor, it returns a one-element list with @@ -210,7 +209,7 @@ def as_cif(self) -> str: """Serialize this descriptor to a CIF-formatted string.""" return param_to_cif(self) - def from_cif(self, block, idx=0): + def from_cif(self, block: object, idx: int = 0) -> None: """Populate this parameter from a CIF block.""" param_from_cif(self, block, idx) @@ -223,7 +222,7 @@ class GenericStringDescriptor(GenericDescriptorBase): def __init__( self, - **kwargs: Any, + **kwargs: object, ) -> None: super().__init__(**kwargs) @@ -238,7 +237,7 @@ def __init__( self, *, units: str = '', - **kwargs: Any, + **kwargs: object, ) -> None: super().__init__(**kwargs) self._units: str = units @@ -269,8 +268,8 @@ class GenericParameter(GenericNumericDescriptor): def __init__( self, - **kwargs: Any, - ): + **kwargs: object, + ) -> None: super().__init__(**kwargs) # Initial validated states @@ -310,22 +309,22 @@ def _generate_uid(length: int = 16) -> str: return ''.join(secrets.choice(letters) for _ in range(length)) @property - def uid(self): + def uid(self) -> str: """Stable random identifier for this descriptor.""" return self._uid @property - def _minimizer_uid(self): + def _minimizer_uid(self) -> str: """Variant of uid that is safe for minimizer engines.""" # return self.unique_name.replace('.', '__') return self.uid @property - def constrained(self): + def constrained(self) -> bool: """Whether this parameter is part of a constraint expression.""" return self._constrained - def _set_value_constrained(self, v) -> None: + def _set_value_constrained(self, v: object) -> None: """Set the value from a constraint expression. Validates against the spec, marks the parent datablock dirty, @@ -336,50 +335,50 @@ def _set_value_constrained(self, v) -> None: self._constrained = True @property - def free(self): + def free(self) -> bool: """Whether this parameter is currently varied during fitting.""" return self._free @free.setter - def free(self, v): + def free(self, v: bool) -> None: """Set the "free" flag after validation.""" self._free = self._free_spec.validated( v, name=f'{self.unique_name}.free', current=self._free ) @property - def uncertainty(self): + def uncertainty(self) -> float | None: """Estimated standard uncertainty of the fitted value, if available. """ return self._uncertainty @uncertainty.setter - def uncertainty(self, v): + def uncertainty(self, v: float | None) -> None: """Set the uncertainty value (must be non-negative or None).""" self._uncertainty = self._uncertainty_spec.validated( v, name=f'{self.unique_name}.uncertainty', current=self._uncertainty ) @property - def fit_min(self): + def fit_min(self) -> float: """Lower fitting bound.""" return self._fit_min @fit_min.setter - def fit_min(self, v): + def fit_min(self, v: float) -> None: """Set the lower bound for the parameter value.""" self._fit_min = self._fit_min_spec.validated( v, name=f'{self.unique_name}.fit_min', current=self._fit_min ) @property - def fit_max(self): + def fit_max(self) -> float: """Upper fitting bound.""" return self._fit_max @fit_max.setter - def fit_max(self, v): + def fit_max(self, v: float) -> None: """Set the upper bound for the parameter value.""" self._fit_max = self._fit_max_spec.validated( v, name=f'{self.unique_name}.fit_max', current=self._fit_max @@ -394,7 +393,7 @@ def __init__( self, *, cif_handler: CifHandler, - **kwargs: Any, + **kwargs: object, ) -> None: """ String descriptor bound to a CIF handler. @@ -419,7 +418,7 @@ def __init__( self, *, cif_handler: CifHandler, - **kwargs: Any, + **kwargs: object, ) -> None: """ Numeric descriptor bound to a CIF handler. @@ -444,7 +443,7 @@ def __init__( self, *, cif_handler: CifHandler, - **kwargs: Any, + **kwargs: object, ) -> None: """ Fittable parameter bound to a CIF handler. diff --git a/src/easydiffraction/crystallography/space_groups.py b/src/easydiffraction/crystallography/space_groups.py index 12e35984..d3bf35fc 100644 --- a/src/easydiffraction/crystallography/space_groups.py +++ b/src/easydiffraction/crystallography/space_groups.py @@ -10,10 +10,9 @@ import gzip import pickle # noqa: S403 - trusted internal pickle file (package data only) from pathlib import Path -from typing import Any -def _restricted_pickle_load(file_obj) -> Any: +def _restricted_pickle_load(file_obj: object) -> object: """Load pickle data from an internal gz file (trusted boundary). The archive lives in the package; no user-controlled input enters @@ -23,7 +22,7 @@ def _restricted_pickle_load(file_obj) -> Any: return data -def _load(): +def _load() -> object: """Load space-group data from the packaged archive.""" path = Path(__file__).with_name('space_groups.pkl.gz') with gzip.open(path, 'rb') as f: diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index b2b0584f..c043f9ab 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -144,10 +144,10 @@ class ChebyshevPolynomialBackground(BackgroundBase): }), ) - def __init__(self): + def __init__(self) -> None: super().__init__(item_type=PolynomialTerm) - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: """Evaluate polynomial background over x data.""" del called_by_minimizer diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index a74f0c11..d30a6d71 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -149,10 +149,10 @@ class LineSegmentBackground(BackgroundBase): calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}), ) - def __init__(self): + def __init__(self) -> None: super().__init__(item_type=LineSegment) - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: """Interpolate background points over x data.""" del called_by_minimizer diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 3aa6813b..510d6a22 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -29,7 +29,7 @@ class PdDataPointBaseMixin: """Single base data point mixin for powder diffraction data.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._point_id = StringDescriptor( @@ -193,7 +193,7 @@ class PdCwlDataPointMixin: wavelength. """ - def __init__(self): + def __init__(self) -> None: super().__init__() self._two_theta = NumericDescriptor( @@ -229,7 +229,7 @@ def two_theta(self) -> NumericDescriptor: class PdTofDataPointMixin: """Mixin for powder diffraction data points with time-of-flight.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._time_of_flight = NumericDescriptor( @@ -308,17 +308,17 @@ class PdDataBase(CategoryCollection): # Should be set only once - def _set_point_id(self, values) -> None: + def _set_point_id(self, values: object) -> None: """Helper method to set point IDs.""" for p, v in zip(self._items, values, strict=True): p.point_id._value = v - def _set_intensity_meas(self, values) -> None: + def _set_intensity_meas(self, values: object) -> None: """Helper method to set measured intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas._value = v - def _set_intensity_meas_su(self, values) -> None: + def _set_intensity_meas_su(self, values: object) -> None: """Helper method to set standard uncertainty of measured intensity. """ @@ -327,22 +327,22 @@ def _set_intensity_meas_su(self, values) -> None: # Can be set multiple times - def _set_d_spacing(self, values) -> None: + def _set_d_spacing(self, values: object) -> None: """Helper method to set d-spacing values.""" for p, v in zip(self._calc_items, values, strict=True): p.d_spacing._value = v - def _set_intensity_calc(self, values) -> None: + def _set_intensity_calc(self, values: object) -> None: """Helper method to set calculated intensity.""" for p, v in zip(self._calc_items, values, strict=True): p.intensity_calc._value = v - def _set_intensity_bkg(self, values) -> None: + def _set_intensity_bkg(self, values: object) -> None: """Helper method to set background intensity.""" for p, v in zip(self._calc_items, values, strict=True): p.intensity_bkg._value = v - def _set_calc_status(self, values) -> None: + def _set_calc_status(self, values: object) -> None: """Helper method to set refinement status.""" for p, v in zip(self._items, values, strict=True): if v: @@ -359,13 +359,13 @@ def _calc_mask(self) -> np.ndarray: return self.calc_status == 'incl' @property - def _calc_items(self): + def _calc_items(self) -> list: """Get only the items included in calculations.""" return [item for item, mask in zip(self._items, self._calc_mask, strict=False) if mask] # Misc - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: experiment = self._parent experiments = experiment._parent project = experiments._parent @@ -472,7 +472,7 @@ class PdCwlData(PdDataBase): calculators=frozenset({CalculatorEnum.CRYSPY}), ) - def __init__(self): + def __init__(self) -> None: super().__init__(item_type=PdCwlDataPoint) ################# @@ -481,7 +481,7 @@ def __init__(self): # Should be set only once - def _create_items_set_xcoord_and_id(self, values) -> None: + def _create_items_set_xcoord_and_id(self, values: object) -> None: """Helper method to set 2θ values.""" # TODO: split into multiple methods @@ -497,7 +497,7 @@ def _create_items_set_xcoord_and_id(self, values) -> None: # Misc - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: super()._update(called_by_minimizer) experiment = self._parent @@ -547,7 +547,7 @@ class PdTofData(PdDataBase): calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}), ) - def __init__(self): + def __init__(self) -> None: super().__init__(item_type=PdTofDataPoint) ################# @@ -556,7 +556,7 @@ def __init__(self): # Should be set only once - def _create_items_set_xcoord_and_id(self, values) -> None: + def _create_items_set_xcoord_and_id(self, values: object) -> None: """Helper method to set time-of-flight values.""" # TODO: split into multiple methods @@ -572,7 +572,7 @@ def _create_items_set_xcoord_and_id(self, values) -> None: # Misc - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: super()._update(called_by_minimizer) experiment = self._parent diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index e229797a..663118f3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -248,7 +248,7 @@ class ReflnData(CategoryCollection): _update_priority = 100 - def __init__(self): + def __init__(self) -> None: super().__init__(item_type=Refln) ################# @@ -257,7 +257,7 @@ def __init__(self): # Should be set only once - def _create_items_set_hkl_and_id(self, indices_h, indices_k, indices_l) -> None: + def _create_items_set_hkl_and_id(self, indices_h: object, indices_k: object, indices_l: object) -> None: """Helper method to set Miller indices.""" # TODO: split into multiple methods @@ -275,48 +275,48 @@ def _create_items_set_hkl_and_id(self, indices_h, indices_k, indices_l) -> None: # Set reflection IDs self._set_id([str(i + 1) for i in range(indices_h.size)]) - def _set_id(self, values) -> None: + def _set_id(self, values: object) -> None: """Helper method to set reflection IDs.""" for p, v in zip(self._items, values, strict=True): p.id._value = v - def _set_intensity_meas(self, values) -> None: + def _set_intensity_meas(self, values: object) -> None: """Helper method to set measured intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas._value = v - def _set_intensity_meas_su(self, values) -> None: + def _set_intensity_meas_su(self, values: object) -> None: """Helper method to set standard uncertainty of measured intensity. """ for p, v in zip(self._items, values, strict=True): p.intensity_meas_su._value = v - def _set_wavelength(self, values) -> None: + def _set_wavelength(self, values: object) -> None: """Helper method to set wavelength.""" for p, v in zip(self._items, values, strict=True): p.wavelength._value = v # Can be set multiple times - def _set_d_spacing(self, values) -> None: + def _set_d_spacing(self, values: object) -> None: """Helper method to set d-spacing values.""" for p, v in zip(self._items, values, strict=True): p.d_spacing._value = v - def _set_sin_theta_over_lambda(self, values) -> None: + def _set_sin_theta_over_lambda(self, values: object) -> None: """Helper method to set sin(theta)/lambda values.""" for p, v in zip(self._items, values, strict=True): p.sin_theta_over_lambda._value = v - def _set_intensity_calc(self, values) -> None: + def _set_intensity_calc(self, values: object) -> None: """Helper method to set calculated intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_calc._value = v # Misc - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: experiment = self._parent experiments = experiment._parent project = experiments._parent diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index 1b5d867c..ae0c9f83 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -186,17 +186,17 @@ class TotalDataBase(CategoryCollection): # Should be set only once - def _set_point_id(self, values) -> None: + def _set_point_id(self, values: object) -> None: """Helper method to set point IDs.""" for p, v in zip(self._items, values, strict=True): p.point_id._value = v - def _set_g_r_meas(self, values) -> None: + def _set_g_r_meas(self, values: object) -> None: """Helper method to set measured G(r).""" for p, v in zip(self._items, values, strict=True): p.g_r_meas._value = v - def _set_g_r_meas_su(self, values) -> None: + def _set_g_r_meas_su(self, values: object) -> None: """Helper method to set standard uncertainty of measured G(r). """ @@ -205,12 +205,12 @@ def _set_g_r_meas_su(self, values) -> None: # Can be set multiple times - def _set_g_r_calc(self, values) -> None: + def _set_g_r_calc(self, values: object) -> None: """Helper method to set calculated G(r).""" for p, v in zip(self._calc_items, values, strict=True): p.g_r_calc._value = v - def _set_calc_status(self, values) -> None: + def _set_calc_status(self, values: object) -> None: """Helper method to set calculation status.""" for p, v in zip(self._items, values, strict=True): if v: @@ -227,13 +227,13 @@ def _calc_mask(self) -> np.ndarray: return self.calc_status == 'incl' @property - def _calc_items(self): + def _calc_items(self) -> list: """Get only the items included in calculations.""" return [item for item, mask in zip(self._items, self._calc_mask, strict=False) if mask] # Misc - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: experiment = self._parent experiments = experiment._parent project = experiments._parent @@ -319,7 +319,7 @@ class TotalData(TotalDataBase): calculators=frozenset({CalculatorEnum.PDFFIT}), ) - def __init__(self): + def __init__(self) -> None: super().__init__(item_type=TotalDataPoint) ################# @@ -328,7 +328,7 @@ def __init__(self): # Should be set only once - def _create_items_set_xcoord_and_id(self, values) -> None: + def _create_items_set_xcoord_and_id(self, values: object) -> None: """Helper method to set r values.""" # TODO: split into multiple methods diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 51492d9e..1d6a30b2 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -29,7 +29,7 @@ class ExcludedRegion(CategoryItem): """Closed interval [start, end] to be excluded.""" - def __init__(self): + def __init__(self) -> None: super().__init__() # TODO: Add point_id as for the background @@ -130,10 +130,10 @@ class ExcludedRegions(CategoryCollection): sample_form=frozenset({SampleFormEnum.POWDER}), ) - def __init__(self): + def __init__(self) -> None: super().__init__(item_type=ExcludedRegion) - def _update(self, called_by_minimizer=False): + def _update(self, called_by_minimizer: bool = False) -> None: del called_by_minimizer data = self._parent.data diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 899c8217..4c8207b1 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -44,7 +44,7 @@ class ExperimentType(CategoryItem): description='Experiment type descriptor', ) - def __init__(self): + def __init__(self) -> None: super().__init__() self._sample_form = StringDescriptor( diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index 8587fd00..ba2e85c3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -23,7 +23,7 @@ class LinkedPhase(CategoryItem): """Link to a phase by id with a scale factor.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._id = StringDescriptor( @@ -92,6 +92,6 @@ class LinkedPhases(CategoryCollection): sample_form=frozenset({SampleFormEnum.POWDER}), ) - def __init__(self): + def __init__(self) -> None: """Create an empty collection of linked phases.""" super().__init__(item_type=LinkedPhase) diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index c389f571..24aca092 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -16,7 +16,7 @@ class CwlBroadeningMixin: """CWL Gaussian and Lorentz broadening parameters.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._broad_gauss_u: Parameter = Parameter( @@ -150,7 +150,7 @@ def broad_lorentz_y(self, value: float) -> None: class EmpiricalAsymmetryMixin: """Empirical CWL peak asymmetry parameters.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._asym_empir_1: Parameter = Parameter( @@ -254,7 +254,7 @@ def asym_empir_4(self, value: float) -> None: class FcjAsymmetryMixin: """Finger–Cox–Jephcoat (FCJ) asymmetry parameters.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._asym_fcj_1: Parameter = Parameter( diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index 9f2bcabb..3231923c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -18,7 +18,7 @@ class TofBroadeningMixin: """TOF Gaussian/Lorentz broadening and mixing parameters.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._broad_gauss_sigma_0 = Parameter( @@ -224,7 +224,7 @@ def broad_mix_beta_1(self, value: float) -> None: class IkedaCarpenterAsymmetryMixin: """Ikeda–Carpenter asymmetry parameters.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._asym_alpha_0 = Parameter( diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 3c2f65a7..edcd2061 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -17,7 +17,7 @@ class TotalBroadeningMixin: """PDF broadening/damping/sharpening parameters.""" - def __init__(self): + def __init__(self) -> None: super().__init__() self._damp_q = Parameter( diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 86ddf119..9488eb21 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -43,7 +43,7 @@ def __init__( *, name: str, type: ExperimentType, - ): + ) -> None: super().__init__() self._name = name self._type = type @@ -69,7 +69,7 @@ def name(self, new: str) -> None: self._name = new @property - def type(self): # TODO: Consider another name + def type(self) -> object: # TODO: Consider another name """Experiment type descriptor (sample form, probe, beam mode). """ @@ -104,7 +104,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: # ------------------------------------------------------------------ @property - def calculator(self): + def calculator(self) -> object: """The active calculator instance for this experiment. Auto-resolved on first access from the experiment's data @@ -254,7 +254,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: # ------------------------------------------------------------------ @property - def extinction(self): + def extinction(self) -> object: """Active extinction correction model.""" return self._extinction @@ -301,7 +301,7 @@ def show_current_extinction_type(self) -> None: # ------------------------------------------------------------------ @property - def linked_crystal(self): + def linked_crystal(self) -> object: """Linked crystal model for this experiment.""" return self._linked_crystal @@ -348,7 +348,7 @@ def show_current_linked_crystal_type(self) -> None: # ------------------------------------------------------------------ @property - def instrument(self): + def instrument(self) -> object: """Active instrument model for this experiment.""" return self._instrument @@ -403,7 +403,7 @@ def show_current_instrument_type(self) -> None: # ------------------------------------------------------------------ @property - def data(self): + def data(self) -> object: """Data collection for this experiment.""" return self._data @@ -523,7 +523,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: pass @property - def linked_phases(self): + def linked_phases(self) -> object: """Collection of phases linked to this experiment.""" return self._linked_phases @@ -566,7 +566,7 @@ def show_current_linked_phases_type(self) -> None: console.print(self.linked_phases_type) @property - def excluded_regions(self): + def excluded_regions(self) -> object: """Collection of excluded regions for the x-grid.""" return self._excluded_regions @@ -615,7 +615,7 @@ def show_current_excluded_regions_type(self) -> None: # ------------------------------------------------------------------ @property - def data(self): + def data(self) -> object: """Data collection for this experiment.""" return self._data @@ -657,17 +657,17 @@ def show_current_data_type(self) -> None: console.print(self.data_type) @property - def peak(self): + def peak(self) -> object: """Peak category object with profile parameters and mixins.""" return self._peak @property - def peak_profile_type(self): + def peak_profile_type(self) -> object: """Currently selected peak profile type enum.""" return self._peak_profile_type @peak_profile_type.setter - def peak_profile_type(self, new_type: str): + def peak_profile_type(self, new_type: str) -> None: """ Change the active peak profile type, if supported. @@ -700,14 +700,14 @@ def peak_profile_type(self, new_type: str): console.paragraph(f"Peak profile type for experiment '{self.name}' changed to") console.print(new_type) - def show_supported_peak_profile_types(self): + def show_supported_peak_profile_types(self) -> None: """Print available peak profile types for this experiment.""" PeakFactory.show_supported( scattering_type=self.type.scattering_type.value, beam_mode=self.type.beam_mode.value, ) - def show_current_peak_profile_type(self): + def show_current_peak_profile_type(self) -> None: """Print the currently selected peak profile type.""" console.paragraph('Current peak profile type') console.print(self.peak_profile_type) diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index 24dbf9c3..230f69d2 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -105,7 +105,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: # ------------------------------------------------------------------ @property - def instrument(self): + def instrument(self) -> object: """Active instrument model for this experiment.""" return self._instrument @@ -160,12 +160,12 @@ def show_current_instrument_type(self) -> None: # ------------------------------------------------------------------ @property - def background_type(self): + def background_type(self) -> object: """Current background type enum value.""" return self._background_type @background_type.setter - def background_type(self, new_type): + def background_type(self, new_type: str) -> None: """Set a new background type and recreate background object.""" if self._background_type == new_type: console.paragraph(f"Background type for experiment '{self.name}' already set to") @@ -193,14 +193,14 @@ def background_type(self, new_type): console.print(new_type) @property - def background(self): + def background(self) -> object: return self._background - def show_supported_background_types(self): + def show_supported_background_types(self) -> None: """Print a table of supported background types.""" BackgroundFactory.show_supported() - def show_current_background_type(self): + def show_current_background_type(self) -> None: """Print the currently used background type.""" console.paragraph('Current background type') console.print(self.background_type) diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 3d38cbd3..2c3a38c2 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -55,7 +55,7 @@ class ExperimentFactory(FactoryBase): } # TODO: Add to core/factory.py? - def __init__(self): + def __init__(self) -> None: log.error( 'Experiment objects must be created using class methods such as ' '`ExperimentFactory.from_cif_str(...)`, etc.' @@ -95,7 +95,7 @@ def _create_experiment_type( @classmethod @typechecked - def _resolve_class(cls, expt_type: ExperimentType): + def _resolve_class(cls, expt_type: ExperimentType) -> type: """Look up the experiment class from the type enums.""" tag = cls.default_tag( scattering_type=expt_type.scattering_type.value, diff --git a/src/easydiffraction/datablocks/experiment/item/total_pd.py b/src/easydiffraction/datablocks/experiment/item/total_pd.py index 33390617..1f912002 100644 --- a/src/easydiffraction/datablocks/experiment/item/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/total_pd.py @@ -38,10 +38,10 @@ def __init__( self, name: str, type: ExperimentType, - ): + ) -> None: super().__init__(name=name, type=type) - def _load_ascii_data_to_experiment(self, data_path): + def _load_ascii_data_to_experiment(self, data_path: str) -> None: """Loads x, y, sy values from an ASCII data file into the experiment. diff --git a/src/easydiffraction/datablocks/structure/item/factory.py b/src/easydiffraction/datablocks/structure/item/factory.py index 3148639a..2078e68b 100644 --- a/src/easydiffraction/datablocks/structure/item/factory.py +++ b/src/easydiffraction/datablocks/structure/item/factory.py @@ -26,7 +26,7 @@ class StructureFactory: """Create :class:`Structure` instances from supported inputs.""" - def __init__(self): + def __init__(self) -> None: log.error( 'Structure objects must be created using class methods such as ' '`StructureFactory.from_cif_str(...)`, etc.' diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index 54ff5dd1..6f32e84f 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -6,7 +6,6 @@ from abc import ABC from abc import abstractmethod -from typing import Any from typing import List from typing import Tuple @@ -25,7 +24,7 @@ class RendererBase(SingletonBase, ABC): supported engines in a table-friendly format. """ - def __init__(self): + def __init__(self) -> None: self._engine = self._default_engine() self._backend = self._factory().create(self._engine) @@ -90,7 +89,7 @@ class RendererFactoryBase(ABC): """Base factory that manages discovery and creation of backends.""" @classmethod - def create(cls, engine_name: str) -> Any: + def create(cls, engine_name: str) -> object: """ Create a backend instance for the given engine. diff --git a/src/easydiffraction/display/plotters/ascii.py b/src/easydiffraction/display/plotters/ascii.py index 44c1c13e..02559f18 100644 --- a/src/easydiffraction/display/plotters/ascii.py +++ b/src/easydiffraction/display/plotters/ascii.py @@ -25,7 +25,7 @@ class AsciiPlotter(PlotterBase): """Terminal-based plotter using ASCII art.""" - def _get_legend_item(self, label): + def _get_legend_item(self, label: str) -> str: """ Return a colored legend entry for a given series label. @@ -50,13 +50,13 @@ def _get_legend_item(self, label): def plot_powder( self, - x, - y_series, - labels, - axes_labels, - title, - height=None, - ): + x: object, + y_series: object, + labels: object, + axes_labels: object, + title: str, + height: int | None = None, + ) -> None: """ Render a line plot for powder diffraction data. @@ -103,13 +103,13 @@ def plot_powder( def plot_single_crystal( self, - x_calc, - y_meas, - y_meas_su, - axes_labels, - title, - height=None, - ): + x_calc: object, + y_meas: object, + y_meas_su: object, + axes_labels: object, + title: str, + height: int | None = None, + ) -> None: """ Render a scatter plot for single crystal diffraction data. diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index a60fcdd7..cffa3b81 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -165,13 +165,13 @@ class PlotterBase(ABC): @abstractmethod def plot_powder( self, - x, - y_series, - labels, - axes_labels, - title, - height, - ): + x: object, + y_series: object, + labels: object, + axes_labels: object, + title: str, + height: int | None, + ) -> None: """ Render a line plot for powder diffraction data. @@ -198,13 +198,13 @@ def plot_powder( @abstractmethod def plot_single_crystal( self, - x_calc, - y_meas, - y_meas_su, - axes_labels, - title, - height, - ): + x_calc: object, + y_meas: object, + y_meas_su: object, + axes_labels: object, + title: str, + height: int | None, + ) -> None: """ Render a scatter plot for single crystal diffraction data. diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index 67fff499..e81e68d6 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -38,10 +38,10 @@ class PlotlyPlotter(PlotterBase): def _get_powder_trace( self, - x, - y, - label, - ): + x: object, + y: object, + label: str, + ) -> object: """ Create a Plotly trace for powder diffraction data. @@ -76,10 +76,10 @@ def _get_powder_trace( def _get_single_crystal_trace( self, - x_calc, - y_meas, - y_meas_su, - ): + x_calc: object, + y_meas: object, + y_meas_su: object, + ) -> object: """ Create a Plotly trace for single crystal diffraction data. @@ -118,7 +118,7 @@ def _get_single_crystal_trace( return trace - def _get_diagonal_shape(self): + def _get_diagonal_shape(self) -> dict: """ Create a diagonal reference line shape. @@ -141,7 +141,7 @@ def _get_diagonal_shape(self): line=dict(width=0.5), ) - def _get_config(self): + def _get_config(self) -> dict: """ Return the Plotly figure configuration. @@ -162,9 +162,9 @@ def _get_config(self): def _get_figure( self, - data, - layout, - ): + data: object, + layout: object, + ) -> object: """ Create and configure a Plotly figure. @@ -189,8 +189,8 @@ def _get_figure( def _show_figure( self, - fig, - ): + fig: object, + ) -> None: """ Display a Plotly figure. @@ -217,10 +217,10 @@ def _show_figure( def _get_layout( self, - title, - axes_labels, - **kwargs, - ): + title: str, + axes_labels: object, + **kwargs: object, + ) -> object: """ Create a Plotly layout configuration. @@ -271,13 +271,13 @@ def _get_layout( def plot_powder( self, - x, - y_series, - labels, - axes_labels, - title, - height=None, - ): + x: object, + y_series: object, + labels: object, + axes_labels: object, + title: str, + height: int | None = None, + ) -> None: """ Render a line plot for powder diffraction data. @@ -318,13 +318,13 @@ def plot_powder( def plot_single_crystal( self, - x_calc, - y_meas, - y_meas_su, - axes_labels, - title, - height=None, - ): + x_calc: object, + y_meas: object, + y_meas_su: object, + axes_labels: object, + title: str, + height: int | None = None, + ) -> None: """ Render a scatter plot for single crystal diffraction data. diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index d1e33950..1ac47d03 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -56,7 +56,7 @@ class Plotter(RendererBase): # Private special methods # ------------------------------------------------------------------ - def __init__(self): + def __init__(self) -> None: super().__init__() # X-axis limits self._x_min = DEFAULT_MIN @@ -80,7 +80,7 @@ def _default_engine(cls) -> str: # Private helper methods # ------------------------------------------------------------------ - def _auto_x_range_for_ascii(self, pattern, x_array, x_min, x_max): + def _auto_x_range_for_ascii(self, pattern: object, x_array: object, x_min: object, x_max: object) -> tuple: """ For the ASCII engine, narrow the range around the tallest peak. @@ -110,11 +110,11 @@ def _auto_x_range_for_ascii(self, pattern, x_array, x_min, x_max): def _filtered_y_array( self, - y_array, - x_array, - x_min, - x_max, - ): + y_array: object, + x_array: object, + x_min: object, + x_max: object, + ) -> object: """ Filter an array by the inclusive x-range limits. @@ -144,7 +144,7 @@ def _filtered_y_array( return filtered_y_array - def _get_axes_labels(self, sample_form, scattering_type, x_axis): + def _get_axes_labels(self, sample_form: object, scattering_type: object, x_axis: object) -> list: """Look up axis labels for the given experiment / x-axis combination. """ @@ -152,16 +152,16 @@ def _get_axes_labels(self, sample_form, scattering_type, x_axis): def _prepare_powder_data( self, - pattern, - expt_name, - expt_type, - x_min, - x_max, - x, - need_meas=False, - need_calc=False, - show_residual=False, - ): + pattern: object, + expt_name: str, + expt_type: object, + x_min: object, + x_max: object, + x: object, + need_meas: bool = False, + need_calc: bool = False, + show_residual: bool = False, + ) -> dict | None: """ Validate, resolve axes, auto-range, and filter arrays. @@ -245,7 +245,7 @@ def _prepare_powder_data( 'x_axis': x_axis, } - def _resolve_x_axis(self, expt_type, x): + def _resolve_x_axis(self, expt_type: object, x: object) -> tuple: """ Determine the x-axis type from experiment metadata. @@ -274,12 +274,12 @@ def _resolve_x_axis(self, expt_type, x): # ------------------------------------------------------------------ @property - def x_min(self): + def x_min(self) -> float: """Minimum x-axis limit.""" return self._x_min @x_min.setter - def x_min(self, value): + def x_min(self, value: object) -> None: """ Set the minimum x-axis limit. @@ -294,12 +294,12 @@ def x_min(self, value): self._x_min = DEFAULT_MIN @property - def x_max(self): + def x_max(self) -> float: """Maximum x-axis limit.""" return self._x_max @x_max.setter - def x_max(self, value): + def x_max(self, value: object) -> None: """ Set the maximum x-axis limit. @@ -314,12 +314,12 @@ def x_max(self, value): self._x_max = DEFAULT_MAX @property - def height(self): + def height(self) -> int: """Plot height (rows for ASCII, pixels for Plotly).""" return self._height @height.setter - def height(self, value): + def height(self, value: object) -> None: """ Set plot height. @@ -337,7 +337,7 @@ def height(self, value): # Public methods # ------------------------------------------------------------------ - def show_config(self): + def show_config(self) -> None: """Display the current plotting configuration.""" headers = [ ('Parameter', 'left'), @@ -354,13 +354,13 @@ def show_config(self): def plot_meas( self, - pattern, - expt_name, - expt_type, - x_min=None, - x_max=None, - x=None, - ): + pattern: object, + expt_name: str, + expt_type: object, + x_min: object = None, + x_max: object = None, + x: object = None, + ) -> None: """ Plot measured pattern using the current engine. @@ -404,13 +404,13 @@ def plot_meas( def plot_calc( self, - pattern, - expt_name, - expt_type, - x_min=None, - x_max=None, - x=None, - ): + pattern: object, + expt_name: str, + expt_type: object, + x_min: object = None, + x_max: object = None, + x: object = None, + ) -> None: """ Plot calculated pattern using the current engine. @@ -454,14 +454,14 @@ def plot_calc( def plot_meas_vs_calc( self, - pattern, - expt_name, - expt_type, - x_min=None, - x_max=None, - show_residual=False, - x=None, - ): + pattern: object, + expt_name: str, + expt_type: object, + x_min: object = None, + x_max: object = None, + show_residual: bool = False, + x: object = None, + ) -> None: """ Plot measured and calculated series and optional residual. diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index 00daed6f..fcc5fa5b 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -10,7 +10,6 @@ from abc import ABC from abc import abstractmethod -from typing import Any from IPython import get_ipython from rich.color import Color @@ -33,7 +32,7 @@ def __init__(self) -> None: super().__init__() self._float_fmt = f'{{:.{self.FLOAT_PRECISION}f}}'.format - def _format_value(self, value: Any) -> Any: + def _format_value(self, value: object) -> object: """ Format floats with fixed precision and others as strings. @@ -65,7 +64,7 @@ def _is_dark_theme(self) -> bool: return is_dark() - def _rich_to_hex(self, color): + def _rich_to_hex(self, color: str) -> str: """ Convert a Rich color name to a CSS-style hex string. @@ -97,10 +96,10 @@ def _pandas_border_color(self) -> str: @abstractmethod def render( self, - alignments, - df, - display_handle: Any | None = None, - ) -> Any: + alignments: object, + df: object, + display_handle: object | None = None, + ) -> object: """ Render the provided DataFrame with backend-specific styling. diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index b4d5eaa2..ba20bafa 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -4,8 +4,6 @@ from __future__ import annotations -from typing import Any - try: from IPython.display import HTML from IPython.display import display @@ -80,7 +78,7 @@ def _build_base_styles(self, color: str) -> list[dict]: }, ] - def _build_header_alignment_styles(self, df, alignments) -> list[dict]: + def _build_header_alignment_styles(self, df: object, alignments: object) -> list[dict]: """ Generate header cell alignment styles per column. @@ -104,7 +102,7 @@ def _build_header_alignment_styles(self, df, alignments) -> list[dict]: for column, align in zip(df.columns, alignments, strict=False) ] - def _apply_styling(self, df, alignments, color: str): + def _apply_styling(self, df: object, alignments: object, color: str) -> object: """ Build a configured Styler with alignments and base styles. @@ -135,7 +133,7 @@ def _apply_styling(self, df, alignments, color: str): ) return styler - def _update_display(self, styler, display_handle) -> None: + def _update_display(self, styler: object, display_handle: object) -> None: """ Single, consistent update path for Jupyter. @@ -170,10 +168,10 @@ def _update_display(self, styler, display_handle) -> None: def render( self, - alignments, - df, - display_handle: Any | None = None, - ) -> Any: + alignments: object, + df: object, + display_handle: object | None = None, + ) -> object: """ Render a styled DataFrame. diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index 5c8bbf2a..41c34879 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -5,7 +5,6 @@ from __future__ import annotations import io -from typing import Any from rich.box import Box from rich.console import Console @@ -66,7 +65,7 @@ def _to_html(self, table: Table) -> str: ) return html - def _build_table(self, df, alignments, color: str) -> Table: + def _build_table(self, df: object, alignments: object, color: str) -> Table: """ Construct a Rich Table with formatted data and alignment. @@ -106,7 +105,7 @@ def _build_table(self, df, alignments, color: str) -> Table: return table - def _update_display(self, table: Table, display_handle) -> None: + def _update_display(self, table: Table, display_handle: object) -> None: """ Single, consistent update path for Jupyter and terminal. @@ -148,10 +147,10 @@ def _update_display(self, table: Table, display_handle) -> None: def render( self, - alignments, - df, - display_handle=None, - ) -> Any: + alignments: object, + df: object, + display_handle: object = None, + ) -> object: """ Render a styled table using Rich. diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index d12c7dbd..79c4ec70 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -5,7 +5,6 @@ from __future__ import annotations from enum import Enum -from typing import Any import pandas as pd @@ -65,7 +64,7 @@ def show_config(self) -> None: console.paragraph('Current tabler configuration') TableRenderer.get().render(df) - def render(self, df, display_handle: Any | None = None) -> Any: + def render(self, df: object, display_handle: object | None = None) -> object: """ Render a DataFrame as a table using the active backend. diff --git a/src/easydiffraction/io/cif/handler.py b/src/easydiffraction/io/cif/handler.py index 62fb59ac..4d56ee09 100644 --- a/src/easydiffraction/io/cif/handler.py +++ b/src/easydiffraction/io/cif/handler.py @@ -16,7 +16,7 @@ def __init__(self, *, names: list[str]) -> None: self._names = names self._owner = None # set by attach - def attach(self, owner): + def attach(self, owner: object) -> None: """Attach to a descriptor or parameter instance.""" self._owner = owner diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index cbea397a..cc38ffc9 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -22,7 +22,7 @@ from easydiffraction.core.variable import GenericDescriptorBase -def format_value(value) -> str: +def format_value(value: object) -> str: """Format a single CIF value, quoting strings with whitespace, and format floats with global precision. @@ -60,7 +60,7 @@ def format_value(value) -> str: ################## -def param_to_cif(param) -> str: +def param_to_cif(param: object) -> str: """Render a single descriptor/parameter to a CIF line. Expects ``param`` to expose ``_cif_handler.names`` and ``value``. @@ -70,7 +70,7 @@ def param_to_cif(param) -> str: return f'{main_key} {format_value(param.value)}' -def category_item_to_cif(item) -> str: +def category_item_to_cif(item: object) -> str: """Render a CategoryItem-like object to CIF text. Expects ``item.parameters`` iterable of params with @@ -83,7 +83,7 @@ def category_item_to_cif(item) -> str: def category_collection_to_cif( - collection, + collection: object, max_display: Optional[int] = 20, ) -> str: """Render a CategoryCollection-like object to CIF text. @@ -124,7 +124,7 @@ def category_collection_to_cif( return '\n'.join(lines) -def datablock_item_to_cif(datablock) -> str: +def datablock_item_to_cif(datablock: object) -> str: """Render a DatablockItem-like object to CIF text. Emits a data_ header and then concatenates category CIF sections. @@ -149,12 +149,12 @@ def datablock_item_to_cif(datablock) -> str: return '\n\n'.join(parts) -def datablock_collection_to_cif(collection) -> str: +def datablock_collection_to_cif(collection: object) -> str: """Render a collection of datablocks by joining their CIF blocks.""" return '\n\n'.join([block.as_cif for block in collection.values()]) -def project_info_to_cif(info) -> str: +def project_info_to_cif(info: object) -> str: """Render ProjectInfo to CIF text (id, title, description, dates). """ @@ -183,7 +183,7 @@ def project_info_to_cif(info) -> str: ) -def project_to_cif(project) -> str: +def project_to_cif(project: object) -> str: """Render a whole project by concatenating sections when present.""" parts: list[str] = [] if hasattr(project, 'info'): @@ -199,12 +199,12 @@ def project_to_cif(project) -> str: return '\n\n'.join([p for p in parts if p]) -def experiment_to_cif(experiment) -> str: +def experiment_to_cif(experiment: object) -> str: """Render an experiment: datablock part plus measured data.""" return datablock_item_to_cif(experiment) -def analysis_to_cif(analysis) -> str: +def analysis_to_cif(analysis: object) -> str: """Render analysis metadata, aliases, and constraints to CIF.""" cur_min = format_value(analysis.current_minimizer) lines: list[str] = [] @@ -221,7 +221,7 @@ def analysis_to_cif(analysis) -> str: return '\n'.join(lines) -def summary_to_cif(_summary) -> str: +def summary_to_cif(_summary: object) -> str: """Render a summary CIF block (placeholder for now).""" return 'To be added...' @@ -301,7 +301,7 @@ def category_collection_from_cif( # Iterate over category parameters and their possible CIF names # trying to find the whole loop it belongs to inside the CIF block - def _get_loop(block, category_item): + def _get_loop(block: object, category_item: object) -> object | None: for param in category_item.parameters: for name in param._cif_handler.names: loop = block.find_loop(name).get_loop() diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 8a2cf5ba..c015612a 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -94,42 +94,42 @@ def structures(self, structures: Structures) -> None: self._structures = structures @property - def experiments(self): + def experiments(self) -> Experiments: """Collection of experiments in the project.""" return self._experiments @experiments.setter @typechecked - def experiments(self, experiments: Experiments): + def experiments(self, experiments: Experiments) -> None: self._experiments = experiments @property - def plotter(self): + def plotter(self) -> Plotter: """Plotting facade bound to the project.""" return self._plotter @property - def tabler(self): + def tabler(self) -> TableRenderer: """Tables rendering facade bound to the project.""" return self._tabler @property - def analysis(self): + def analysis(self) -> Analysis: """Analysis entry-point bound to the project.""" return self._analysis @property - def summary(self): + def summary(self) -> Summary: """Summary report builder bound to the project.""" return self._summary @property - def parameters(self): + def parameters(self) -> list: """Return parameters from all structures and experiments.""" return self.structures.parameters + self.experiments.parameters @property - def as_cif(self): + def as_cif(self) -> str: """Export whole project as CIF text.""" # Concatenate sections using centralized CIF serializers return project_to_cif(self) @@ -216,7 +216,7 @@ def save_as( # Plotting # ------------------------------------------ - def _update_categories(self, expt_name) -> None: + def _update_categories(self, expt_name: str) -> None: for structure in self.structures: structure._update_categories() self.analysis._update_categories() @@ -225,11 +225,11 @@ def _update_categories(self, expt_name) -> None: def plot_meas( self, - expt_name, - x_min=None, - x_max=None, - x=None, - ): + expt_name: str, + x_min: float | None = None, + x_max: float | None = None, + x: object | None = None, + ) -> None: self._update_categories(expt_name) experiment = self.experiments[expt_name] @@ -244,11 +244,11 @@ def plot_meas( def plot_calc( self, - expt_name, - x_min=None, - x_max=None, - x=None, - ): + expt_name: str, + x_min: float | None = None, + x_max: float | None = None, + x: object | None = None, + ) -> None: self._update_categories(expt_name) experiment = self.experiments[expt_name] @@ -263,12 +263,12 @@ def plot_calc( def plot_meas_vs_calc( self, - expt_name, - x_min=None, - x_max=None, - show_residual=False, - x=None, - ): + expt_name: str, + x_min: float | None = None, + x_max: float | None = None, + show_residual: bool = False, + x: object | None = None, + ) -> None: self._update_categories(expt_name) experiment = self.experiments[expt_name] diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py index e641a1e5..d5e8f826 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_info.py @@ -69,7 +69,7 @@ def path(self) -> pathlib.Path: return self._path @path.setter - def path(self, value) -> None: + def path(self, value: object) -> None: # Accept str or Path; normalize to Path self._path = pathlib.Path(value) @@ -87,7 +87,7 @@ def update_last_modified(self) -> None: """Update the last modified timestamp.""" self._last_modified = datetime.datetime.now() - def parameters(self): + def parameters(self) -> None: """Placeholder for parameter listing.""" pass diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index 295fcfb9..71d852d0 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -15,7 +15,7 @@ class Summary: fitted model, experiments, and analysis results. """ - def __init__(self, project) -> None: + def __init__(self, project: object) -> None: """ Initialize the summary with a reference to the project. diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index 10478a27..dabf35de 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -55,7 +55,7 @@ class IconifiedRichHandler(RichHandler): logging.INFO: 'ℹ️', } - def __init__(self, *args, mode: str = 'compact', **kwargs): + def __init__(self, *args: object, mode: str = 'compact', **kwargs: object) -> None: super().__init__(*args, **kwargs) self.mode = mode @@ -272,14 +272,14 @@ def compact_excepthook( sys.excepthook = compact_excepthook # type: ignore[assignment] @staticmethod - def restore_original_hook(): + def restore_original_hook() -> None: """Restore the original sys.excepthook if it was overridden.""" if hasattr(Logger, '_orig_excepthook'): sys.excepthook = Logger._orig_excepthook # type: ignore[attr-defined] # Jupyter-specific traceback suppression (inlined here) @staticmethod - def _suppress_traceback(logger): + def _suppress_traceback(logger: object) -> object: """ Build a Jupyter custom exception callback that logs only the message. @@ -294,7 +294,7 @@ def _suppress_traceback(logger): full tracebacks and logs only the exception message. """ - def suppress_jupyter_traceback(*args, **kwargs): + def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: try: _evalue = ( args[2] if len(args) > 2 else kwargs.get('_evalue') or kwargs.get('evalue') @@ -350,7 +350,7 @@ class Mode(Enum): COMPACT = 'compact' # single line; no traceback @classmethod - def default(cls): + def default(cls) -> Logger.Mode: return cls.COMPACT class Level(IntEnum): @@ -363,7 +363,7 @@ class Level(IntEnum): CRITICAL = logging.CRITICAL @classmethod - def default(cls): + def default(cls) -> Logger.Level: return cls.WARNING class Reaction(Enum): @@ -373,7 +373,7 @@ class Reaction(Enum): WARN = auto() @classmethod - def default(cls): + def default(cls) -> Logger.Reaction: return cls.RAISE # --- Internal state --- @@ -533,7 +533,7 @@ class ConsolePrinter: _console = ConsoleManager.get() @classmethod - def print(cls, *objects, **kwargs): + def print(cls, *objects: object, **kwargs: object) -> None: """Print objects to the console with left padding. - Renderables (Rich types like Text, Table, Panel, etc.) are diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 0259fc26..41070cb2 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -313,7 +313,7 @@ def _get_version_for_url(package_name: str = 'easydiffraction') -> str: return stripped_package_version(package_name) or 'dev' -def _safe_urlopen(request_or_url): # type: ignore[no-untyped-def] +def _safe_urlopen(request_or_url: object) -> object: # type: ignore[no-untyped-def] """Wrapper for urlopen with prior validation. Centralises lint suppression for validated HTTPS requests. @@ -511,11 +511,11 @@ def show_version() -> None: # TODO: This is a temporary utility function. Complete migration to # TableRenderer (as e.g. in show_all_params) and remove this. def render_table( - columns_data, - columns_alignment, - columns_headers=None, - display_handle=None, -): + columns_data: object, + columns_alignment: object, + columns_headers: object = None, + display_handle: object = None, +) -> None: headers = [ (col, align) for col, align in zip(columns_headers, columns_alignment, strict=False) ] @@ -525,7 +525,7 @@ def render_table( tabler.render(df, display_handle=display_handle) -def render_cif(cif_text) -> None: +def render_cif(cif_text: str) -> None: """ Display the CIF text as a formatted table in Jupyter Notebook or terminal. @@ -554,7 +554,7 @@ def tof_to_d( offset: float, linear: float, quad: float, - quad_eps=1e-20, + quad_eps: float = 1e-20, ) -> np.ndarray: """ Convert time-of-flight (TOF) to d-spacing using a quadratic calibration. @@ -642,7 +642,7 @@ def tof_to_d( return d_out -def twotheta_to_d(twotheta, wavelength): +def twotheta_to_d(twotheta: object, wavelength: float) -> object: """ Convert 2-theta to d-spacing using Bragg's law. @@ -663,7 +663,7 @@ def twotheta_to_d(twotheta, wavelength): return d -def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda): +def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda: object) -> object: """ Convert sin(theta)/lambda to d-spacing. @@ -683,7 +683,7 @@ def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda): return d -def get_value_from_xye_header(file_path, key): +def get_value_from_xye_header(file_path: str, key: str) -> float: """ Extracts a floating point value from the first line of the file, corresponding to the given key. From 1ae2177f5d9e28631e41843100df22d4a870be79 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 14:21:44 +0100 Subject: [PATCH 27/48] Apply linting and formatting --- src/easydiffraction/core/validation.py | 10 ++++++++-- .../experiment/categories/data/bragg_sc.py | 7 ++++++- src/easydiffraction/display/plotting.py | 15 +++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/easydiffraction/core/validation.py b/src/easydiffraction/core/validation.py index f08d8152..e224c8f7 100644 --- a/src/easydiffraction/core/validation.py +++ b/src/easydiffraction/core/validation.py @@ -41,7 +41,7 @@ def __str__(self) -> str: return self.name.lower() @property - def expected_type(self) -> DataTypes: + def expected_type(self) -> tuple: """Convenience alias for tuple of allowed Python types.""" return self.value @@ -72,7 +72,13 @@ class ValidatorBase(ABC): """Abstract base class for all validators.""" @abstractmethod - def validated(self, value: object, name: str, default: object = None, current: object = None) -> object: + def validated( + self, + value: object, + name: str, + default: object = None, + current: object = None, + ) -> object: """Return a validated value or fallback. Subclasses must implement this method. diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 663118f3..2f6e5b83 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -257,7 +257,12 @@ def __init__(self) -> None: # Should be set only once - def _create_items_set_hkl_and_id(self, indices_h: object, indices_k: object, indices_l: object) -> None: + def _create_items_set_hkl_and_id( + self, + indices_h: object, + indices_k: object, + indices_l: object, + ) -> None: """Helper method to set Miller indices.""" # TODO: split into multiple methods diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 1ac47d03..86b3a852 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -80,7 +80,13 @@ def _default_engine(cls) -> str: # Private helper methods # ------------------------------------------------------------------ - def _auto_x_range_for_ascii(self, pattern: object, x_array: object, x_min: object, x_max: object) -> tuple: + def _auto_x_range_for_ascii( + self, + pattern: object, + x_array: object, + x_min: object, + x_max: object, + ) -> tuple: """ For the ASCII engine, narrow the range around the tallest peak. @@ -144,7 +150,12 @@ def _filtered_y_array( return filtered_y_array - def _get_axes_labels(self, sample_form: object, scattering_type: object, x_axis: object) -> list: + def _get_axes_labels( + self, + sample_form: object, + scattering_type: object, + x_axis: object, + ) -> list: """Look up axis labels for the given experiment / x-axis combination. """ From 19d59f5c09fdaf32cf8b465728b4242cb0359f6a Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 14:54:42 +0100 Subject: [PATCH 28/48] Run docstring format fix for src/ only --- pixi.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixi.toml b/pixi.toml index 1cbf7fbb..a4b248ea 100644 --- a/pixi.toml +++ b/pixi.toml @@ -119,7 +119,7 @@ check = 'pre-commit run --hook-stage manual --all-files' ########## param-docstring-fix = 'python tools/param_consistency.py src/ --fix' -docstring-format-fix = 'format-docstring src/ docs/docs/tutorials/' +docstring-format-fix = 'format-docstring src/' notebook-lint-fix = 'nbqa ruff --fix docs/docs/tutorials/' py-lint-fix = 'ruff check --fix src/ tests/ docs/docs/tutorials/' py-format-fix = 'ruff format src/ tests/ docs/docs/tutorials/' From bbc6323ac481dc695c173fe08590f9eddcd2589a Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 15:29:46 +0100 Subject: [PATCH 29/48] Add types to the Parameters sections in docstrings --- src/easydiffraction/analysis/analysis.py | 24 +- .../analysis/calculators/crysfml.py | 32 +- .../analysis/calculators/cryspy.py | 35 +- .../analysis/categories/aliases/default.py | 7 - .../categories/constraints/default.py | 7 - .../joint_fit_experiments/default.py | 6 - .../analysis/fit_helpers/metrics.py | 39 +- .../analysis/fit_helpers/reporting.py | 30 +- .../analysis/fit_helpers/tracking.py | 9 +- src/easydiffraction/analysis/fitting.py | 30 +- .../analysis/minimizers/base.py | 14 +- .../analysis/minimizers/dfols.py | 7 +- .../analysis/minimizers/lmfit.py | 27 +- src/easydiffraction/core/category.py | 4 +- src/easydiffraction/core/collection.py | 9 +- src/easydiffraction/core/datablock.py | 2 +- src/easydiffraction/core/factory.py | 44 ++- src/easydiffraction/core/guard.py | 8 +- src/easydiffraction/core/metadata.py | 9 +- src/easydiffraction/core/variable.py | 29 +- .../crystallography/crystallography.py | 14 +- .../categories/experiment_type/default.py | 13 +- .../datablocks/experiment/collection.py | 24 +- .../datablocks/experiment/item/base.py | 34 +- .../datablocks/experiment/item/bragg_pd.py | 2 +- .../datablocks/experiment/item/factory.py | 30 +- .../categories/atom_sites/default.py | 2 +- .../datablocks/structure/item/base.py | 6 +- src/easydiffraction/display/base.py | 3 +- src/easydiffraction/display/plotters/ascii.py | 27 +- src/easydiffraction/display/plotters/base.py | 24 +- .../display/plotters/plotly.py | 68 ++-- src/easydiffraction/display/plotting.py | 86 +++-- src/easydiffraction/display/tablers/base.py | 16 +- src/easydiffraction/display/tablers/pandas.py | 30 +- src/easydiffraction/display/tablers/rich.py | 28 +- src/easydiffraction/display/tables.py | 5 +- src/easydiffraction/summary/summary.py | 2 +- .../utils/_vendored/theme_detect.py | 12 +- src/easydiffraction/utils/logging.py | 26 +- src/easydiffraction/utils/utils.py | 80 ++-- tools/check_param_consistency.py | 365 ------------------ 42 files changed, 483 insertions(+), 786 deletions(-) delete mode 100644 tools/check_param_consistency.py diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index cd188df0..d9c5442b 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -38,14 +38,6 @@ class Analysis: - Display or filter parameters to fit. - Select a calculator/minimizer implementation. - Calculate patterns and run single or joint fits. - - Attributes - ---------- - project - The parent Project object. aliases: A registry of human-friendly - aliases for parameters. constraints: Symbolic constraints - between parameters. calculator: Active calculator used for - computations. fitter: Active fitter/minimizer driver. """ def __init__(self, project: object) -> None: @@ -54,7 +46,7 @@ def __init__(self, project: object) -> None: Parameters ---------- - project + project : object The project that owns models and experiments. """ self.project = project @@ -145,7 +137,7 @@ def aliases_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Aliases tag (e.g. ``'default'``). """ supported_tags = AliasesFactory.supported_tags() @@ -186,7 +178,7 @@ def constraints_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Constraints tag (e.g. ``'default'``). """ supported_tags = ConstraintsFactory.supported_tags() @@ -220,11 +212,12 @@ def _get_params_as_dataframe( Parameters ---------- - params + params : List[Union[NumericDescriptor, Parameter]] List of DescriptorFloat or Parameter objects. Returns ------- + pd.DataFrame A pandas DataFrame containing parameter information. """ records = [] @@ -503,7 +496,7 @@ def current_minimizer(self, selection: str) -> None: Parameters ---------- - selection + selection : str Minimizer selection string, e.g. 'lmfit'. """ self.fitter = Fitter(selection) @@ -531,7 +524,7 @@ def fit_mode_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Fit-mode tag (e.g. ``'default'``). """ supported_tags = FitModeFactory.supported_tags() @@ -710,7 +703,7 @@ def _update_categories(self, called_by_minimizer: bool = False) -> None: Parameters ---------- - called_by_minimizer + called_by_minimizer : bool, default=False Whether this is called during fitting. """ # Apply constraints to sync dependent parameters @@ -729,6 +722,7 @@ def as_cif(self) -> str: Returns ------- + str The analysis section represented as a CIF document string. """ from easydiffraction.io.cif.serialize import analysis_to_cif diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 02d78d90..4605f275 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -53,10 +53,15 @@ def calculate_structure_factors( Parameters ---------- - structures + structures : Structures The structures to calculate structure factors for. - experiments + experiments : Experiments The experiments associated with the sample models. + + Raises + ------ + NotImplementedError + HKL calculation is not implemented for CrysfmlCalculator. """ raise NotImplementedError('HKL calculation is not implemented for CrysfmlCalculator.') @@ -72,15 +77,16 @@ def calculate_pattern( Parameters ---------- - structure + structure : Structures The structure to calculate the pattern for. - experiment + experiment : ExperimentBase The experiment associated with the structure. - called_by_minimizer + called_by_minimizer : bool, default=False Whether the calculation is called by a minimizer. Returns ------- + Union[np.ndarray, List[float]] The calculated diffraction pattern as a NumPy array or a list of floats. """ @@ -106,13 +112,14 @@ def _adjust_pattern_length( Parameters ---------- - pattern + pattern : List[float] The pattern to adjust. - target_length + target_length : int The desired length of the pattern. Returns ------- + List[float] The adjusted pattern. """ # TODO: Check the origin of this discrepancy coming from @@ -132,13 +139,14 @@ def _crysfml_dict( Parameters ---------- - structure + structure : Structures The structure to convert. - experiment + experiment : ExperimentBase The experiment to convert. Returns ------- + Dict[str, Union[ExperimentBase, Structure]] A dictionary representation of the structure and experiment. """ structure_dict = self._convert_structure_to_dict(structure) @@ -157,11 +165,12 @@ def _convert_structure_to_dict( Parameters ---------- - structure + structure : Structure The structure to convert. Returns ------- + Dict[str, Any] A dictionary representation of the structure. """ structure_dict = { @@ -201,11 +210,12 @@ def _convert_experiment_to_dict( Parameters ---------- - experiment + experiment : ExperimentBase The experiment to convert. Returns ------- + Dict[str, Any] A dictionary representation of the experiment. """ expt_type = getattr(experiment, 'type', None) diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 829ce30c..0d1354e5 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -66,11 +66,11 @@ def calculate_structure_factors( Parameters ---------- - structure + structure : Structure The structure to calculate structure factors for. - experiment + experiment : ExperimentBase The experiment associated with the sample models. - called_by_minimizer + called_by_minimizer : bool, default=False Whether the calculation is called by a minimizer. """ combined_name = f'{structure.name}_{experiment.name}' @@ -131,17 +131,18 @@ def calculate_pattern( Parameters ---------- - structure + structure : Structure The structure to calculate the pattern for. - experiment + experiment : ExperimentBase The experiment associated with the structure. - called_by_minimizer + called_by_minimizer : bool, default=False Whether the calculation is called by a minimizer. Returns ------- - The calculated diffraction pattern as a NumPy array or a list of - floats. + Union[np.ndarray, List[float]] + The calculated diffraction pattern as a NumPy array or a + list of floats. """ combined_name = f'{structure.name}_{experiment.name}' @@ -204,13 +205,14 @@ def _recreate_cryspy_dict( Parameters ---------- - structure + structure : Structure The structure to update. - experiment + experiment : ExperimentBase The experiment to update. Returns ------- + Dict[str, Any] The updated Cryspy dictionary. """ combined_name = f'{structure.name}_{experiment.name}' @@ -322,13 +324,14 @@ def _recreate_cryspy_obj( Parameters ---------- - structure + structure : Structure The structure to recreate. - experiment + experiment : ExperimentBase The experiment to recreate. Returns ------- + object The recreated Cryspy object. """ cryspy_obj = str_to_globaln('') @@ -357,11 +360,12 @@ def _convert_structure_to_cryspy_cif( Parameters ---------- - structure + structure : Structure The structure to convert. Returns ------- + str The Cryspy CIF string representation of the structure. """ return structure.as_cif @@ -376,13 +380,14 @@ def _convert_experiment_to_cryspy_cif( Parameters ---------- - experiment + experiment : ExperimentBase The experiment to convert. - linked_structure + linked_structure : object The structure linked to the experiment. Returns ------- + str The Cryspy CIF string representation of the experiment. """ # Try to get experiment attributes diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 146bd22f..b6a4d32f 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -24,13 +24,6 @@ class Alias(CategoryItem): Maps a human-readable ``label`` to a concrete ``param_uid`` used by the engine. - - Parameters - ---------- - label - Alias label. Must match ``^[A-Za- z_][A-Za-z0-9_]*$``. - param_uid: Target parameter uid. Same identifier pattern as - ``label``. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 7f5468a5..77f57fdb 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -22,13 +22,6 @@ class Constraint(CategoryItem): """ Single constraint item. - - Parameters - ---------- - lhs_alias - Left-hand side alias name being constrained. - rhs_expr - Right-hand side expression as a string. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index edcc4283..cca7a48e 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -25,12 +25,6 @@ class JointFitExperiment(CategoryItem): """ A single joint-fit entry. - - Parameters - ---------- - id - Experiment identifier used in the fit session. weight: Relative - weight factor in the combined objective. """ def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index 2f9c61cc..8616593f 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -20,13 +20,14 @@ def calculate_r_factor( Parameters ---------- - y_obs + y_obs : np.ndarray Observed data points. - y_calc + y_calc : np.ndarray Calculated data points. Returns ------- + float R-factor value. """ y_obs = np.asarray(y_obs) @@ -46,15 +47,16 @@ def calculate_weighted_r_factor( Parameters ---------- - y_obs + y_obs : np.ndarray Observed data points. - y_calc + y_calc : np.ndarray Calculated data points. - weights + weights : np.ndarray Weights for each data point. Returns ------- + float Weighted R-factor value. """ y_obs = np.asarray(y_obs) @@ -74,13 +76,14 @@ def calculate_rb_factor( Parameters ---------- - y_obs + y_obs : np.ndarray Observed data points. - y_calc + y_calc : np.ndarray Calculated data points. Returns ------- + float Bragg R-factor value. """ y_obs = np.asarray(y_obs) @@ -99,13 +102,14 @@ def calculate_r_factor_squared( Parameters ---------- - y_obs + y_obs : np.ndarray Observed data points. - y_calc + y_calc : np.ndarray Calculated data points. Returns ------- + float R-factor squared value. """ y_obs = np.asarray(y_obs) @@ -124,13 +128,14 @@ def calculate_reduced_chi_square( Parameters ---------- - residuals + residuals : np.ndarray Residuals between observed and calculated data. - num_parameters + num_parameters : int Number of free parameters used in the model. Returns ------- + float Reduced chi-square value. """ residuals = np.asarray(residuals) @@ -153,15 +158,19 @@ def get_reliability_inputs( Parameters ---------- - structures + structures : Structures Collection of structures. - experiments + experiments : Experiments Collection of experiments. Returns ------- - Tuple containing arrays of (observed values, calculated values, - error values) + np.ndarray + Observed values. + np.ndarray + Calculated values. + Optional[np.ndarray] + Error values, or None if not available. """ y_obs_all = [] y_calc_all = [] diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index 64abb528..e851306b 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -38,25 +38,25 @@ def __init__( Parameters ---------- - success + success : bool, default=False Indicates if the fit was successful. - parameters + parameters : Optional[List[object]], default=None List of parameters used in the fit. - chi_square + chi_square : Optional[float], default=None Chi-square value of the fit. - reduced_chi_square + reduced_chi_square : Optional[float], default=None Reduced chi-square value of the fit. - message + message : str, default='' Message related to the fit. - iterations + iterations : int, default=0 Number of iterations performed. - engine_result + engine_result : Optional[object], default=None Result from the fitting engine. - starting_parameters + starting_parameters : Optional[List[object]], default=None Initial parameters for the fit. - fitting_time + fitting_time : Optional[float], default=None Time taken for the fitting process. - **kwargs + **kwargs : object Additional engine-specific fields. If ``redchi`` is provided and ``reduced_chi_square`` is not set, it is used as the reduced chi-square value. @@ -93,15 +93,15 @@ def display_results( Parameters ---------- - y_obs + y_obs : Optional[List[float]], default=None Observed intensities for pattern R-factor metrics. - y_calc + y_calc : Optional[List[float]], default=None Calculated intensities for pattern R-factor metrics. - y_err + y_err : Optional[List[float]], default=None Standard deviations of observed intensities for wR. - f_obs + f_obs : Optional[List[float]], default=None Observed structure-factor magnitudes for Bragg R. - f_calc + f_calc : Optional[List[float]], default=None Calculated structure-factor magnitudes for Bragg R. """ status_icon = '✅' if self.success else '❌' diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index 2450a931..6f86194e 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -115,13 +115,14 @@ def track( Parameters ---------- - residuals + residuals : np.ndarray Residuals between measured and calculated data. - parameters + parameters : List[float] Current free parameters being fitted. Returns ------- + np.ndarray Residuals unchanged, for optimizer consumption. """ self._iteration += 1 @@ -208,7 +209,7 @@ def start_tracking(self, minimizer_name: str) -> None: Parameters ---------- - minimizer_name + minimizer_name : str Name of the minimizer used for the run. """ console.print(f"🚀 Starting fit process with '{minimizer_name}'...") @@ -232,7 +233,7 @@ def add_tracking_info(self, row: List[str]) -> None: Parameters ---------- - row + row : List[str] Columns corresponding to DEFAULT_HEADERS. """ # Append and update via the active handle (Jupyter or diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index 5d2681fa..c807b7b0 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -44,13 +44,13 @@ def fit( Parameters ---------- - structures + structures : Structures Collection of structures. - experiments + experiments : Experiments Collection of experiments. - weights + weights : Optional[np.array], default=None Optional weights for joint fitting. - analysis + analysis : object, default=None Optional Analysis object to update its categories during fitting. """ @@ -90,9 +90,9 @@ def _process_fit_results( Parameters ---------- - structures + structures : Structures Collection of structures. - experiments + experiments : Experiments Collection of experiments. """ y_obs, y_calc, y_err = get_reliability_inputs( @@ -122,13 +122,14 @@ def _collect_free_parameters( Parameters ---------- - structures + structures : Structures Collection of structures. - experiments + experiments : Experiments Collection of experiments. Returns ------- + List[Parameter] List of free parameters. """ free_params: List[Parameter] = structures.free_parameters + experiments.free_parameters @@ -150,22 +151,23 @@ def _residual_function( Parameters ---------- - engine_params + engine_params : Dict[str, Any] Engine-specific parameter dict. - parameters + parameters : List[Parameter] List of parameters being optimized. - structures + structures : Structures Collection of structures. - experiments + experiments : Experiments Collection of experiments. - weights + weights : Optional[np.array], default=None Optional weights for joint fitting. - analysis + analysis : object, default=None Optional Analysis object to update its categories during fitting. Returns ------- + np.ndarray Array of weighted residuals. """ # Sync parameters back to objects diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index 7702a480..a0af66de 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -47,7 +47,7 @@ def _start_tracking(self, minimizer_name: str) -> None: Parameters ---------- - minimizer_name + minimizer_name : str Human-readable name shown in progress. """ self.tracker.reset() @@ -66,11 +66,12 @@ def _prepare_solver_args(self, parameters: List[Any]) -> Dict[str, Any]: Parameters ---------- - parameters + parameters : List[Any] List of free parameters to be fitted. Returns ------- + Dict[str, Any] Mapping of keyword arguments to pass into ``_run_solver``. """ pass @@ -105,9 +106,9 @@ def _finalize_fit( Parameters ---------- - parameters + parameters : List[object] Parameters after the solver finished. - raw_result + raw_result : object Backend-specific solver output object. Returns @@ -142,14 +143,15 @@ def fit( Parameters ---------- - parameters + parameters : List[object] Free parameters to optimize. - objective_function + objective_function : Callable[..., object] Callable returning residuals for a given set of engine arguments. Returns ------- + FitResults FitResults with success flag, best chi2 and timing. """ minimizer_name = self.name or 'Unnamed Minimizer' diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index 9777ed83..59878437 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -61,9 +61,9 @@ def _sync_result_to_parameters( Parameters ---------- - parameters + parameters : List[object] List of parameters being optimized. - raw_result + raw_result : object The result object returned by the solver. """ # Ensure compatibility with raw_result coming from dfols.solve() @@ -83,11 +83,12 @@ def _check_success(self, raw_result: object) -> bool: Parameters ---------- - raw_result + raw_result : object The result object returned by the solver. Returns ------- + bool True if the optimization was successful, False otherwise. """ return raw_result.flag == raw_result.EXIT_SUCCESS diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index ed57100d..834f1804 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -44,11 +44,12 @@ def _prepare_solver_args( Parameters ---------- - parameters + parameters : List[object] List of parameters to be optimized. Returns ------- + Dict[str, object] A dictionary containing the prepared lmfit. Parameters object. """ engine_parameters = lmfit.Parameters() @@ -68,13 +69,14 @@ def _run_solver(self, objective_function: object, **kwargs: object) -> object: Parameters ---------- - objective_function - The objective function to - minimize. **kwargs + objective_function : object + The objective function to minimize. + **kwargs : object Additional arguments for the solver. Returns ------- + object The result of the lmfit minimization. """ engine_parameters = kwargs.get('engine_parameters') @@ -97,9 +99,9 @@ def _sync_result_to_parameters( Parameters ---------- - parameters + parameters : List[object] List of parameters being optimized. - raw_result + raw_result : object The result object returned by the solver. """ param_values = raw_result.params if hasattr(raw_result, 'params') else raw_result @@ -118,11 +120,12 @@ def _check_success(self, raw_result: object) -> bool: Parameters ---------- - raw_result + raw_result : object The result object returned by the solver. Returns ------- + bool True if the optimization was successful, False otherwise. """ return getattr(raw_result, 'success', False) @@ -140,15 +143,15 @@ def _iteration_callback( Parameters ---------- - params + params : lmfit.Parameters The current parameters. - iter + iter : int The current iteration number. - resid + resid : object The residuals. - *args + *args : object Additional positional arguments. - **kwargs + **kwargs : object Additional keyword arguments. """ # Intentionally unused, required by callback signature diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index d0773bfd..8cb21271 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -221,7 +221,7 @@ def add(self, item: object) -> None: Parameters ---------- - item + item : object A ``CategoryItem`` instance to add. """ self[item._identity.category_entry_name] = item @@ -236,7 +236,7 @@ def create(self, **kwargs: object) -> None: Parameters ---------- - **kwargs + **kwargs : object Attribute names and values for the new item. """ child_obj = self._item_type() diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index 3db3d63c..e5164e41 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -21,7 +21,7 @@ class CollectionBase(GuardedBase): Parameters ---------- - item_type + item_type : type Type of items accepted by the collection. Used for validation and tooling; not enforced at runtime here. """ @@ -86,7 +86,7 @@ def remove(self, name: str) -> None: Parameters ---------- - name + name : str Identity key of the item to remove. Raises @@ -94,7 +94,10 @@ def remove(self, name: str) -> None: KeyError If no item with the given key exists. """ - del self[name] + try: + del self[name] + except KeyError: + raise def _key_for(self, item: GuardedBase) -> str | None: """Return the identity key for *item*. diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index daf557fd..066f9435 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -138,7 +138,7 @@ def add(self, item: object) -> None: Parameters ---------- - item + item : object A ``DatablockItem`` instance (e.g. a ``Structure`` or ``ExperimentBase`` subclass). """ diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index 6c14bae0..af7395d6 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -89,12 +89,13 @@ def default_tag(cls, **conditions: object) -> str: Parameters ---------- - **conditions + **conditions : object Experimental-axis values, e.g. ``scattering_type=ScatteringTypeEnum.BRAGG``. Returns ------- + str The resolved default tag string. Raises @@ -129,11 +130,16 @@ def create(cls, tag: str, **kwargs: object) -> object: Parameters ---------- - tag + tag : str ``type_info.tag`` value. - **kwargs + **kwargs : object Forwarded to the class constructor. + Returns + ------- + object + A new instance of the registered class. + Raises ------ ValueError @@ -153,8 +159,13 @@ def create_default_for(cls, **conditions: object) -> object: Parameters ---------- - **conditions + **conditions : object Experimental-axis values. + + Returns + ------- + object + A new instance of the default class. """ tag = cls.default_tag(**conditions) return cls.create(tag) @@ -178,16 +189,21 @@ def supported_for( Parameters ---------- - calculator + calculator : object, default=None Optional ``CalculatorEnum`` value. - sample_form + sample_form : object, default=None Optional ``SampleFormEnum`` value. - scattering_type + scattering_type : object, default=None Optional ``ScatteringTypeEnum`` value. - beam_mode + beam_mode : object, default=None Optional ``BeamModeEnum`` value. - radiation_probe + radiation_probe : object, default=None Optional ``RadiationProbeEnum`` value. + + Returns + ------- + List[Type] + Classes matching the given conditions. """ result = [] for klass in cls._supported_map().values(): @@ -224,15 +240,15 @@ def show_supported( Parameters ---------- - calculator + calculator : object, default=None Optional ``CalculatorEnum`` filter. - sample_form + sample_form : object, default=None Optional ``SampleFormEnum`` filter. - scattering_type + scattering_type : object, default=None Optional ``ScatteringTypeEnum`` filter. - beam_mode + beam_mode : object, default=None Optional ``BeamModeEnum`` filter. - radiation_probe + radiation_probe : object, default=None Optional ``RadiationProbeEnum`` filter. """ matching = cls.supported_for( diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index 1ec403da..fa67426b 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -84,9 +84,8 @@ def _iter_properties(cls) -> Generator[tuple[str, property], None, None]: Yields ------ - Each (key, property) pair for - - public attributes. + tuple[str, property] + Each (key, property) pair for public attributes. """ for base in cls.mro(): for key, attr in base.__dict__.items(): @@ -167,7 +166,8 @@ def _iter_methods(cls) -> Generator[tuple[str, object], None, None]: Yields ------ - Each (name, function) pair. + tuple[str, object] + Each (name, function) pair. """ seen: set = set() for base in cls.mro(): diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index 68626640..9d721f5c 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -23,12 +23,12 @@ class TypeInfo: Attributes ---------- - tag + tag : str Short, stable string identifier used for serialization, user-facing selection, and factory lookup. Must be unique within a factory's registry. Examples: ``'line-segment'``, ``'pseudo-voigt'``, ``'cryspy'``. - description + description : str, default='' One-line human-readable explanation. Used in ``show_supported()`` tables and documentation. """ @@ -90,7 +90,7 @@ class CalculatorSupport: Attributes ---------- - calculators + calculators : FrozenSet, default=frozenset() Frozenset of ``CalculatorEnum`` values. Empty means "any calculator" (no restriction). """ @@ -103,11 +103,12 @@ def supports(self, calculator: object) -> bool: Parameters ---------- - calculator + calculator : object A ``CalculatorEnum`` value. Returns ------- + bool ``True`` if the calculator is in the set, or if the set is empty (meaning any calculator is accepted). """ diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 136654b4..1733101b 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -29,18 +29,10 @@ class GenericDescriptorBase(GuardedBase): """ Base class for all parameter-like descriptors. - A descriptor encapsulates a typed value with validation, human- readable + A descriptor encapsulates a typed value with validation, human-readable name/description and a globally unique identifier that is stable across the session. Concrete subclasses specialize the expected data type and can extend the public API with additional behavior (e.g. units). - - Attributes - ---------- - name - Local parameter name (e.g. 'a', 'b_iso'). - description - Optional human-readable description. uid: Stable random - identifier for external references. """ _BOOL_SPEC_TEMPLATE = AttributeSpec( @@ -60,11 +52,11 @@ def __init__( Parameters ---------- - value_spec + value_spec : AttributeSpec Validation specification for the value. - name + name : str Local name of the descriptor within its category. - description + description : str, default=None Optional human-readable description. """ super().__init__() @@ -117,7 +109,6 @@ def unique_name(self) -> str: """Fully qualified name including datablock, category and entry name. """ - # 7c: Use filter(None, [...]) parts = [ self._identity.datablock_entry_name, self._identity.category_code, @@ -400,9 +391,9 @@ def __init__( Parameters ---------- - cif_handler + cif_handler : CifHandler Object that tracks CIF identifiers. - **kwargs + **kwargs : object Forwarded to GenericStringDescriptor. """ super().__init__(**kwargs) @@ -425,9 +416,9 @@ def __init__( Parameters ---------- - cif_handler + cif_handler : CifHandler Object that tracks CIF identifiers. - **kwargs + **kwargs : object Forwarded to GenericNumericDescriptor. """ super().__init__(**kwargs) @@ -450,9 +441,9 @@ def __init__( Parameters ---------- - cif_handler + cif_handler : CifHandler Object that tracks CIF identifiers. - **kwargs + **kwargs : object Forwarded to GenericParameter. """ super().__init__(**kwargs) diff --git a/src/easydiffraction/crystallography/crystallography.py b/src/easydiffraction/crystallography/crystallography.py index da60a762..b58b19a4 100644 --- a/src/easydiffraction/crystallography/crystallography.py +++ b/src/easydiffraction/crystallography/crystallography.py @@ -26,13 +26,14 @@ def apply_cell_symmetry_constraints( Parameters ---------- - cell + cell : Dict[str, float] Dictionary containing lattice parameters. - name_hm + name_hm : str Hermann-Mauguin symbol of the space group. Returns ------- + Dict[str, float] The cell dictionary with applied symmetry constraints. """ it_number = get_it_number_by_name_hm_short(name_hm) @@ -99,17 +100,18 @@ def apply_atom_site_symmetry_constraints( Parameters ---------- - atom_site + atom_site : Dict[str, Any] Dictionary containing atom position data. - name_hm + name_hm : str Hermann-Mauguin symbol of the space group. - coord_code + coord_code : int Coordinate system code. - wyckoff_letter + wyckoff_letter : str Wyckoff position letter. Returns ------- + Dict[str, Any] The atom_site dictionary with applied symmetry constraints. """ it_number = get_it_number_by_name_hm_short(name_hm) diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 4c8207b1..4b8709ef 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -26,18 +26,7 @@ @ExperimentTypeFactory.register class ExperimentType(CategoryItem): - """ - Container of categorical attributes defining experiment flavor. - - Parameters - ---------- - sample_form - Powder or Single crystal. beam_mode: - Constant wavelength : CW) or time-of-flight (TOF - - Neutrons or X-rays. scattering_type - Bragg or Total. - """ + """Container of categorical attributes defining experiment flavor.""" type_info = TypeInfo( tag='default', diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index 4cf8375b..2f707da1 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -40,15 +40,15 @@ def create( Parameters ---------- - name + name : str Experiment identifier. - sample_form + sample_form : str | None, default=None Sample form (e.g. ``'powder'``). - beam_mode + beam_mode : str | None, default=None Beam mode (e.g. ``'constant wavelength'``). - radiation_probe + radiation_probe : str | None, default=None Radiation probe (e.g. ``'neutron'``). - scattering_type + scattering_type : str | None, default=None Scattering type (e.g. ``'bragg'``). """ experiment = ExperimentFactory.from_scratch( @@ -71,7 +71,7 @@ def add_from_cif_str( Parameters ---------- - cif_str + cif_str : str Full CIF document as a string. """ experiment = ExperimentFactory.from_cif_str(cif_str) @@ -110,17 +110,17 @@ def add_from_data_path( Parameters ---------- - name + name : str Experiment identifier. - data_path + data_path : str Path to the measured data file. - sample_form + sample_form : str | None, default=None Sample form (e.g. ``'powder'``). - beam_mode + beam_mode : str | None, default=None Beam mode (e.g. ``'constant wavelength'``). - radiation_probe + radiation_probe : str | None, default=None Radiation probe (e.g. ``'neutron'``). - scattering_type + scattering_type : str | None, default=None Scattering type (e.g. ``'bragg'``). """ experiment = ExperimentFactory.from_data_path( diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 9488eb21..3679c45a 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -63,7 +63,7 @@ def name(self, new: str) -> None: Parameters ---------- - new + new : str New name for this experiment. """ self._name = new @@ -94,8 +94,13 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: Parameters ---------- - data_path + data_path : str Path to the ASCII file to load. + + Raises + ------ + NotImplementedError + Subclasses must implement this method. """ raise NotImplementedError() @@ -129,7 +134,7 @@ def calculator_type(self, tag: str) -> None: Parameters ---------- - tag + tag : str Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``, ``'pdffit'``). """ from easydiffraction.analysis.calculators.factory import CalculatorFactory @@ -244,7 +249,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: Parameters ---------- - data_path + data_path : str Path to data file with columns compatible with the beam mode. """ pass @@ -270,7 +275,7 @@ def extinction_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Extinction tag (e.g. ``'shelx'``). """ supported_tags = ExtinctionFactory.supported_tags() @@ -317,7 +322,7 @@ def linked_crystal_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Linked-crystal tag (e.g. ``'default'``). """ supported_tags = LinkedCrystalFactory.supported_tags() @@ -364,7 +369,7 @@ def instrument_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Instrument tag (e.g. ``'cwl-sc'``). """ supported = InstrumentFactory.supported_for( @@ -419,7 +424,7 @@ def data_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Data tag (e.g. ``'bragg-sc'``). """ supported_tags = DataFactory.supported_tags() @@ -481,11 +486,12 @@ def _get_valid_linked_phases( Parameters ---------- - structures + structures : Structures Collection of structures. Returns ------- + List[Any] A list of valid linked phases. """ if not self.linked_phases: @@ -516,7 +522,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: Parameters ---------- - data_path + data_path : str Path to data file with columns compatible with the beam mode (e.g. 2θ/I/σ for CWL, TOF/I/σ for TOF). """ @@ -539,7 +545,7 @@ def linked_phases_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Linked-phases tag (e.g. ``'default'``). """ supported_tags = LinkedPhasesFactory.supported_tags() @@ -582,7 +588,7 @@ def excluded_regions_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Excluded-regions tag (e.g. ``'default'``). """ supported_tags = ExcludedRegionsFactory.supported_tags() @@ -631,7 +637,7 @@ def data_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Data tag (e.g. ``'bragg-pd-cwl'``). """ supported_tags = DataFactory.supported_tags() @@ -673,7 +679,7 @@ def peak_profile_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str New profile type as tag string. """ supported = PeakFactory.supported_for( diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index 230f69d2..e414670e 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -121,7 +121,7 @@ def instrument_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Instrument tag (e.g. ``'cwl-pd'``). """ supported = InstrumentFactory.supported_for( diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 2c3a38c2..aaf16c89 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -145,19 +145,20 @@ def from_scratch( Parameters ---------- - name + name : str Experiment identifier. - sample_form + sample_form : str | None, default=None Sample form (e.g. ``'powder'``). - beam_mode + beam_mode : str | None, default=None Beam mode (e.g. ``'constant wavelength'``). - radiation_probe + radiation_probe : str | None, default=None Radiation probe (e.g. ``'neutron'``). - scattering_type + scattering_type : str | None, default=None Scattering type (e.g. ``'bragg'``). Returns ------- + ExperimentBase An experiment instance with only metadata. """ expt_type = cls._create_experiment_type( @@ -182,11 +183,12 @@ def from_cif_str( Parameters ---------- - cif_str + cif_str : str Full CIF document as a string. Returns ------- + ExperimentBase A populated experiment instance. """ doc = document_from_string(cif_str) @@ -205,11 +207,12 @@ def from_cif_path( Parameters ---------- - cif_path + cif_path : str Path to a CIF file. Returns ------- + ExperimentBase A populated experiment instance. """ doc = document_from_path(cif_path) @@ -233,21 +236,22 @@ def from_data_path( Parameters ---------- - name + name : str Experiment identifier. - data_path + data_path : str Path to the measured data file. - sample_form + sample_form : str | None, default=None Sample form (e.g. ``'powder'``). - beam_mode + beam_mode : str | None, default=None Beam mode (e.g. ``'constant wavelength'``). - radiation_probe + radiation_probe : str | None, default=None Radiation probe (e.g. ``'neutron'``). - scattering_type + scattering_type : str | None, default=None Scattering type (e.g. ``'bragg'``). Returns ------- + ExperimentBase An experiment instance with measured data attached. """ expt_obj = cls.from_scratch( diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index dfb616e6..b605b3cd 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -366,7 +366,7 @@ def _update( Parameters ---------- - called_by_minimizer : bool + called_by_minimizer : bool, default=False Whether the update was triggered by the fitting minimizer. Currently unused. """ diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index 3283886e..e78e8086 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -97,7 +97,7 @@ def cell_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Cell tag (e.g. ``'default'``). """ supported_tags = CellFactory.supported_tags() @@ -156,7 +156,7 @@ def space_group_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Space-group tag (e.g. ``'default'``). """ supported_tags = SpaceGroupFactory.supported_tags() @@ -215,7 +215,7 @@ def atom_sites_type(self, new_type: str) -> None: Parameters ---------- - new_type + new_type : str Atom-sites tag (e.g. ``'default'``). """ supported_tags = AtomSitesFactory.supported_tags() diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index 6f32e84f..d33a553a 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -95,12 +95,13 @@ def create(cls, engine_name: str) -> object: Parameters ---------- - engine_name + engine_name : str Identifier of the engine to instantiate as listed in ``_registry()``. Returns ------- + object A new backend instance corresponding to ``engine_name``. Raises diff --git a/src/easydiffraction/display/plotters/ascii.py b/src/easydiffraction/display/plotters/ascii.py index 02559f18..f3053535 100644 --- a/src/easydiffraction/display/plotters/ascii.py +++ b/src/easydiffraction/display/plotters/ascii.py @@ -34,11 +34,12 @@ def _get_legend_item(self, label: str) -> str: Parameters ---------- - label + label : str Series identifier (e.g., ``'meas'``). Returns ------- + str A formatted legend string with color escapes. """ color_start = DEFAULT_COLORS[label] @@ -66,17 +67,17 @@ def plot_powder( Parameters ---------- - x + x : object 1D array-like of x values (only used for range display). - y_series + y_series : object Sequence of y arrays to plot. - labels + labels : object Series identifiers corresponding to y_series. - axes_labels + axes_labels : object Ignored; kept for API compatibility. - title + title : str Figure title printed above the chart. - height + height : int | None, default=None Number of text rows to allocate for the chart. """ # Intentionally unused; kept for a consistent display API @@ -118,18 +119,18 @@ def plot_single_crystal( Parameters ---------- - x_calc + x_calc : object 1D array-like of calculated values (x-axis). - y_meas + y_meas : object 1D array-like of measured values (y-axis). - y_meas_su + y_meas_su : object 1D array-like of measurement uncertainties (ignored in ASCII mode). - axes_labels + axes_labels : object Pair of strings for the x and y titles. - title + title : str Figure title. - height + height : int | None, default=None Number of text rows for the chart (default: 15). """ # Intentionally unused; ASCII scatter doesn't show error bars diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index cffa3b81..d6668985 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -180,17 +180,17 @@ def plot_powder( Parameters ---------- - x + x : object 1D array of x-axis values. - y_series + y_series : object Sequence of y arrays to plot. - labels + labels : object Identifiers corresponding to y_series. - axes_labels + axes_labels : object Pair of strings for the x and y titles. - title + title : str Figure title. - height + height : int | None Backend-specific height (text rows or pixels). """ pass @@ -213,17 +213,17 @@ def plot_single_crystal( Parameters ---------- - x_calc + x_calc : object 1D array of calculated values (x-axis). - y_meas + y_meas : object 1D array of measured values (y-axis). - y_meas_su + y_meas_su : object 1D array of measurement uncertainties. - axes_labels + axes_labels : object Pair of strings for the x and y titles. - title + title : str Figure title. - height + height : int | None Backend-specific height (text rows or pixels). """ pass diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index e81e68d6..07ff1ec2 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -47,17 +47,17 @@ def _get_powder_trace( Parameters ---------- - x + x : object 1D array-like of x-axis values. - y + y : object 1D array- like of y-axis values. - label + label : str Series identifier (``'meas'``, ``'calc'``, or ``'resid'``). Returns ------- - A configured - class:`plotly.graph_objects.Scatter` trace. + object + A configured :class:`plotly.graph_objects.Scatter` trace. """ mode = SERIES_CONFIG[label]['mode'] name = SERIES_CONFIG[label]['name'] @@ -85,18 +85,18 @@ def _get_single_crystal_trace( Parameters ---------- - x_calc + x_calc : object 1D array-like of calculated values (x-axis). - y_meas + y_meas : object 1D array-like of measured values (y-axis). - y_meas_su + y_meas_su : object 1D array-like of measurement uncertainties. Returns ------- - A configured - class:`plotly.graph_objects.Scatter` trace with markers and - error bars. + object + A configured :class:`plotly.graph_objects.Scatter` trace with + markers and error bars. """ trace = go.Scatter( x=x_calc, @@ -127,6 +127,7 @@ def _get_diagonal_shape(self) -> dict: Returns ------- + dict A dict configuring a diagonal line shape. """ return dict( @@ -147,6 +148,7 @@ def _get_config(self) -> dict: Returns ------- + dict A dict with display and mode bar settings. """ return dict( @@ -170,15 +172,15 @@ def _get_figure( Parameters ---------- - data + data : object List of traces to include in the figure. - layout + layout : object Layout configuration dict. Returns ------- - A configured - class:`plotly.graph_objects.Figure`. + object + A configured :class:`plotly.graph_objects.Figure`. """ fig = go.Figure(data=data, layout=layout) # Format axis ticks: @@ -199,7 +201,7 @@ def _show_figure( Parameters ---------- - fig + fig : object A :class:`plotly.graph_objects.Figure` to display. """ config = self._get_config() @@ -226,17 +228,17 @@ def _get_layout( Parameters ---------- - title + title : str Figure title. - axes_labels + axes_labels : object Pair of strings for the x and y titles. - **kwargs + **kwargs : object Additional layout parameters (e.g., shapes). Returns ------- - A configured - class:`plotly.graph_objects.Layout`. + object + A configured :class:`plotly.graph_objects.Layout`. """ return go.Layout( margin=dict( @@ -286,17 +288,17 @@ def plot_powder( Parameters ---------- - x + x : object 1D array-like of x-axis values. - y_series + y_series : object Sequence of y arrays to plot. - labels + labels : object Series identifiers corresponding to y_series. - axes_labels + axes_labels : object Pair of strings for the x and y titles. - title + title : str Figure title. - height + height : int | None, default=None Ignored; Plotly auto-sizes based on renderer. """ # Intentionally unused; accepted for API compatibility @@ -334,17 +336,17 @@ def plot_single_crystal( Parameters ---------- - x_calc + x_calc : object 1D array-like of calculated values (x-axis). - y_meas + y_meas : object 1D array-like of measured values (y-axis). - y_meas_su + y_meas_su : object 1D array-like of measurement uncertainties. - axes_labels + axes_labels : object Pair of strings for the x and y titles. - title + title : str Figure title. - height + height : int | None, default=None Ignored; Plotly auto-sizes based on renderer. """ # Intentionally unused; accepted for API compatibility diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 86b3a852..432bc88e 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -92,17 +92,18 @@ def _auto_x_range_for_ascii( Parameters ---------- - pattern + pattern : object Data pattern object (needs ``intensity_meas``). - x_array + x_array : object Full x-axis array. - x_min + x_min : object Current minimum (may be ``None``). - x_max + x_max : object Current maximum (may be ``None``). Returns ------- + tuple Tuple of ``(x_min, x_max)``, possibly narrowed. """ if self._engine == 'asciichartpy' and (x_min is None or x_max is None): @@ -126,17 +127,18 @@ def _filtered_y_array( Parameters ---------- - y_array + y_array : object 1D array-like of y values. - x_array + x_array : object 1D array-like of x values (same length as ``y_array``). - x_min + x_min : object Minimum x limit (or ``None`` to use default). - x_max + x_max : object Maximum x limit (or ``None`` to use default). Returns ------- + object Filtered ``y_array`` values where ``x_array`` lies within ``[x_min, x_max]``. """ @@ -178,27 +180,28 @@ def _prepare_powder_data( Parameters ---------- - pattern + pattern : object Data pattern object with intensity arrays. - expt_name + expt_name : str Experiment name for error messages. - expt_type + expt_type : object Experiment type with sample_form, scattering, and beam enums. - x_min + x_min : object Optional minimum x-axis limit. - x_max + x_max : object Optional maximum x-axis limit. - x + x : object Explicit x-axis type or ``None``. - need_meas + need_meas : bool, default=False Whether ``intensity_meas`` is required. - need_calc + need_calc : bool, default=False Whether ``intensity_calc`` is required. - show_residual + show_residual : bool, default=False If ``True``, compute meas − calc residual. Returns ------- + dict | None A dict with keys ``x_filtered``, ``y_series``, ``y_labels``, ``axes_labels``, and ``x_axis``; or ``None`` when a required array is missing. @@ -262,14 +265,15 @@ def _resolve_x_axis(self, expt_type: object, x: object) -> tuple: Parameters ---------- - expt_type + expt_type : object Experiment type with sample_form, scattering_type, and beam_mode enums. - x + x : object Explicit x-axis type or ``None`` to auto-detect. Returns ------- + tuple Tuple of ``(x_axis, x_name, sample_form, scattering_type, beam_mode)``. """ @@ -296,7 +300,7 @@ def x_min(self, value: object) -> None: Parameters ---------- - value + value : object Minimum limit or ``None`` to reset to default. """ if value is not None: @@ -316,7 +320,7 @@ def x_max(self, value: object) -> None: Parameters ---------- - value + value : object Maximum limit or ``None`` to reset to default. """ if value is not None: @@ -336,7 +340,7 @@ def height(self, value: object) -> None: Parameters ---------- - value + value : object Height value or ``None`` to reset to default. """ if value is not None: @@ -377,18 +381,18 @@ def plot_meas( Parameters ---------- - pattern + pattern : object Object with x-axis arrays (``two_theta``, ``time_of_flight``, ``d_spacing``) and ``meas`` array. - expt_name + expt_name : str Experiment name for the title. - expt_type + expt_type : object Experiment type with scattering/beam enums. - x_min + x_min : object, default=None Optional minimum x-axis limit. - x_max + x_max : object, default=None Optional maximum x-axis limit. - x + x : object, default=None X-axis type (``'two_theta'``, ``'time_of_flight'``, or ``'d_spacing'``). If ``None``, auto-detected from beam mode. """ @@ -427,18 +431,18 @@ def plot_calc( Parameters ---------- - pattern + pattern : object Object with x-axis arrays (``two_theta``, ``time_of_flight``, ``d_spacing``) and ``calc`` array. - expt_name + expt_name : str Experiment name for the title. - expt_type + expt_type : object Experiment type with scattering/beam enums. - x_min + x_min : object, default=None Optional minimum x-axis limit. - x_max + x_max : object, default=None Optional maximum x-axis limit. - x + x : object, default=None X-axis type (``'two_theta'``, ``'time_of_flight'``, or ``'d_spacing'``). If ``None``, auto-detected from beam mode. """ @@ -486,19 +490,19 @@ def plot_meas_vs_calc( Parameters ---------- - pattern + pattern : object Data pattern object with meas/calc arrays. - expt_name + expt_name : str Experiment name for the title. - expt_type + expt_type : object Experiment type with sample_form, scattering, and beam enums. - x_min + x_min : object, default=None Optional minimum x-axis limit. - x_max + x_max : object, default=None Optional maximum x-axis limit. - show_residual + show_residual : bool, default=False If ``True``, add residual series (powder only). - x + x : object, default=None X-axis type. If ``None``, auto-detected from sample form and beam mode. """ diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index fcc5fa5b..74c66207 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -38,11 +38,12 @@ def _format_value(self, value: object) -> object: Parameters ---------- - value + value : object Cell value to format. Returns ------- + object A string representation with fixed precision for floats or ``str(value)`` for other types. """ @@ -70,12 +71,12 @@ def _rich_to_hex(self, color: str) -> str: Parameters ---------- - color - Rich color name or specification parsable by - mod:`rich`. + color : str + Rich color name or specification parsable by :mod:`rich`. Returns ------- + str Hex color string in the form ``#RRGGBB``. """ c = Color.parse(color) @@ -105,16 +106,17 @@ def render( Parameters ---------- - alignments + alignments : object Iterable of column justifications (e.g., ``'left'`` or ``'center'``) corresponding to the data columns. - df + df : object Index-aware DataFrame with data to render. - display_handle + display_handle : object | None, default=None Optional environment-specific handle to enable in-place updates. Returns ------- + object Backend-defined return value (commonly ``None``). """ pass diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index ba20bafa..7cbefd1d 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -25,12 +25,13 @@ def _build_base_styles(self, color: str) -> list[dict]: Parameters ---------- - color + color : str CSS color value (e.g., ``#RRGGBB``) to use for borders and header accents. Returns ------- + list[dict] A list of ``Styler.set_table_styles`` dictionaries. """ return [ @@ -84,14 +85,15 @@ def _build_header_alignment_styles(self, df: object, alignments: object) -> list Parameters ---------- - df + df : object DataFrame whose columns are being rendered. - alignments + alignments : object Iterable of text alignment values (e.g., ``'left'``, ``'center'``) matching ``df`` columns. Returns ------- + list[dict] A list of CSS rules for header cell alignment. """ return [ @@ -108,15 +110,16 @@ def _apply_styling(self, df: object, alignments: object, color: str) -> object: Parameters ---------- - df + df : object DataFrame to style. - alignments + alignments : object Iterable of text alignment values for columns. - color + color : str CSS color value used for borders/header. Returns ------- + object A configured pandas Styler ready for display. """ table_styles = self._build_base_styles(color) @@ -143,9 +146,9 @@ def _update_display(self, styler: object, display_handle: object) -> None: Parameters ---------- - styler + styler : object Configured DataFrame Styler to be rendered. - display_handle + display_handle : object Optional IPython DisplayHandle used for in-place updates. """ # Handle with update() method @@ -177,13 +180,18 @@ def render( Parameters ---------- - alignments + alignments : object Iterable of column justifications (e.g. 'left'). - df + df : object DataFrame whose index is displayed as the first column. - display_handle + display_handle : object | None, default=None Optional IPython DisplayHandle to update an existing output area in place when running in Jupyter. + + Returns + ------- + object + Backend-defined return value (commonly ``None``). """ color = self._pandas_border_color styler = self._apply_styling(df, alignments, color) diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index 41c34879..6b1dca71 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -48,11 +48,12 @@ def _to_html(self, table: Table) -> str: Parameters ---------- - table + table : Table Rich :class:`~rich.table.Table` to export. Returns ------- + str HTML string with inline styles for notebook display. """ tmp = Console(force_jupyter=False, record=True, file=io.StringIO()) @@ -71,17 +72,17 @@ def _build_table(self, df: object, alignments: object, color: str) -> Table: Parameters ---------- - df + df : object DataFrame-like object providing rows to render. - alignments + alignments : object Iterable of text alignment values for columns. - color + color : str Rich color name used for borders/index style. Returns ------- - A - class:`~rich.table.Table` configured for display. + Table + A :class:`~rich.table.Table` configured for display. """ table = Table( title=None, @@ -116,9 +117,9 @@ def _update_display(self, table: Table, display_handle: object) -> None: Parameters ---------- - table + table : Table Rich :class:`~rich.table.Table` to display. - display_handle + display_handle : object Optional environment-specific handle for in- place updates (IPython or terminal live). """ @@ -156,12 +157,17 @@ def render( Parameters ---------- - alignments + alignments : object Iterable of text-align values for columns. - df + df : object Index-aware DataFrame to render. - display_handle + display_handle : object, default=None Optional environment handle for in-place updates. + + Returns + ------- + object + Backend-defined return value (commonly ``None``). """ color = self._rich_border_color table = self._build_table(df, alignments, color) diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index 79c4ec70..b42bf109 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -70,16 +70,17 @@ def render(self, df: object, display_handle: object | None = None) -> object: Parameters ---------- - df + df : object DataFrame with a two-level column index where the second level provides per-column alignment. - display_handle + display_handle : object | None, default=None Optional environment-specific handle used to update an existing output area in-place (e.g., an IPython DisplayHandle or a terminal live handle). Returns ------- + object Backend-specific return value (usually ``None``). """ # Work on a copy to avoid mutating the original DataFrame diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index 71d852d0..971835d3 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -21,7 +21,7 @@ def __init__(self, project: object) -> None: Parameters ---------- - project + project : object The Project instance this summary belongs to. """ self.project = project diff --git a/src/easydiffraction/utils/_vendored/theme_detect.py b/src/easydiffraction/utils/_vendored/theme_detect.py index 9f9bf264..956c09b3 100644 --- a/src/easydiffraction/utils/_vendored/theme_detect.py +++ b/src/easydiffraction/utils/_vendored/theme_detect.py @@ -49,7 +49,10 @@ def is_dark() -> bool: (for browser-based Jupyter) 4. System preferences (fallback - may differ from Jupyter theme) - Returns: True if dark mode is detected, False otherwise. + Returns + ------- + bool + True if dark mode is detected, False otherwise. """ # Try Jupyter-specific methods first result = _check_jupyterlab_settings() @@ -78,8 +81,11 @@ def is_dark() -> bool: def get_detection_result() -> dict[str, Optional[bool]]: """Get results from all detection methods for debugging. - Returns: Dictionary with detection method names as keys and - their results (True/False/None) as values. + Returns + ------- + dict[str, Optional[bool]] + Dictionary with detection method names as keys and + their results (True/False/None) as values. """ return { 'jupyterlab_settings': _check_jupyterlab_settings(), diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index dabf35de..817fa7bf 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -99,6 +99,7 @@ def _detect_width() -> int: Returns ------- + int The detected terminal width, clamped at ``_MIN_CONSOLE_WIDTH`` to avoid cramped layouts. """ @@ -141,13 +142,13 @@ def setup_handlers( Parameters ---------- - logger + logger : logging.Logger Logger instance to attach handlers to. - level + level : int Minimum log level to emit. - rich_tracebacks + rich_tracebacks : bool Whether to enable Rich tracebacks. - mode + mode : str, default='compact' Output mode name ("compact" or "verbose"). """ logger.handlers.clear() @@ -188,13 +189,13 @@ def configure( Parameters ---------- - logger + logger : logging.Logger Logger instance to configure. - mode + mode : Logger.Mode Output mode (compact or verbose). - level + level : Logger.Level Minimum log level to emit. - rich_tracebacks + rich_tracebacks : bool Whether to enable Rich tracebacks. """ LoggerConfig.setup_handlers( @@ -223,7 +224,7 @@ def install_verbose_hook(logger: logging.Logger) -> None: Parameters ---------- - logger + logger : logging.Logger Logger used to emit the exception information. """ if not hasattr(Logger, '_orig_excepthook'): @@ -255,7 +256,7 @@ def install_compact_hook(logger: logging.Logger) -> None: Parameters ---------- - logger + logger : logging.Logger Logger used to emit the error message. """ if not hasattr(Logger, '_orig_excepthook'): @@ -285,11 +286,12 @@ def _suppress_traceback(logger: object) -> object: Parameters ---------- - logger + logger : object Logger used to emit error messages. Returns ------- + object A callable suitable for IPython's set_custom_exc that suppresses full tracebacks and logs only the exception message. """ @@ -314,7 +316,7 @@ def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: Parameters ---------- - logger + logger : logging.Logger Logger used to emit error messages. """ try: diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 41070cb2..b83cda6e 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -35,7 +35,7 @@ def _validate_url(url: str) -> None: Parameters ---------- - url + url : str The URL to validate. Raises @@ -140,11 +140,11 @@ def download_data( Parameters ---------- - id + id : int | str Numeric dataset id (e.g. 12). - destination + destination : str, default='data' Directory to save the file into (created if missing). - overwrite + overwrite : bool, default=False Whether to overwrite the file if it already exists. Returns @@ -155,9 +155,7 @@ def download_data( Raises ------ KeyError - - If the id is not found in the index. ValueError - If the resolved URL is not HTTP/HTTPS. + If the id is not found in the index. """ index = _fetch_data_index() key = str(id) @@ -299,7 +297,7 @@ def _get_version_for_url(package_name: str = 'easydiffraction') -> str: Parameters ---------- - package_name : str + package_name : str, default='easydiffraction' The name of the package to query. Returns @@ -393,11 +391,11 @@ def download_tutorial( Parameters ---------- - id + id : int | str Numeric tutorial id (e.g. 1). - destination + destination : str, default='tutorials' Directory to save the file into (created if missing). - overwrite + overwrite : bool, default=False Whether to overwrite the file if it already exists. Returns @@ -408,9 +406,7 @@ def download_tutorial( Raises ------ KeyError - - If the id is not found in the index. ValueError - If the resolved URL is not HTTP/HTTPS. + If the id is not found in the index. """ index = _fetch_tutorials_index() key = str(id) @@ -468,9 +464,9 @@ def download_all_tutorials( Parameters ---------- - destination + destination : str, default='tutorials' Directory to save the files into (created if missing). - overwrite + overwrite : bool, default=False Whether to overwrite files if they already exist. Returns @@ -532,7 +528,7 @@ def render_cif(cif_text: str) -> None: Parameters ---------- - cif_text + cif_text : str The CIF text to display. """ # Split into lines @@ -576,8 +572,8 @@ def tof_to_d( Linear calibration coefficient (µs/Å). quad : float Quadratic calibration coefficient (µs/Ų). - quad_eps : float, optional - Threshold to treat ``quad`` as zero. Defaults to 1e-20. + quad_eps : float, default=1e-20 + Threshold to treat ``quad`` as zero. Returns ------- @@ -646,13 +642,17 @@ def twotheta_to_d(twotheta: object, wavelength: float) -> object: """ Convert 2-theta to d-spacing using Bragg's law. - Parameters: twotheta (float or np.ndarray): 2-theta angle in degrees. - wavelength (float): Wavelength in Å. + Parameters + ---------- + twotheta : object + 2-theta angle in degrees (float or np.ndarray). + wavelength : float + Wavelength in Å. Returns ------- - d (float or np.ndarray) - d-spacing in Å. + object + d-spacing in Å (float or np.ndarray). """ # Convert twotheta from degrees to radians theta_rad = np.radians(twotheta / 2) @@ -667,13 +667,15 @@ def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda: object) -> object: """ Convert sin(theta)/lambda to d-spacing. - Parameters: sin_theta_over_lambda (float or np.ndarray): - sin(theta)/lambda in 1/Å. + Parameters + ---------- + sin_theta_over_lambda : object + sin(theta)/lambda in 1/Å (float or np.ndarray). Returns ------- - d (float or np.ndarray) - d-spacing in Å. + object + d-spacing in Å (float or np.ndarray). """ # Avoid division by zero with np.errstate(divide='ignore', invalid='ignore'): @@ -688,8 +690,12 @@ def get_value_from_xye_header(file_path: str, key: str) -> float: Extracts a floating point value from the first line of the file, corresponding to the given key. - Parameters: file_path (str): Path to the input file. key (str): The key - to extract ('DIFC' or 'two_theta'). + Parameters + ---------- + file_path : str + Path to the input file. + key : str + The key to extract ('DIFC' or 'two_theta'). Returns ------- @@ -728,15 +734,19 @@ def str_to_ufloat(s: Optional[str], default: Optional[float] = None) -> UFloat: provided". - If parsing fails, the function falls back to the given `default` value with uncertainty NaN. - Parameters ---------- s : str or None Numeric string in CIF format (e.g. - "3.566", "3.566(2)") or None. default : float or None, optional Default - value to use if `s` is None or parsing fails. Defaults to None. + Parameters + ---------- + s : Optional[str] + Numeric string in CIF format (e.g. "3.566", "3.566(2)") or None. + default : Optional[float], default=None + Default value to use if `s` is None or parsing fails. Returns ------- - ------- UFloat An `uncertainties.UFloat` object with the parsed - value and uncertainty. The uncertainty will be NaN if not - specified or parsing failed. + UFloat + An `uncertainties.UFloat` object with the parsed value and + uncertainty. The uncertainty will be NaN if not specified or + parsing failed. """ if s is None: return ufloat(default, np.nan) diff --git a/tools/check_param_consistency.py b/tools/check_param_consistency.py deleted file mode 100644 index 22244618..00000000 --- a/tools/check_param_consistency.py +++ /dev/null @@ -1,365 +0,0 @@ -"""Check consistency between Parameter/Descriptor definitions and their -public properties. - -Three checks are performed for every public property whose getter -returns a GenericDescriptorBase subclass instance: - -1. **Docstring vs description** – The first sentence of the property - getter docstring must match the ``description`` string of the - backing Parameter/Descriptor (case-insensitive, ignoring trailing - punctuation and markup). - -2. **Getter return-type annotation vs backing attribute type** – The - annotation on the getter (e.g. ``-> Parameter``) must resolve to a - type (or union containing a type) that the runtime object is an - instance of. Union, Optional, and Annotated wrappers are - decomposed structurally. - -3. **Setter value-type annotation vs descriptor data type** – For - numeric descriptors the setter ``value`` argument must be annotated - with a type drawn from ``{int, float}`` (per PEP 484 numeric - tower, ``float`` alone is the canonical form). For string - descriptors it must be ``str``. Annotations are decomposed - structurally, so ``float``, ``int | float``, ``float | int`` are - all accepted for numeric descriptors. - -The script instantiates every concrete ``CategoryItem`` and -``CategoryCollection`` subclass found under ``src/easydiffraction/`` -and introspects properties at runtime. - -Exit code 0 when all checks pass, 1 otherwise. Import and -instantiation failures are reported explicitly rather than silently -skipped. -""" - -from __future__ import annotations - -import contextlib -import importlib -import inspect -import pkgutil -import re -import sys -import types -from pathlib import Path -from typing import Union -from typing import get_args -from typing import get_origin -from typing import get_type_hints - -# --------------------------------------------------------------------------- -# Bootstrap: make sure the package is importable -# --------------------------------------------------------------------------- - -_repo = Path(__file__).resolve().parents[1] -_src = _repo / 'src' -if str(_src) not in sys.path: - sys.path.insert(0, str(_src)) - -from easydiffraction.core.category import CategoryCollection # noqa: E402 -from easydiffraction.core.category import CategoryItem # noqa: E402 -from easydiffraction.core.variable import GenericDescriptorBase # noqa: E402 -from easydiffraction.core.variable import GenericNumericDescriptor # noqa: E402 -from easydiffraction.core.variable import GenericStringDescriptor # noqa: E402 - -# --------------------------------------------------------------------------- -# Known intermediate bases that require constructor arguments and have -# no descriptor-backed properties of their own. Their concrete leaves -# (PdCwlData, PdTofData, TotalData, etc.) *are* checked. -# If a class is skipped and NOT listed here, the check fails. -# --------------------------------------------------------------------------- - -_KNOWN_INTERMEDIATE_BASES: frozenset[str] = frozenset({ - 'PdDataBase', - 'TotalDataBase', -}) - - -# --------------------------------------------------------------------------- -# Discovery helpers -# --------------------------------------------------------------------------- - - -def _import_all_submodules(package_name: str) -> list[str]: - """Recursively import every submodule of *package_name*. - - Returns: - List of module names that failed to import. - """ - package = importlib.import_module(package_name) - prefix = package.__name__ + '.' - failed: list[str] = [] - for _importer, modname, _ispkg in pkgutil.walk_packages(package.__path__, prefix=prefix): - try: - importlib.import_module(modname) - except Exception as exc: # noqa: BLE001 - failed.append(f'{modname}: {exc}') - return failed - - -def _concrete_subclasses(base: type) -> set[type]: - """Return all non-abstract subclasses of *base* (deep).""" - result: set[type] = set() - for sub in base.__subclasses__(): - if not inspect.isabstract(sub): - result.add(sub) - result.update(_concrete_subclasses(sub)) - return result - - -# --------------------------------------------------------------------------- -# Normalisation helpers -# --------------------------------------------------------------------------- - -_TRAILING_PUNCT = re.compile(r'[.\s]+$') -_MARKDOWN_EMPHASIS = re.compile(r'\*{1,2}([^*]+)\*{1,2}') -_RST_ROLE = re.compile(r':[a-z]+:`([^`]+)`') -_DOUBLE_BACKTICK = re.compile(r'``([^`]+)``') -_UNICODE_DASHES = re.compile(r'[\u2013\u2014]') # en-dash, em-dash - - -def _normalise(text: str) -> str: - """Lower-case, strip markup formatting, normalise dashes and - trailing punctuation.""" - t = _MARKDOWN_EMPHASIS.sub(r'\1', text) - t = _RST_ROLE.sub(r'\1', t) - t = _DOUBLE_BACKTICK.sub(r'\1', t) - t = _UNICODE_DASHES.sub('-', t) - return _TRAILING_PUNCT.sub('', t.strip()).lower() - - -def _first_sentence(docstring: str | None) -> str: - """Extract the first paragraph / sentence from a docstring.""" - if not docstring: - return '' - first_para = docstring.strip().split('\n\n')[0] - return ' '.join(line.strip() for line in first_para.splitlines()) - - -# --------------------------------------------------------------------------- -# Structural type extraction -# --------------------------------------------------------------------------- - -# Allowed concrete types for setter annotations -_NUMERIC_ALLOWED: frozenset[type] = frozenset({int, float}) -_STRING_ALLOWED: frozenset[type] = frozenset({str}) - - -def _extract_types(annotation: object) -> tuple[type, ...]: - """Extract concrete type objects from an annotation. - - Handles plain types, ``X | Y`` (types.UnionType), - ``typing.Union[X, Y]``, and ``typing.Optional[X]``. - ``NoneType`` members are filtered out. - - Returns: - Tuple of concrete types, or empty tuple when the annotation - cannot be decomposed (e.g. ``Any``, unresolved forward ref). - """ - origin = get_origin(annotation) - if origin is types.UnionType or origin is Union: - return tuple( - a for a in get_args(annotation) if isinstance(a, type) and a is not type(None) - ) - if isinstance(annotation, type): - return (annotation,) - return () - - -# --------------------------------------------------------------------------- -# Main checking logic -# --------------------------------------------------------------------------- - - -def _check_class(cls: type, instance: object, errors: list[str]) -> None: - """Run all three checks for *cls* using the given *instance*.""" - - # Collect all property objects from the MRO - props: dict[str, property] = {} - for base in cls.__mro__: - for key, val in base.__dict__.items(): - if isinstance(val, property) and not key.startswith('_'): - props.setdefault(key, val) - - for prop_name, prop_obj in props.items(): - # Retrieve the runtime value - try: - val = getattr(instance, prop_name) - except Exception: # noqa: BLE001, S112 - continue - - if not isinstance(val, GenericDescriptorBase): - continue - - getter = prop_obj.fget - setter = prop_obj.fset - loc = f'{cls.__name__}.{prop_name}' - - # --------------------------------------------------------------- - # Check 1: docstring vs description - # --------------------------------------------------------------- - description = getattr(val, '_description', None) or '' - doc = _first_sentence(getter.__doc__) if getter and getter.__doc__ else '' - - if description and not doc: - errors.append( - f'{loc}: getter has no docstring, but Parameter description is "{description}"' - ) - elif description and doc and _normalise(doc) != _normalise(description): - errors.append( - f'{loc}: docstring first sentence does not match ' - f'Parameter description.\n' - f' docstring: "{doc}"\n' - f' description: "{description}"' - ) - - # --------------------------------------------------------------- - # Check 2: getter return-type annotation - # --------------------------------------------------------------- - if getter is not None: - hints: dict = {} - with contextlib.suppress(Exception): - hints = get_type_hints(getter) - ret = hints.get('return') - - if ret is None: - errors.append( - f'{loc}: getter has no return-type annotation (expected {type(val).__name__})' - ) - else: - ret_types = _extract_types(ret) - if not ret_types: - errors.append( - f'{loc}: getter return annotation "{ret}" ' - f'could not be structurally verified ' - f'(expected {type(val).__name__})' - ) - elif not any(isinstance(val, t) for t in ret_types): - names = ' | '.join(t.__name__ for t in ret_types) - errors.append( - f'{loc}: getter return annotation ' - f'{names} does not match runtime type ' - f'{type(val).__name__}' - ) - - # --------------------------------------------------------------- - # Check 3: setter value-type annotation - # --------------------------------------------------------------- - if setter is not None: - setter_hints: dict = {} - with contextlib.suppress(Exception): - setter_hints = get_type_hints(setter) - - sig = inspect.signature(setter) - params = [p for name, p in sig.parameters.items() if name != 'self'] - if not params: - continue - - value_param = params[0] - # Prefer resolved hint over raw signature annotation - resolved_ann = setter_hints.get(value_param.name) - raw_ann = value_param.annotation - - if resolved_ann is None and raw_ann is inspect.Parameter.empty: - is_numeric = isinstance(val, GenericNumericDescriptor) - is_string = isinstance(val, GenericStringDescriptor) - expected = 'float' if is_numeric else 'str' if is_string else '?' - errors.append( - f'{loc}: setter parameter ' - f'"{value_param.name}" has no type ' - f'annotation (expected {expected})' - ) - else: - ann = resolved_ann if resolved_ann is not None else raw_ann - ann_types = _extract_types(ann) - - if not ann_types: - errors.append( - f'{loc}: setter annotation "{ann}" could not be structurally verified' - ) - elif isinstance(val, GenericNumericDescriptor) and not set(ann_types).issubset( - _NUMERIC_ALLOWED - ): - names = ' | '.join(t.__name__ for t in ann_types) - errors.append( - f'{loc}: setter annotated ' - f'"{names}" for a numeric ' - f'descriptor (expected float)' - ) - elif isinstance(val, GenericStringDescriptor) and not set(ann_types).issubset( - _STRING_ALLOWED - ): - names = ' | '.join(t.__name__ for t in ann_types) - errors.append( - f'{loc}: setter annotated "{names}" for a string descriptor (expected str)' - ) - - -# --------------------------------------------------------------------------- -# Entry point -# --------------------------------------------------------------------------- - - -def main() -> int: - """Run all consistency checks and print results.""" - # Import everything so all subclasses are registered - import_failures = _import_all_submodules('easydiffraction') - if import_failures: - print(f'\n⚠️ {len(import_failures)} module(s) failed to import:') - for msg in import_failures: - print(f' {msg}') - print() - - errors: list[str] = [] - checked = 0 - skipped: list[str] = [] - unexpected_skips: list[str] = [] - - all_targets: list[type] = [] - for base in (CategoryItem, CategoryCollection): - for cls in sorted(_concrete_subclasses(base), key=lambda c: c.__name__): - all_targets.append(cls) - - for cls in all_targets: - try: - instance = cls() - except Exception as exc: # noqa: BLE001 - name = cls.__name__ - if name in _KNOWN_INTERMEDIATE_BASES: - skipped.append(f'{name} (expected: {exc})') - else: - unexpected_skips.append(f'{name}: {exc}') - continue - - _check_class(cls, instance, errors) - checked += 1 - - # Report skipped intermediate bases (informational) - if skipped: - print(f'ℹ️ {len(skipped)} known intermediate base(s) skipped:') - for msg in skipped: - print(f' {msg}') - print() - - # Unexpected skips are errors - if unexpected_skips: - print(f'❌ {len(unexpected_skips)} class(es) unexpectedly failed to instantiate:') - for msg in unexpected_skips: - print(f' {msg}') - print(' Add to _KNOWN_INTERMEDIATE_BASES if this is expected, or fix the constructor.\n') - - # Summary - print(f'Checked {checked} classes') - if errors or unexpected_skips: - if errors: - print(f'\n❌ {len(errors)} consistency issue(s) found:\n') - for i, err in enumerate(errors, 1): - print(f' {i}. {err}\n') - return 1 - else: - print('✅ All parameter-property consistency checks passed.') - return 0 - - -if __name__ == '__main__': - sys.exit(main()) From 1257b1506a5eafca97b5b8fe2467340dd40dbca4 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 16:00:02 +0100 Subject: [PATCH 30/48] Apply docstrings formatting --- pixi.lock | 4 +- pixi.toml | 2 +- pyproject.toml | 6 +- src/easydiffraction/analysis/analysis.py | 45 +++++++------- .../analysis/calculators/base.py | 3 +- .../analysis/calculators/crysfml.py | 12 ++-- .../analysis/calculators/cryspy.py | 24 ++++---- .../analysis/calculators/factory.py | 6 +- .../analysis/calculators/pdffit.py | 3 +- .../analysis/categories/aliases/default.py | 13 ++-- .../categories/constraints/default.py | 9 ++- .../analysis/categories/fit_mode/fit_mode.py | 9 ++- .../joint_fit_experiments/default.py | 9 ++- .../joint_fit_experiments/factory.py | 4 +- .../analysis/fit_helpers/metrics.py | 3 +- .../analysis/fit_helpers/reporting.py | 9 +-- .../analysis/fit_helpers/tracking.py | 10 ++-- src/easydiffraction/analysis/fitting.py | 17 +++--- .../analysis/minimizers/base.py | 6 +- .../analysis/minimizers/dfols.py | 5 +- .../analysis/minimizers/lmfit.py | 3 +- src/easydiffraction/core/category.py | 10 ++-- src/easydiffraction/core/collection.py | 15 +++-- src/easydiffraction/core/datablock.py | 7 ++- src/easydiffraction/core/diagnostic.py | 10 ++-- src/easydiffraction/core/factory.py | 16 ++--- src/easydiffraction/core/guard.py | 18 +++--- src/easydiffraction/core/identity.py | 3 +- src/easydiffraction/core/metadata.py | 17 +++--- src/easydiffraction/core/singleton.py | 30 ++++++---- src/easydiffraction/core/validation.py | 17 ++++-- src/easydiffraction/core/variable.py | 32 ++++++---- .../crystallography/crystallography.py | 6 +- .../crystallography/space_groups.py | 6 +- .../experiment/categories/background/base.py | 3 +- .../categories/background/chebyshev.py | 15 +++-- .../categories/background/line_segment.py | 12 ++-- .../experiment/categories/data/bragg_pd.py | 48 ++++++++------- .../experiment/categories/data/bragg_sc.py | 41 ++++++++----- .../experiment/categories/data/total_pd.py | 28 +++++---- .../categories/excluded_regions/default.py | 12 ++-- .../categories/excluded_regions/factory.py | 4 +- .../categories/experiment_type/default.py | 20 ++++--- .../experiment/categories/extinction/shelx.py | 10 ++-- .../experiment/categories/instrument/base.py | 6 +- .../experiment/categories/instrument/cwl.py | 6 +- .../experiment/categories/instrument/tof.py | 15 +++-- .../categories/linked_crystal/default.py | 9 ++- .../categories/linked_phases/default.py | 6 +- .../experiment/categories/peak/cwl_mixins.py | 38 ++++++++---- .../experiment/categories/peak/tof_mixins.py | 35 +++++++---- .../categories/peak/total_mixins.py | 23 ++++--- .../datablocks/experiment/collection.py | 3 +- .../datablocks/experiment/item/base.py | 32 ++++++---- .../datablocks/experiment/item/bragg_pd.py | 9 +-- .../datablocks/experiment/item/bragg_sc.py | 16 +++-- .../datablocks/experiment/item/factory.py | 7 ++- .../datablocks/experiment/item/total_pd.py | 3 +- .../categories/atom_sites/default.py | 43 +++++++------ .../structure/categories/cell/default.py | 24 +++++--- .../categories/space_group/default.py | 12 ++-- .../datablocks/structure/collection.py | 3 +- .../datablocks/structure/item/base.py | 4 +- .../datablocks/structure/item/factory.py | 3 +- src/easydiffraction/display/__init__.py | 3 +- src/easydiffraction/display/base.py | 10 ++-- .../display/plotters/__init__.py | 3 +- src/easydiffraction/display/plotters/ascii.py | 13 ++-- src/easydiffraction/display/plotters/base.py | 14 +++-- .../display/plotters/plotly.py | 17 +++--- src/easydiffraction/display/plotting.py | 29 +++++---- .../display/tablers/__init__.py | 3 +- src/easydiffraction/display/tablers/base.py | 12 ++-- src/easydiffraction/display/tablers/pandas.py | 10 ++-- src/easydiffraction/display/tablers/rich.py | 7 ++- src/easydiffraction/display/tables.py | 17 +++--- src/easydiffraction/display/utils.py | 4 +- src/easydiffraction/io/cif/handler.py | 3 +- src/easydiffraction/io/cif/serialize.py | 19 +++--- src/easydiffraction/project/project.py | 10 ++-- src/easydiffraction/project/project_info.py | 5 +- src/easydiffraction/summary/summary.py | 18 +++--- .../utils/_vendored/__init__.py | 11 ++-- .../utils/_vendored/theme_detect.py | 26 ++++---- src/easydiffraction/utils/environment.py | 9 ++- src/easydiffraction/utils/logging.py | 49 ++++++++------- src/easydiffraction/utils/utils.py | 60 ++++++++++--------- 87 files changed, 742 insertions(+), 489 deletions(-) diff --git a/pixi.lock b/pixi.lock index 32b17e23..4ae22895 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty29 - sha256: f79ed38b71757cf1a1590713918d3cf0ecf291a1b756edd7c0030cd3981857af + version: 0.10.2+dev33 + sha256: 29514fa5b8b013c07c7dbb679a502c339da4c2e394c411e598406f1adddeedfa requires_dist: - asciichartpy - asteval diff --git a/pixi.toml b/pixi.toml index a4b248ea..4020b449 100644 --- a/pixi.toml +++ b/pixi.toml @@ -128,7 +128,7 @@ nonpy-format-fix-modified = 'python tools/nonpy_prettier_modified.py --write' success-message = 'echo "✅ All auto-formatting steps completed successfully!"' fix = { depends-on = [ - 'param-docstring-fix', # ED only + #'param-docstring-fix', # ED only 'docstring-format-fix', 'py-format-fix', 'py-lint-fix', diff --git a/pyproject.toml b/pyproject.toml index c650a466..9029cef0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -237,7 +237,7 @@ select = [ #'PLW', # https://docs.astral.sh/ruff/rules/#warning-plw # flake8 rules #'A', # https://docs.astral.sh/ruff/rules/#flake8-builtins-a - 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann 'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg #'ASYNC', # https://docs.astral.sh/ruff/rules/#flake8-async-async 'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b @@ -280,7 +280,7 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc # Temporary: 'DTZ005', - 'W505' + 'W505', ] @@ -374,4 +374,4 @@ exclude = 'src/easydiffraction/utils/_vendored/jupyter_dark_detect' # ED only docstring_style = 'numpy' line_length = 72 fix_rst_backticks = true -verbose = 'diff' +verbose = 'default' # or 'diff' to show changes made to docstrings diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index d9c5442b..de8844b2 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -30,9 +30,9 @@ class Analysis: """ High-level orchestration of analysis tasks for a Project. - This class wires calculators and minimizers, exposes a compact interface - for parameters, constraints and results, and coordinates computations - across the project's structures and experiments. + This class wires calculators and minimizers, exposes a compact + interface for parameters, constraints and results, and coordinates + computations across the project's structures and experiments. Typical usage: @@ -253,7 +253,8 @@ def _get_params_as_dataframe( return df def show_all_params(self) -> None: - """Print a table with all parameters for structures and + """ + Print a table with all parameters for structures and experiments. """ structures_params = self.project.structures.parameters @@ -285,8 +286,8 @@ def show_all_params(self) -> None: tabler.render(filtered_df) def show_fittable_params(self) -> None: - """Print a table with parameters that can be included in - fitting. + """ + Print a table with parameters that can be included in fitting. """ structures_params = self.project.structures.fittable_parameters experiments_params = self.project.experiments.fittable_parameters @@ -319,8 +320,8 @@ def show_fittable_params(self) -> None: tabler.render(filtered_df) def show_free_params(self) -> None: - """Print a table with only currently-free (varying) - parameters. + """ + Print a table with only currently-free (varying) parameters. """ structures_params = self.project.structures.free_parameters experiments_params = self.project.experiments.free_parameters @@ -352,7 +353,8 @@ def show_free_params(self) -> None: tabler.render(filtered_df) def how_to_access_parameters(self) -> None: - """Show Python access paths for all parameters. + """ + Show Python access paths for all parameters. The output explains how to reference specific parameters in code. @@ -416,7 +418,8 @@ def how_to_access_parameters(self) -> None: ) def show_parameter_cif_uids(self) -> None: - """Show CIF unique IDs for all parameters. + """ + Show CIF unique IDs for all parameters. The output explains which unique identifiers are used when creating CIF-based constraints. @@ -479,8 +482,8 @@ def show_current_minimizer(self) -> None: @staticmethod def show_available_minimizers() -> None: - """Print a table of available minimizer drivers on this - system. + """ + Print a table of available minimizer drivers on this system. """ MinimizerFactory.show_supported() @@ -587,8 +590,8 @@ def show_constraints(self) -> None: ) def apply_constraints(self) -> None: - """Apply the currently defined constraints to the active - project. + """ + Apply the currently defined constraints to the active project. """ if not self.constraints._items: log.warning('No constraints defined.') @@ -599,7 +602,8 @@ def apply_constraints(self) -> None: self.constraints_handler.apply() def fit(self) -> None: - """Execute fitting using the selected mode, calculator and + """ + Execute fitting using the selected mode, calculator and minimizer. This method performs the optimization but does not display @@ -672,7 +676,8 @@ def fit(self) -> None: self.fit_results = self.fitter.results def show_fit_results(self) -> None: - """Display a summary of the fit results. + """ + Display a summary of the fit results. Renders the fit quality metrics (reduced χ², R-factors) and a table of fitted parameters with their starting values, final @@ -698,8 +703,8 @@ def _update_categories(self, called_by_minimizer: bool = False) -> None: """ Update all categories owned by Analysis. - This ensures aliases and constraints are up-to-date before serialization - or after parameter changes. + This ensures aliases and constraints are up-to-date before + serialization or after parameter changes. Parameters ---------- @@ -731,8 +736,8 @@ def as_cif(self) -> str: return analysis_to_cif(self) def show_as_cif(self) -> None: - """Render the analysis section as CIF in a formatted console - view. + """ + Render the analysis section as CIF in a formatted console view. """ cif_text: str = self.as_cif() paragraph_title: str = 'Analysis 🧮 info as cif' diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index ae0c1b4c..7ff6bc92 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -45,7 +45,8 @@ def calculate_pattern( called_by_minimizer: bool, ) -> np.ndarray: """ - Calculate the diffraction pattern for a single structure and experiment. + Calculate the diffraction pattern for a single structure and + experiment. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 4605f275..2fdf8736 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -72,8 +72,8 @@ def calculate_pattern( called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: """ - Calculates the diffraction pattern using Crysfml for the given structure - and experiment. + Calculates the diffraction pattern using Crysfml for the given + structure and experiment. Parameters ---------- @@ -87,8 +87,8 @@ def calculate_pattern( Returns ------- Union[np.ndarray, List[float]] - The calculated diffraction pattern as a NumPy array or a list of - floats. + The calculated diffraction pattern as a NumPy array or a + list of floats. """ # Intentionally unused, required by public API/signature del called_by_minimizer @@ -134,8 +134,8 @@ def _crysfml_dict( experiment: ExperimentBase, ) -> Dict[str, Union[ExperimentBase, Structure]]: """ - Converts the structure and experiment into a dictionary format for - Crysfml. + Converts the structure and experiment into a dictionary format + for Crysfml. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 0d1354e5..8e00c20a 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -35,7 +35,8 @@ @CalculatorFactory.register class CryspyCalculator(CalculatorBase): - """Cryspy-based diffraction calculator. + """ + Cryspy-based diffraction calculator. Converts EasyDiffraction models into Cryspy objects and computes patterns. @@ -62,7 +63,8 @@ def calculate_structure_factors( called_by_minimizer: bool = False, ) -> None: """ - Raises a NotImplementedError as HKL calculation is not implemented. + Raises a NotImplementedError as HKL calculation is not + implemented. Parameters ---------- @@ -121,13 +123,13 @@ def calculate_pattern( called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: """ - Calculates the diffraction pattern using Cryspy for the given structure - and experiment. + Calculates the diffraction pattern using Cryspy for the given + structure and experiment. - We only recreate the cryspy_obj if this method is - NOT called by the - minimizer, or - the cryspy_dict is NOT yet created. In other cases, we - are modifying the existing cryspy_dict This allows significantly - speeding up the calculation + We only recreate the cryspy_obj if this method is - NOT called + by the minimizer, or - the cryspy_dict is NOT yet created. In + other cases, we are modifying the existing cryspy_dict This + allows significantly speeding up the calculation Parameters ---------- @@ -201,7 +203,8 @@ def _recreate_cryspy_dict( experiment: ExperimentBase, ) -> Dict[str, Any]: """ - Recreates the Cryspy dictionary for the given structure and experiment. + Recreates the Cryspy dictionary for the given structure and + experiment. Parameters ---------- @@ -320,7 +323,8 @@ def _recreate_cryspy_obj( experiment: ExperimentBase, ) -> object: """ - Recreates the Cryspy object for the given structure and experiment. + Recreates the Cryspy object for the given structure and + experiment. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/factory.py b/src/easydiffraction/analysis/calculators/factory.py index edc395db..f9860f81 100644 --- a/src/easydiffraction/analysis/calculators/factory.py +++ b/src/easydiffraction/analysis/calculators/factory.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Calculator factory — delegates to ``FactoryBase``. +""" +Calculator factory — delegates to ``FactoryBase``. Overrides ``_supported_map`` to filter out calculators whose engines are not importable in the current environment. @@ -17,7 +18,8 @@ class CalculatorFactory(FactoryBase): - """Factory for creating calculation engine instances. + """ + Factory for creating calculation engine instances. Only calculators whose ``engine_imported`` flag is ``True`` are available for creation. diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py index 6fb0880a..28806f31 100644 --- a/src/easydiffraction/analysis/calculators/pdffit.py +++ b/src/easydiffraction/analysis/calculators/pdffit.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""PDF calculation backend using diffpy.pdffit2 if available. +""" +PDF calculation backend using diffpy.pdffit2 if available. The class adapts the engine to EasyDiffraction calculator interface and silences stdio on import to avoid noisy output in notebooks and logs. diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index b6a4d32f..63cc5f28 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Alias category for mapping friendly names to parameter UIDs. +""" +Alias category for mapping friendly names to parameter UIDs. Defines a small record type used by analysis configuration to refer to parameters via readable labels instead of raw unique identifiers. @@ -22,8 +23,8 @@ class Alias(CategoryItem): """ Single alias entry. - Maps a human-readable ``label`` to a concrete ``param_uid`` used by the - engine. + Maps a human-readable ``label`` to a concrete ``param_uid`` used by + the engine. """ def __init__(self) -> None: @@ -57,7 +58,8 @@ def __init__(self) -> None: @property def label(self) -> StringDescriptor: - """... + """ + ... Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -71,7 +73,8 @@ def label(self, value: str) -> None: @property def param_uid(self) -> StringDescriptor: - """... + """ + ... Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 77f57fdb..ebc51aa8 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Simple symbolic constraint between parameters. +""" +Simple symbolic constraint between parameters. Represents an equation of the form ``lhs_alias = rhs_expr`` where ``rhs_expr`` is evaluated elsewhere by the analysis engine. @@ -55,7 +56,8 @@ def __init__(self) -> None: @property def lhs_alias(self) -> StringDescriptor: - """Left-hand side of the equation. + """ + Left-hand side of the equation. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -69,7 +71,8 @@ def lhs_alias(self, value: str) -> None: @property def rhs_expr(self) -> StringDescriptor: - """Right-hand side expression. + """ + Right-hand side expression. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index 5ec2039a..b0589a2e 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Fit-mode category item. +""" +Fit-mode category item. Stores the active fitting strategy as a CIF-serializable descriptor validated by ``FitModeEnum``. @@ -20,7 +21,8 @@ @FitModeFactory.register class FitMode(CategoryItem): - """Fitting strategy selector. + """ + Fitting strategy selector. Holds a single ``mode`` descriptor whose value is one of ``FitModeEnum`` members (``'single'`` or ``'joint'``). @@ -48,7 +50,8 @@ def __init__(self) -> None: @property def mode(self) -> StringDescriptor: - """Fitting strategy. + """ + Fitting strategy. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index cca7a48e..454c2524 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Joint-fit experiment weighting configuration. +""" +Joint-fit experiment weighting configuration. Stores per-experiment weights to be used when multiple experiments are fitted simultaneously. @@ -58,7 +59,8 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: - """Experiment identifier. + """ + Experiment identifier. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -72,7 +74,8 @@ def id(self, value: str) -> None: @property def weight(self) -> NumericDescriptor: - """Weight factor. + """ + Weight factor. Reading this property returns the underlying ``NumericDescriptor`` object. Assigning to it updates the diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py index 897a3746..f02ef6ee 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Joint-fit-experiments factory — delegates entirely to -``FactoryBase``. +""" +Joint-fit-experiments factory — delegates entirely to ``FactoryBase``. """ from __future__ import annotations diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index 8616593f..e5ccdad9 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -43,7 +43,8 @@ def calculate_weighted_r_factor( weights: np.ndarray, ) -> float: """ - Calculate the weighted R-factor between observed and calculated data. + Calculate the weighted R-factor between observed and calculated + data. Parameters ---------- diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index e851306b..a1468aa5 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -13,7 +13,8 @@ class FitResults: - """Container for results of a single optimization run. + """ + Container for results of a single optimization run. Holds success flag, chi-square metrics, iteration counts, timing, and parameter objects. Provides a printer to summarize key @@ -57,9 +58,9 @@ def __init__( fitting_time : Optional[float], default=None Time taken for the fitting process. **kwargs : object - Additional engine-specific fields. If ``redchi`` is provided and - ``reduced_chi_square`` is not set, it is used as the reduced - chi-square value. + Additional engine-specific fields. If ``redchi`` is provided + and ``reduced_chi_square`` is not set, it is used as the + reduced chi-square value. """ self.success: bool = success self.parameters: List[object] = parameters if parameters is not None else [] diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index 6f86194e..05b607c1 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -35,7 +35,8 @@ class _TerminalLiveHandle: - """Adapter that exposes update()/close() for terminal live updates. + """ + Adapter that exposes update()/close() for terminal live updates. Wraps a rich.live.Live instance but keeps the tracker decoupled from the underlying UI mechanism. @@ -53,8 +54,8 @@ def close(self) -> None: def _make_display_handle() -> object | None: - """Create and initialize a display/update handle for the - environment. + """ + Create and initialize a display/update handle for the environment. - In Jupyter, returns an IPython DisplayHandle and creates a placeholder. - In terminal, returns a _TerminalLiveHandle backed by @@ -75,7 +76,8 @@ def _make_display_handle() -> object | None: class FitProgressTracker: - """Track and report reduced chi-square during optimization. + """ + Track and report reduced chi-square during optimization. The tracker keeps iteration counters, remembers the best observed reduced chi-square and when it occurred, and can display progress as diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index c807b7b0..e2d14a1b 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -38,9 +38,9 @@ def fit( """ Run the fitting process. - This method performs the optimization but does not display results. Use - :meth:`show_fit_results` on the Analysis object to display the fit - results after fitting is complete. + This method performs the optimization but does not display + results. Use :meth:`show_fit_results` on the Analysis object to + display the fit results after fitting is complete. Parameters ---------- @@ -84,9 +84,10 @@ def _process_fit_results( """ Collect reliability inputs and display fit results. - This method is typically called by :meth:`Analysis.show_fit_results` - rather than directly. It calculates R-factors and other metrics, then - renders them to the console. + This method is typically called by + :meth:`Analysis.show_fit_results` rather than directly. It + calculates R-factors and other metrics, then renders them to the + console. Parameters ---------- @@ -146,8 +147,8 @@ def _residual_function( ) -> np.ndarray: """ Residual function computes the difference between measured and - calculated patterns. It updates the parameter values according to the - optimizer-provided engine_params. + calculated patterns. It updates the parameter values according + to the optimizer-provided engine_params. Parameters ---------- diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index a0af66de..c3d666a9 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -16,7 +16,8 @@ class MinimizerBase(ABC): - """Abstract base for concrete minimizers. + """ + Abstract base for concrete minimizers. Contract: - Subclasses must implement ``_prepare_solver_args``, ``_run_solver``, ``_sync_result_to_parameters`` and @@ -91,7 +92,8 @@ def _sync_result_to_parameters( raw_result: object, parameters: List[object], ) -> None: - """Copy values from ``raw_result`` back to ``parameters`` in- + """ + Copy values from ``raw_result`` back to ``parameters`` in- place. """ pass diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index 59878437..1c323963 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -16,8 +16,9 @@ @MinimizerFactory.register class DfolsMinimizer(MinimizerBase): - """Minimizer using the DFO-LS package (Derivative-Free Optimization - for Least-Squares). + """ + Minimizer using the DFO-LS package (Derivative-Free Optimization for + Least-Squares). """ type_info = TypeInfo( diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index 834f1804..18484c59 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -50,7 +50,8 @@ def _prepare_solver_args( Returns ------- Dict[str, object] - A dictionary containing the prepared lmfit. Parameters object. + A dictionary containing the prepared lmfit. Parameters + object. """ engine_parameters = lmfit.Parameters() for param in parameters: diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index 8cb21271..f94f5bec 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -160,7 +160,8 @@ def help(self) -> None: class CategoryCollection(CollectionBase): - """Handles loop-style category containers (e.g. AtomSites). + """ + Handles loop-style category containers (e.g. AtomSites). Each item is a CategoryItem (component). """ @@ -173,7 +174,8 @@ def _key_for(self, item: object) -> str | None: return item._identity.category_entry_name def _mark_parent_dirty(self) -> None: - """Set ``_need_categories_update`` on the parent datablock. + """ + Set ``_need_categories_update`` on the parent datablock. Called whenever the collection content changes (items added or removed) so that subsequent ``_update_categories()`` calls re- @@ -231,8 +233,8 @@ def create(self, **kwargs: object) -> None: """ Create a new item with the given attributes and add it. - A default instance of the collection's item type is created, then each - keyword argument is applied via ``setattr``. + A default instance of the collection's item type is created, + then each keyword argument is applied via ``setattr``. Parameters ---------- diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py index e5164e41..0560a37e 100644 --- a/src/easydiffraction/core/collection.py +++ b/src/easydiffraction/core/collection.py @@ -1,10 +1,11 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Lightweight container for guarded items with name-based indexing. +""" +Lightweight container for guarded items with name-based indexing. -`CollectionBase` maintains an ordered list of items and a lazily rebuilt -index by the item's identity key. It supports dict-like access for get, -set and delete, along with iteration over the items. +``CollectionBase`` maintains an ordered list of items and a lazily +rebuilt index by the item's identity key. It supports dict-like access +for get, set and delete, along with iteration over the items. """ from __future__ import annotations @@ -33,7 +34,8 @@ def __init__(self, item_type: type) -> None: self._item_type = item_type def __getitem__(self, name: str) -> GuardedBase: - """Return an item by its identity key. + """ + Return an item by its identity key. Rebuilds the internal index on a cache miss to stay consistent with recent mutations. @@ -100,7 +102,8 @@ def remove(self, name: str) -> None: raise def _key_for(self, item: GuardedBase) -> str | None: - """Return the identity key for *item*. + """ + Return the identity key for *item*. Subclasses must override to return the appropriate key (``category_entry_name`` or ``datablock_entry_name``). diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 066f9435..2583079f 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -75,8 +75,8 @@ def categories(self) -> list: @property def parameters(self) -> list: - """All parameters from all categories contained in this - datablock. + """ + All parameters from all categories contained in this datablock. """ params = [] for v in self.categories: @@ -118,7 +118,8 @@ def help(self) -> None: class DatablockCollection(CollectionBase): - """Handles top-level category collections (e.g. Structures, + """ + Handles top-level category collections (e.g. Structures, Experiments). Each item is a DatablockItem. diff --git a/src/easydiffraction/core/diagnostic.py b/src/easydiffraction/core/diagnostic.py index 30b7cf14..3132ed7a 100644 --- a/src/easydiffraction/core/diagnostic.py +++ b/src/easydiffraction/core/diagnostic.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Diagnostics helpers for logging validation messages. +""" +Diagnostics helpers for logging validation messages. This module centralizes human-friendly error and debug logs for attribute validation and configuration checks. @@ -20,7 +21,8 @@ class Diagnostics: @staticmethod def type_override_error(cls_name: str, expected: object, got: object) -> None: - """Report an invalid DataTypes override. + """ + Report an invalid DataTypes override. Used when descriptor and AttributeSpec types conflict. """ @@ -55,8 +57,8 @@ def attr_error( allowed: set[str], label: str = 'Allowed', ) -> None: - """Log access to an unknown attribute and suggest closest - key. + """ + Log access to an unknown attribute and suggest closest key. """ suggestion = Diagnostics._build_suggestion(key, allowed) # Use consistent (label) logic for allowed diff --git a/src/easydiffraction/core/factory.py b/src/easydiffraction/core/factory.py index af7395d6..af99217a 100644 --- a/src/easydiffraction/core/factory.py +++ b/src/easydiffraction/core/factory.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Base factory with registration, lookup, and context-dependent -defaults. +""" +Base factory with registration, lookup, and context-dependent defaults. Concrete factories inherit from ``FactoryBase`` and only need to define ``_default_rules``. @@ -21,7 +21,8 @@ class FactoryBase: - """Shared base for all factories. + """ + Shared base for all factories. Subclasses must set: @@ -48,7 +49,8 @@ def __init_subclass__(cls, **kwargs: object) -> None: @classmethod def register(cls, klass: type) -> type: - """Class decorator to register a concrete class. + """ + Class decorator to register a concrete class. Usage:: @@ -83,9 +85,9 @@ def default_tag(cls, **conditions: object) -> str: """ Resolve the default tag for a given experimental context. - Uses *largest-subset matching*: the rule whose key is the biggest subset - of the given conditions wins. A rule with an empty key (``frozenset()``) - acts as a universal fallback. + Uses *largest-subset matching*: the rule whose key is the + biggest subset of the given conditions wins. A rule with an + empty key (``frozenset()``) acts as a universal fallback. Parameters ---------- diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index fa67426b..581f9722 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -12,8 +12,8 @@ class GuardedBase(ABC): - """Base class enforcing controlled attribute access and parent - linkage. + """ + Base class enforcing controlled attribute access and parent linkage. """ _diagnoser = Diagnostics() @@ -80,7 +80,8 @@ def _assign_attr(self, key: str, value: object) -> None: @classmethod def _iter_properties(cls) -> Generator[tuple[str, property], None, None]: """ - Iterate over all public properties defined in the class hierarchy. + Iterate over all public properties defined in the class + hierarchy. Yields ------ @@ -134,7 +135,8 @@ def unique_name(self) -> str: @property @abstractmethod def parameters(self) -> list: - """Return a list of parameter objects (to be implemented by + """ + Return a list of parameter objects (to be implemented by subclasses). """ raise NotImplementedError @@ -142,14 +144,16 @@ def parameters(self) -> list: @property @abstractmethod def as_cif(self) -> str: - """Return CIF representation of this object (to be implemented - by subclasses). + """ + Return CIF representation of this object (to be implemented by + subclasses). """ raise NotImplementedError @staticmethod def _first_sentence(docstring: str | None) -> str: - """Extract the first paragraph from a docstring. + """ + Extract the first paragraph from a docstring. Returns text before the first blank line, with continuation lines joined into a single string. diff --git a/src/easydiffraction/core/identity.py b/src/easydiffraction/core/identity.py index 9cb00d95..d64fce81 100644 --- a/src/easydiffraction/core/identity.py +++ b/src/easydiffraction/core/identity.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Identity helpers to build CIF-like hierarchical names. +""" +Identity helpers to build CIF-like hierarchical names. Used by containers and items to expose datablock/category/entry names without tight coupling. diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index 9d721f5c..a5d7e8f6 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Metadata dataclasses for factory-created classes. +""" +Metadata dataclasses for factory-created classes. Three frozen dataclasses describe a concrete class: @@ -18,8 +19,8 @@ @dataclass(frozen=True) class TypeInfo: """ - Stable identity and human-readable description for a factory- created - class. + Stable identity and human-readable description for a factory- + created class. Attributes ---------- @@ -39,7 +40,8 @@ class TypeInfo: @dataclass(frozen=True) class Compatibility: - """Experimental conditions under which a class can be used. + """ + Experimental conditions under which a class can be used. Each field is a frozenset of enum values representing the set of supported values for that axis. An empty frozenset means @@ -58,7 +60,8 @@ def supports( beam_mode: object = None, radiation_probe: object = None, ) -> bool: - """Check if this compatibility matches the given conditions. + """ + Check if this compatibility matches the given conditions. Each argument is an optional enum member. Returns ``True`` if every provided value is in the corresponding frozenset (or the @@ -109,8 +112,8 @@ def supports(self, calculator: object) -> bool: Returns ------- bool - ``True`` if the calculator is in the set, or if the set is empty - (meaning any calculator is accepted). + ``True`` if the calculator is in the set, or if the set is + empty (meaning any calculator is accepted). """ if not self.calculators: return True diff --git a/src/easydiffraction/core/singleton.py b/src/easydiffraction/core/singleton.py index 7ab6233c..d7f0d14c 100644 --- a/src/easydiffraction/core/singleton.py +++ b/src/easydiffraction/core/singleton.py @@ -16,7 +16,8 @@ class SingletonBase: - """Base class to implement Singleton pattern. + """ + Base class to implement Singleton pattern. Ensures only one shared instance of a class is ever created. Useful for managing shared state across the library. @@ -47,7 +48,8 @@ def get_uid_map(self) -> Dict[str, Any]: return self._uid_map def add_to_uid_map(self, parameter: object) -> None: - """Adds a single Parameter or Descriptor object to the UID map. + """ + Adds a single Parameter or Descriptor object to the UID map. Only Descriptor or Parameter instances are allowed (not Components or others). @@ -62,7 +64,8 @@ def add_to_uid_map(self, parameter: object) -> None: self._uid_map[parameter.uid] = parameter def replace_uid(self, old_uid: str, new_uid: str) -> None: - """Replaces an existing UID key in the UID map with a new UID. + """ + Replaces an existing UID key in the UID map with a new UID. Moves the associated parameter from old_uid to new_uid. Raises a KeyError if the old_uid doesn't exist. @@ -82,7 +85,8 @@ def replace_uid(self, old_uid: str, new_uid: str) -> None: # TODO: Implement changing atrr '.constrained' back to False # when removing constraints class ConstraintsHandler(SingletonBase): - """Manages user-defined parameter constraints using aliases and + """ + Manages user-defined parameter constraints using aliases and expressions. Uses the asteval interpreter for safe evaluation of mathematical @@ -103,7 +107,8 @@ def __init__(self) -> None: self._parsed_constraints: List[Tuple[str, str]] = [] def set_aliases(self, aliases: object) -> None: - """Sets the alias map (name → parameter wrapper). + """ + Sets the alias map (name → parameter wrapper). Called when user registers parameter aliases like: alias='biso_La', param=model.atom_sites['La'].b_iso @@ -111,8 +116,8 @@ def set_aliases(self, aliases: object) -> None: self._alias_to_param = dict(aliases.items()) def set_constraints(self, constraints: object) -> None: - """Sets the constraints and triggers parsing into internal - format. + """ + Sets the constraints and triggers parsing into internal format. Called when user registers expressions like: lhs_alias='occ_Ba', rhs_expr='1 - occ_La' @@ -121,9 +126,10 @@ def set_constraints(self, constraints: object) -> None: self._parse_constraints() def _parse_constraints(self) -> None: - """Converts raw expression input into a normalized internal list - of (lhs_alias, rhs_expr) pairs, stripping whitespace and - skipping invalid entries. + """ + Converts raw expression input into a normalized internal list of + (lhs_alias, rhs_expr) pairs, stripping whitespace and skipping + invalid entries. """ self._parsed_constraints = [] @@ -136,8 +142,8 @@ def _parse_constraints(self) -> None: self._parsed_constraints.append(constraint) def apply(self) -> None: - """Evaluates constraints and applies them to dependent - parameters. + """ + Evaluates constraints and applies them to dependent parameters. For each constraint: - Evaluate RHS using current values of aliases - Locate the dependent parameter by alias → uid → param diff --git a/src/easydiffraction/core/validation.py b/src/easydiffraction/core/validation.py index e224c8f7..5103cbfe 100644 --- a/src/easydiffraction/core/validation.py +++ b/src/easydiffraction/core/validation.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Lightweight runtime validation utilities. +""" +Lightweight runtime validation utilities. Provides DataTypes, type/content validators, and AttributeSpec used by descriptors and parameters. Only documentation was added here. @@ -79,7 +80,8 @@ def validated( default: object = None, current: object = None, ) -> object: - """Return a validated value or fallback. + """ + Return a validated value or fallback. Subclasses must implement this method. """ @@ -115,7 +117,8 @@ def validated( current: object = None, allow_none: bool = False, ) -> object: - """Validate type and return value or fallback. + """ + Validate type and return value or fallback. If allow_none is True, None bypasses content checks. """ @@ -193,9 +196,10 @@ def validated( class MembershipValidator(ValidatorBase): - """Ensure that a value is among allowed choices. + """ + Ensure that a value is among allowed choices. - `allowed` may be an iterable or a callable returning a collection. + ``allowed`` may be an iterable or a callable returning a collection. """ def __init__(self, allowed: object) -> None: @@ -293,7 +297,8 @@ def validated( name: str, current: object = None, ) -> object: - """Validate through type and content validators. + """ + Validate through type and content validators. Returns validated value, possibly default or current if errors occur. None may short-circuit further checks when allowed. diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 1733101b..1390565e 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -29,10 +29,11 @@ class GenericDescriptorBase(GuardedBase): """ Base class for all parameter-like descriptors. - A descriptor encapsulates a typed value with validation, human-readable - name/description and a globally unique identifier that is stable across - the session. Concrete subclasses specialize the expected data type and - can extend the public API with additional behavior (e.g. units). + A descriptor encapsulates a typed value with validation, + human-readable name/description and a globally unique identifier + that is stable across the session. Concrete subclasses specialize + the expected data type and can extend the public API with additional + behavior (e.g. units). """ _BOOL_SPEC_TEMPLATE = AttributeSpec( @@ -106,7 +107,8 @@ def name(self) -> str: @property def unique_name(self) -> str: - """Fully qualified name including datablock, category and entry + """ + Fully qualified name including datablock, category and entry name. """ parts = [ @@ -118,8 +120,9 @@ def unique_name(self) -> str: return '.'.join(filter(None, parts)) def _parent_of_type(self, cls: type) -> object | None: - """Walk up the parent chain and return the first parent of type - `cls`. + """ + Walk up the parent chain and return the first parent of type + ``cls``. """ obj = getattr(self, '_parent', None) visited = set() @@ -162,7 +165,8 @@ def value(self, v: object) -> None: parent_datablock._need_categories_update = True def _set_value_from_minimizer(self, v: object) -> None: - """Set the value from a minimizer, bypassing validation. + """ + Set the value from a minimizer, bypassing validation. Writes ``_value`` directly — no type or range checks — but still marks the owning :class:`DatablockItem` dirty so that @@ -187,7 +191,8 @@ def description(self) -> str | None: @property def parameters(self) -> list[GenericDescriptorBase]: - """Return a flat list of parameters contained by this object. + """ + Return a flat list of parameters contained by this object. For a single descriptor, it returns a one-element list with itself. Composite objects override this to flatten nested @@ -250,7 +255,8 @@ def units(self) -> str: class GenericParameter(GenericNumericDescriptor): - """Numeric descriptor extended with fitting-related attributes. + """ + Numeric descriptor extended with fitting-related attributes. Adds standard attributes used by minimizers: "free" flag, uncertainty, bounds and an optional starting value. Subclasses can @@ -316,7 +322,8 @@ def constrained(self) -> bool: return self._constrained def _set_value_constrained(self, v: object) -> None: - """Set the value from a constraint expression. + """ + Set the value from a constraint expression. Validates against the spec, marks the parent datablock dirty, and flags the parameter as constrained. Used exclusively by @@ -339,7 +346,8 @@ def free(self, v: bool) -> None: @property def uncertainty(self) -> float | None: - """Estimated standard uncertainty of the fitted value, if + """ + Estimated standard uncertainty of the fitted value, if available. """ return self._uncertainty diff --git a/src/easydiffraction/crystallography/crystallography.py b/src/easydiffraction/crystallography/crystallography.py index b58b19a4..1d7707f6 100644 --- a/src/easydiffraction/crystallography/crystallography.py +++ b/src/easydiffraction/crystallography/crystallography.py @@ -22,7 +22,8 @@ def apply_cell_symmetry_constraints( name_hm: str, ) -> Dict[str, float]: """ - Apply symmetry constraints to unit cell parameters based on space group. + Apply symmetry constraints to unit cell parameters based on space + group. Parameters ---------- @@ -96,7 +97,8 @@ def apply_atom_site_symmetry_constraints( wyckoff_letter: str, ) -> Dict[str, Any]: """ - Apply symmetry constraints to atomic coordinates based on site symmetry. + Apply symmetry constraints to atomic coordinates based on site + symmetry. Parameters ---------- diff --git a/src/easydiffraction/crystallography/space_groups.py b/src/easydiffraction/crystallography/space_groups.py index d3bf35fc..4047b8c5 100644 --- a/src/easydiffraction/crystallography/space_groups.py +++ b/src/easydiffraction/crystallography/space_groups.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Space group reference data. +""" +Space group reference data. Loads a gzipped, packaged pickle with crystallographic space-group information. The file is part of the distribution; user input is not @@ -13,7 +14,8 @@ def _restricted_pickle_load(file_obj: object) -> object: - """Load pickle data from an internal gz file (trusted boundary). + """ + Load pickle data from an internal gz file (trusted boundary). The archive lives in the package; no user-controlled input enters this function. If distribution process changes, revisit. diff --git a/src/easydiffraction/datablocks/experiment/categories/background/base.py b/src/easydiffraction/datablocks/experiment/categories/background/base.py index 5f392532..913cb764 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/base.py @@ -9,7 +9,8 @@ class BackgroundBase(CategoryCollection): - """Abstract base for background subcategories in experiments. + """ + Abstract base for background subcategories in experiments. Concrete implementations provide parameterized background models and compute background intensities on the experiment grid. diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index c043f9ab..4cbef2c0 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Chebyshev polynomial background model. +""" +Chebyshev polynomial background model. Provides a collection of polynomial terms and evaluation helpers. """ @@ -34,7 +35,8 @@ class PolynomialTerm(CategoryItem): - """Chebyshev polynomial term. + """ + Chebyshev polynomial term. New public attribute names: ``order`` and ``coef`` replacing the longer ``chebyshev_order`` / ``chebyshev_coef``. Backward-compatible @@ -85,7 +87,8 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: - """Identifier for this background polynomial term. + """ + Identifier for this background polynomial term. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -99,7 +102,8 @@ def id(self, value: str) -> None: @property def order(self) -> NumericDescriptor: - """Order used in a Chebyshev polynomial background term. + """ + Order used in a Chebyshev polynomial background term. Reading this property returns the underlying ``NumericDescriptor`` object. Assigning to it updates the @@ -113,7 +117,8 @@ def order(self, value: float) -> None: @property def coef(self) -> Parameter: - """Coefficient used in a Chebyshev polynomial background term. + """ + Coefficient used in a Chebyshev polynomial background term. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index d30a6d71..91c8b402 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Line-segment background model. +""" +Line-segment background model. Interpolate user-specified points to form a background curve. """ @@ -94,7 +95,8 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: - """Identifier for this background line segment. + """ + Identifier for this background line segment. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -108,7 +110,8 @@ def id(self, value: str) -> None: @property def x(self) -> NumericDescriptor: - """X-coordinates used to create many straight-line segments + """ + X-coordinates used to create many straight-line segments representing the background in a calculated diffractogram. Reading this property returns the underlying @@ -123,7 +126,8 @@ def x(self, value: float) -> None: @property def y(self) -> Parameter: - """Intensity used to create many straight-line segments + """ + Intensity used to create many straight-line segments representing the background in a calculated diffractogram. Reading this property returns the underlying ``Parameter`` diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 510d6a22..29f363b1 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -123,7 +123,8 @@ def __init__(self) -> None: @property def point_id(self) -> StringDescriptor: - """Identifier for this data point in the dataset. + """ + Identifier for this data point in the dataset. Reading this property returns the underlying ``StringDescriptor`` object. @@ -132,7 +133,8 @@ def point_id(self) -> StringDescriptor: @property def d_spacing(self) -> NumericDescriptor: - """D-spacing value corresponding to this data point. + """ + d-spacing value corresponding to this data point. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -141,7 +143,8 @@ def d_spacing(self) -> NumericDescriptor: @property def intensity_meas(self) -> NumericDescriptor: - """Intensity recorded at each measurement point as a function of + """ + Intensity recorded at each measurement point as a function of angle/time. Reading this property returns the underlying @@ -151,7 +154,8 @@ def intensity_meas(self) -> NumericDescriptor: @property def intensity_meas_su(self) -> NumericDescriptor: - """Standard uncertainty of the measured intensity at this data + """ + Standard uncertainty of the measured intensity at this data point. Reading this property returns the underlying @@ -161,8 +165,8 @@ def intensity_meas_su(self) -> NumericDescriptor: @property def intensity_calc(self) -> NumericDescriptor: - """Intensity value for a computed diffractogram at this data - point. + """ + Intensity value for a computed diffractogram at this data point. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -171,7 +175,8 @@ def intensity_calc(self) -> NumericDescriptor: @property def intensity_bkg(self) -> NumericDescriptor: - """Intensity value for a computed background at this data point. + """ + Intensity value for a computed background at this data point. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -180,7 +185,8 @@ def intensity_bkg(self) -> NumericDescriptor: @property def calc_status(self) -> StringDescriptor: - """Status code of the data point in the calculation process. + """ + Status code of the data point in the calculation process. Reading this property returns the underlying ``StringDescriptor`` object. @@ -189,8 +195,8 @@ def calc_status(self) -> StringDescriptor: class PdCwlDataPointMixin: - """Mixin for powder diffraction data points with constant - wavelength. + """ + Mixin for powder diffraction data points with constant wavelength. """ def __init__(self) -> None: @@ -218,7 +224,8 @@ def __init__(self) -> None: @property def two_theta(self) -> NumericDescriptor: - """Measured 2θ diffraction angle (deg). + """ + Measured 2θ diffraction angle (deg). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -249,7 +256,8 @@ def __init__(self) -> None: @property def time_of_flight(self) -> NumericDescriptor: - """Measured time for time-of-flight neutron measurement (µs). + """ + Measured time for time-of-flight neutron measurement (µs). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -270,8 +278,8 @@ class PdCwlDataPoint( # But also says, that in fact, it is just for consistency. And both # orders work. ): - """Powder diffraction data point for constant-wavelength - experiments. + """ + Powder diffraction data point for constant-wavelength experiments. """ def __init__(self) -> None: @@ -319,8 +327,8 @@ def _set_intensity_meas(self, values: object) -> None: p.intensity_meas._value = v def _set_intensity_meas_su(self, values: object) -> None: - """Helper method to set standard uncertainty of measured - intensity. + """ + Helper method to set standard uncertainty of measured intensity. """ for p, v in zip(self._items, values, strict=True): p.intensity_meas_su._value = v @@ -513,8 +521,8 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def two_theta(self) -> np.ndarray: - """Get the 2θ values for data points included in - calculations. + """ + Get the 2θ values for data points included in calculations. """ return np.fromiter( (p.two_theta.value for p in self._calc_items), @@ -590,8 +598,8 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def time_of_flight(self) -> np.ndarray: - """Get the TOF values for data points included in - calculations. + """ + Get the TOF values for data points included in calculations. """ return np.fromiter( (p.time_of_flight.value for p in self._calc_items), diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 2f6e5b83..00519b8b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -26,8 +26,8 @@ class Refln(CategoryItem): - """Single reflection for single crystal diffraction data - category. + """ + Single reflection for single crystal diffraction data category. """ def __init__(self) -> None: @@ -139,7 +139,8 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: - """Identifier of the reflection. + """ + Identifier of the reflection. Reading this property returns the underlying ``StringDescriptor`` object. @@ -148,7 +149,8 @@ def id(self) -> StringDescriptor: @property def d_spacing(self) -> NumericDescriptor: - """The distance between lattice planes in the crystal for this + """ + The distance between lattice planes in the crystal for this reflection (Å). Reading this property returns the underlying @@ -158,7 +160,8 @@ def d_spacing(self) -> NumericDescriptor: @property def sin_theta_over_lambda(self) -> NumericDescriptor: - """The sin(θ)/λ value for this reflection (Å⁻¹). + """ + The sin(θ)/λ value for this reflection (Å⁻¹). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -167,7 +170,8 @@ def sin_theta_over_lambda(self) -> NumericDescriptor: @property def index_h(self) -> NumericDescriptor: - """Miller index h of a measured reflection. + """ + Miller index h of a measured reflection. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -176,7 +180,8 @@ def index_h(self) -> NumericDescriptor: @property def index_k(self) -> NumericDescriptor: - """Miller index k of a measured reflection. + """ + Miller index k of a measured reflection. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -185,7 +190,8 @@ def index_k(self) -> NumericDescriptor: @property def index_l(self) -> NumericDescriptor: - """Miller index l of a measured reflection. + """ + Miller index l of a measured reflection. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -194,8 +200,8 @@ def index_l(self) -> NumericDescriptor: @property def intensity_meas(self) -> NumericDescriptor: - """The intensity of the reflection derived from the - measurements. + """ + The intensity of the reflection derived from the measurements. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -204,7 +210,8 @@ def intensity_meas(self) -> NumericDescriptor: @property def intensity_meas_su(self) -> NumericDescriptor: - """Standard uncertainty of the measured intensity. + """ + Standard uncertainty of the measured intensity. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -213,7 +220,8 @@ def intensity_meas_su(self) -> NumericDescriptor: @property def intensity_calc(self) -> NumericDescriptor: - """The intensity of the reflection calculated from the atom site + """ + The intensity of the reflection calculated from the atom site data. Reading this property returns the underlying @@ -223,8 +231,9 @@ def intensity_calc(self) -> NumericDescriptor: @property def wavelength(self) -> NumericDescriptor: - """The mean wavelength of radiation used to measure this - reflection (Å). + """ + The mean wavelength of radiation used to measure this reflection + (Å). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -291,8 +300,8 @@ def _set_intensity_meas(self, values: object) -> None: p.intensity_meas._value = v def _set_intensity_meas_su(self, values: object) -> None: - """Helper method to set standard uncertainty of measured - intensity. + """ + Helper method to set standard uncertainty of measured intensity. """ for p, v in zip(self._items, values, strict=True): p.intensity_meas_su._value = v diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index ae0c9f83..b4957ddb 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -26,7 +26,8 @@ class TotalDataPoint(CategoryItem): - """Total scattering (PDF) data point in r-space (real space). + """ + Total scattering (PDF) data point in r-space (real space). Note: PDF data is always in r-space regardless of whether the original measurement was CWL or TOF. @@ -122,7 +123,8 @@ def __init__(self) -> None: @property def point_id(self) -> StringDescriptor: - """Identifier for this data point in the dataset. + """ + Identifier for this data point in the dataset. Reading this property returns the underlying ``StringDescriptor`` object. @@ -131,7 +133,8 @@ def point_id(self) -> StringDescriptor: @property def r(self) -> NumericDescriptor: - """Interatomic distance in real space (Å). + """ + Interatomic distance in real space (Å). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -140,7 +143,8 @@ def r(self) -> NumericDescriptor: @property def g_r_meas(self) -> NumericDescriptor: - """Measured pair distribution function G(r). + """ + Measured pair distribution function G(r). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -149,7 +153,8 @@ def g_r_meas(self) -> NumericDescriptor: @property def g_r_meas_su(self) -> NumericDescriptor: - """Standard uncertainty of measured G(r). + """ + Standard uncertainty of measured G(r). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -158,7 +163,8 @@ def g_r_meas_su(self) -> NumericDescriptor: @property def g_r_calc(self) -> NumericDescriptor: - """Calculated pair distribution function G(r). + """ + Calculated pair distribution function G(r). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -167,7 +173,8 @@ def g_r_calc(self) -> NumericDescriptor: @property def calc_status(self) -> StringDescriptor: - """Status code of the data point in calculation. + """ + Status code of the data point in calculation. Reading this property returns the underlying ``StringDescriptor`` object. @@ -197,8 +204,8 @@ def _set_g_r_meas(self, values: object) -> None: p.g_r_meas._value = v def _set_g_r_meas_su(self, values: object) -> None: - """Helper method to set standard uncertainty of measured - G(r). + """ + Helper method to set standard uncertainty of measured G(r). """ for p, v in zip(self._items, values, strict=True): p.g_r_meas_su._value = v @@ -303,7 +310,8 @@ def intensity_bkg(self) -> np.ndarray: @DataFactory.register class TotalData(TotalDataBase): - """Total scattering (PDF) data collection in r-space. + """ + Total scattering (PDF) data collection in r-space. Note: Works for both CWL and TOF measurements as PDF data is always transformed to r-space. diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 1d6a30b2..4344e45e 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -72,7 +72,8 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: - """Identifier for this excluded region. + """ + Identifier for this excluded region. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -86,7 +87,8 @@ def id(self, value: str) -> None: @property def start(self) -> NumericDescriptor: - """Start of the excluded region. + """ + Start of the excluded region. Reading this property returns the underlying ``NumericDescriptor`` object. Assigning to it updates the @@ -100,7 +102,8 @@ def start(self, value: float) -> None: @property def end(self) -> NumericDescriptor: - """End of the excluded region. + """ + End of the excluded region. Reading this property returns the underlying ``NumericDescriptor`` object. Assigning to it updates the @@ -115,7 +118,8 @@ def end(self, value: float) -> None: @ExcludedRegionsFactory.register class ExcludedRegions(CategoryCollection): - """Collection of ExcludedRegion instances. + """ + Collection of ExcludedRegion instances. Excluded regions define closed intervals [start, end] on the x-axis that are to be excluded from calculations and, as a result, from diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py index 7fa9bf08..e12fb0c0 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Excluded-regions factory — delegates entirely to ``FactoryBase``.""" +""" +Excluded-regions factory — delegates entirely to ``FactoryBase``. +""" from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 4b8709ef..ce7ebe3f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Experiment type descriptor (form, beam, probe, scattering). +""" +Experiment type descriptor (form, beam, probe, scattering). This lightweight container stores the categorical attributes defining an experiment configuration and handles CIF serialization via @@ -26,7 +27,9 @@ @ExperimentTypeFactory.register class ExperimentType(CategoryItem): - """Container of categorical attributes defining experiment flavor.""" + """ + Container of categorical attributes defining experiment flavor. + """ type_info = TypeInfo( tag='default', @@ -106,7 +109,8 @@ def _set_scattering_type(self, value: str) -> None: @property def sample_form(self) -> StringDescriptor: - """Specifies whether the diffraction data corresponds to powder + """ + Specifies whether the diffraction data corresponds to powder diffraction or single crystal diffraction. Reading this property returns the underlying @@ -116,7 +120,8 @@ def sample_form(self) -> StringDescriptor: @property def beam_mode(self) -> StringDescriptor: - """Defines whether the measurement is performed with a constant + """ + Defines whether the measurement is performed with a constant wavelength (CW) or time-of-flight (TOF) method. Reading this property returns the underlying @@ -126,7 +131,8 @@ def beam_mode(self) -> StringDescriptor: @property def radiation_probe(self) -> StringDescriptor: - """Specifies whether the measurement uses neutrons or X-rays. + """ + Specifies whether the measurement uses neutrons or X-rays. Reading this property returns the underlying ``StringDescriptor`` object. @@ -135,8 +141,8 @@ def radiation_probe(self) -> StringDescriptor: @property def scattering_type(self) -> StringDescriptor: - """Specifies whether the experiment uses Bragg scattering (for. - + """ + Specifies whether the experiment uses Bragg scattering (for conventional structure refinement) or total scattering (for pair distribution function analysis - PDF). diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index 6a5e3118..816729bf 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -17,8 +17,8 @@ @ExtinctionFactory.register class ShelxExtinction(CategoryItem): - """Shelx-style isotropic extinction correction for single - crystals. + """ + Shelx-style isotropic extinction correction for single crystals. """ type_info = TypeInfo( @@ -69,7 +69,8 @@ def __init__(self) -> None: @property def mosaicity(self) -> Parameter: - """Mosaicity value for extinction correction (deg). + """ + Mosaicity value for extinction correction (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -82,7 +83,8 @@ def mosaicity(self, value: float) -> None: @property def radius(self) -> Parameter: - """Crystal radius for extinction correction (µm). + """ + Crystal radius for extinction correction (µm). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/base.py b/src/easydiffraction/datablocks/experiment/categories/instrument/base.py index d58dd059..a2568884 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/base.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Instrument category base definitions for CWL/TOF instruments. +""" +Instrument category base definitions for CWL/TOF instruments. This module provides the shared parent used by concrete instrument implementations under the instrument category. @@ -12,7 +13,8 @@ class InstrumentBase(CategoryItem): - """Base class for instrument category items. + """ + Base class for instrument category items. This class sets the common ``category_code`` and is used as a base for concrete CWL/TOF instrument definitions. diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 913e74f6..c850d198 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -37,7 +37,8 @@ def __init__(self) -> None: @property def setup_wavelength(self) -> Parameter: - """Incident neutron or X-ray wavelength (Å). + """ + Incident neutron or X-ray wavelength (Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -101,7 +102,8 @@ def __init__(self) -> None: @property def calib_twotheta_offset(self) -> Parameter: - """Instrument misalignment offset (deg). + """ + Instrument misalignment offset (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index 76f073b9..413a6609 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -100,7 +100,8 @@ def __init__(self) -> None: @property def setup_twotheta_bank(self) -> Parameter: - """Detector bank position (deg). + """ + Detector bank position (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -113,7 +114,8 @@ def setup_twotheta_bank(self, value: float) -> None: @property def calib_d_to_tof_offset(self) -> Parameter: - """TOF offset (µs). + """ + TOF offset (µs). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -126,7 +128,8 @@ def calib_d_to_tof_offset(self, value: float) -> None: @property def calib_d_to_tof_linear(self) -> Parameter: - """TOF linear conversion (µs/Å). + """ + TOF linear conversion (µs/Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -139,7 +142,8 @@ def calib_d_to_tof_linear(self, value: float) -> None: @property def calib_d_to_tof_quad(self) -> Parameter: - """TOF quadratic correction (µs/Ų). + """ + TOF quadratic correction (µs/Ų). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -152,7 +156,8 @@ def calib_d_to_tof_quad(self, value: float) -> None: @property def calib_d_to_tof_recip(self) -> Parameter: - """TOF reciprocal velocity correction (µs·Å). + """ + TOF reciprocal velocity correction (µs·Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index d0d46304..08ebb9b3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -21,7 +21,8 @@ @LinkedCrystalFactory.register class LinkedCrystal(CategoryItem): - """Linked crystal category for referencing from the experiment for + """ + Linked crystal category for referencing from the experiment for single crystal diffraction. """ @@ -63,7 +64,8 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: - """Identifier of the linked crystal. + """ + Identifier of the linked crystal. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -77,7 +79,8 @@ def id(self, value: str) -> None: @property def scale(self) -> Parameter: - """Scale factor of the linked crystal. + """ + Scale factor of the linked crystal. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index ba2e85c3..79fcb17c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -54,7 +54,8 @@ def __init__(self) -> None: @property def id(self) -> StringDescriptor: - """Identifier of the linked phase. + """ + Identifier of the linked phase. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -68,7 +69,8 @@ def id(self, value: str) -> None: @property def scale(self) -> Parameter: - """Scale factor of the linked phase. + """ + Scale factor of the linked phase. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 24aca092..67874d0b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Constant-wavelength (CWL) peak-profile component classes. +""" +Constant-wavelength (CWL) peak-profile component classes. This module provides classes that add broadening and asymmetry parameters. They are composed into concrete peak classes elsewhere via @@ -78,7 +79,8 @@ def __init__(self) -> None: @property def broad_gauss_u(self) -> Parameter: - """Gaussian broadening coefficient (dependent on sample size and + """ + Gaussian broadening coefficient (dependent on sample size and instrument resolution) (deg²). Reading this property returns the underlying ``Parameter`` @@ -92,7 +94,8 @@ def broad_gauss_u(self, value: float) -> None: @property def broad_gauss_v(self) -> Parameter: - """Gaussian broadening coefficient (instrumental broadening + """ + Gaussian broadening coefficient (instrumental broadening contribution) (deg²). Reading this property returns the underlying ``Parameter`` @@ -106,7 +109,8 @@ def broad_gauss_v(self, value: float) -> None: @property def broad_gauss_w(self) -> Parameter: - """Gaussian broadening coefficient (instrumental broadening + """ + Gaussian broadening coefficient (instrumental broadening contribution) (deg²). Reading this property returns the underlying ``Parameter`` @@ -120,7 +124,8 @@ def broad_gauss_w(self, value: float) -> None: @property def broad_lorentz_x(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on sample strain + """ + Lorentzian broadening coefficient (dependent on sample strain effects) (deg). Reading this property returns the underlying ``Parameter`` @@ -134,8 +139,9 @@ def broad_lorentz_x(self, value: float) -> None: @property def broad_lorentz_y(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on - microstructural defects and strain) (deg). + """ + Lorentzian broadening coefficient (dependent on microstructural + defects and strain) (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -200,7 +206,8 @@ def __init__(self) -> None: @property def asym_empir_1(self) -> Parameter: - """Empirical asymmetry coefficient p1. + """ + Empirical asymmetry coefficient p1. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -213,7 +220,8 @@ def asym_empir_1(self, value: float) -> None: @property def asym_empir_2(self) -> Parameter: - """Empirical asymmetry coefficient p2. + """ + Empirical asymmetry coefficient p2. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -226,7 +234,8 @@ def asym_empir_2(self, value: float) -> None: @property def asym_empir_3(self) -> Parameter: - """Empirical asymmetry coefficient p3. + """ + Empirical asymmetry coefficient p3. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -239,7 +248,8 @@ def asym_empir_3(self, value: float) -> None: @property def asym_empir_4(self) -> Parameter: - """Empirical asymmetry coefficient p4. + """ + Empirical asymmetry coefficient p4. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -284,7 +294,8 @@ def __init__(self) -> None: @property def asym_fcj_1(self) -> Parameter: - """Finger-Cox-Jephcoat asymmetry parameter 1. + """ + Finger-Cox-Jephcoat asymmetry parameter 1. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -297,7 +308,8 @@ def asym_fcj_1(self, value: float) -> None: @property def asym_fcj_2(self) -> Parameter: - """Finger-Cox-Jephcoat asymmetry parameter 2. + """ + Finger-Cox-Jephcoat asymmetry parameter 2. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index 3231923c..1d0b5fab 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Time-of-flight (TOF) peak-profile component classes. +""" +Time-of-flight (TOF) peak-profile component classes. Defines classes that add Gaussian/Lorentz broadening, mixing, and Ikeda–Carpenter asymmetry parameters used by TOF peak shapes. This @@ -110,8 +111,8 @@ def __init__(self) -> None: @property def broad_gauss_sigma_0(self) -> Parameter: - """Gaussian broadening coefficient (instrumental resolution) - (µs²). + """ + Gaussian broadening coefficient (instrumental resolution) (µs²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -124,8 +125,8 @@ def broad_gauss_sigma_0(self, value: float) -> None: @property def broad_gauss_sigma_1(self) -> Parameter: - """Gaussian broadening coefficient (dependent on d-spacing) - (µs/Å). + """ + Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -138,7 +139,8 @@ def broad_gauss_sigma_1(self, value: float) -> None: @property def broad_gauss_sigma_2(self) -> Parameter: - """Gaussian broadening coefficient (instrument-dependent term) + """ + Gaussian broadening coefficient (instrument-dependent term) (µs²/Ų). Reading this property returns the underlying ``Parameter`` @@ -152,7 +154,8 @@ def broad_gauss_sigma_2(self, value: float) -> None: @property def broad_lorentz_gamma_0(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on microstrain + """ + Lorentzian broadening coefficient (dependent on microstrain effects) (µs). Reading this property returns the underlying ``Parameter`` @@ -166,7 +169,8 @@ def broad_lorentz_gamma_0(self, value: float) -> None: @property def broad_lorentz_gamma_1(self) -> Parameter: - """Lorentzian broadening coefficient (dependent on d-spacing) + """ + Lorentzian broadening coefficient (dependent on d-spacing) (µs/Å). Reading this property returns the underlying ``Parameter`` @@ -180,7 +184,8 @@ def broad_lorentz_gamma_1(self, value: float) -> None: @property def broad_lorentz_gamma_2(self) -> Parameter: - """Lorentzian broadening coefficient (instrument-dependent term) + """ + Lorentzian broadening coefficient (instrument-dependent term) (µs²/Ų). Reading this property returns the underlying ``Parameter`` @@ -194,7 +199,8 @@ def broad_lorentz_gamma_2(self, value: float) -> None: @property def broad_mix_beta_0(self) -> Parameter: - """Mixing parameter. Defines the ratio of Gaussian to Lorentzian + """ + Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). Reading this property returns the underlying ``Parameter`` @@ -208,7 +214,8 @@ def broad_mix_beta_0(self, value: float) -> None: @property def broad_mix_beta_1(self) -> Parameter: - """Mixing parameter. Defines the ratio of Gaussian to Lorentzian + """ + Mixing parameter. Defines the ratio of Gaussian to Lorentzian contributions in TOF profiles (deg). Reading this property returns the underlying ``Parameter`` @@ -250,7 +257,8 @@ def __init__(self) -> None: @property def asym_alpha_0(self) -> Parameter: - """Ikeda-Carpenter asymmetry parameter α₀. + """ + Ikeda-Carpenter asymmetry parameter α₀. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -263,7 +271,8 @@ def asym_alpha_0(self, value: float) -> None: @property def asym_alpha_1(self) -> Parameter: - """Ikeda-Carpenter asymmetry parameter α₁. + """ + Ikeda-Carpenter asymmetry parameter α₁. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index edcd2061..f772a74b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Total scattering / pair distribution function (PDF) peak-profile +""" +Total scattering / pair distribution function (PDF) peak-profile component classes. This module provides classes that add broadening and asymmetry @@ -90,7 +91,8 @@ def __init__(self) -> None: @property def damp_q(self) -> Parameter: - """Instrumental Q-resolution damping factor (affects high-r PDF + """ + Instrumental Q-resolution damping factor (affects high-r PDF peak amplitude) (Å⁻¹). Reading this property returns the underlying ``Parameter`` @@ -104,7 +106,8 @@ def damp_q(self, value: float) -> None: @property def broad_q(self) -> Parameter: - """Quadratic PDF peak broadening coefficient (thermal and model + """ + Quadratic PDF peak broadening coefficient (thermal and model uncertainty contribution) (Å⁻²). Reading this property returns the underlying ``Parameter`` @@ -118,7 +121,8 @@ def broad_q(self, value: float) -> None: @property def cutoff_q(self) -> Parameter: - """Q-value cutoff applied to model PDF for Fourier transform + """ + Q-value cutoff applied to model PDF for Fourier transform (controls real-space resolution) (Å⁻¹). Reading this property returns the underlying ``Parameter`` @@ -132,7 +136,8 @@ def cutoff_q(self, value: float) -> None: @property def sharp_delta_1(self) -> Parameter: - """PDF peak sharpening coefficient (1/r dependence) (Å). + """ + PDF peak sharpening coefficient (1/r dependence) (Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -145,7 +150,8 @@ def sharp_delta_1(self, value: float) -> None: @property def sharp_delta_2(self) -> Parameter: - """PDF peak sharpening coefficient (1/r² dependence) (Ų). + """ + PDF peak sharpening coefficient (1/r² dependence) (Ų). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -158,8 +164,9 @@ def sharp_delta_2(self, value: float) -> None: @property def damp_particle_diameter(self) -> Parameter: - """Particle diameter for spherical envelope damping correction - in PDF (Å). + """ + Particle diameter for spherical envelope damping correction in + PDF (Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index 2f707da1..5cdf3f6b 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -11,7 +11,8 @@ class Experiments(DatablockCollection): - """Collection of Experiment data blocks. + """ + Collection of Experiment data blocks. Provides convenience constructors for common creation patterns and helper methods for simple presentation of collection contents. diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 3679c45a..3eb5e447 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -34,7 +34,8 @@ class ExperimentBase(DatablockItem): - """Base class for all experiment datablock items with only core + """ + Base class for all experiment datablock items with only core attributes. """ @@ -70,8 +71,8 @@ def name(self, new: str) -> None: @property def type(self) -> object: # TODO: Consider another name - """Experiment type descriptor (sample form, probe, beam - mode). + """ + Experiment type descriptor (sample form, probe, beam mode). """ return self._type @@ -110,7 +111,8 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: @property def calculator(self) -> object: - """The active calculator instance for this experiment. + """ + The active calculator instance for this experiment. Auto-resolved on first access from the experiment's data category ``calculator_support`` and @@ -135,7 +137,8 @@ def calculator_type(self, tag: str) -> None: Parameters ---------- tag : str - Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``, ``'pdffit'``). + Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``, + ``'pdffit'``). """ from easydiffraction.analysis.calculators.factory import CalculatorFactory @@ -153,7 +156,8 @@ def calculator_type(self, tag: str) -> None: console.print(tag) def show_supported_calculator_types(self) -> None: - """Print a table of calculator backends supported by this + """ + Print a table of calculator backends supported by this experiment. """ from easydiffraction.analysis.calculators.factory import CalculatorFactory @@ -182,9 +186,9 @@ def show_current_calculator_type(self) -> None: console.print(self.calculator_type) def _resolve_calculator(self) -> None: - """Auto-resolve the default calculator from the data category's - ``calculator_support`` and - ``CalculatorFactory._default_rules``. + """ + Auto-resolve the default calculator from the data category's + ``calculator_support`` and ``CalculatorFactory._default_rules``. """ from easydiffraction.analysis.calculators.factory import CalculatorFactory @@ -198,7 +202,8 @@ def _resolve_calculator(self) -> None: self._calculator_type = tag def _supported_calculator_tags(self) -> list[str]: - """Return calculator tags supported by this experiment. + """ + Return calculator tags supported by this experiment. Intersects the data category's ``calculator_support`` with calculators whose engines are importable. @@ -250,7 +255,8 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: Parameters ---------- data_path : str - Path to data file with columns compatible with the beam mode. + Path to data file with columns compatible with the beam + mode. """ pass @@ -606,8 +612,8 @@ def excluded_regions_type(self, new_type: str) -> None: console.print(new_type) def show_supported_excluded_regions_types(self) -> None: - """Print a table of supported excluded-regions collection - types. + """ + Print a table of supported excluded-regions collection types. """ ExcludedRegionsFactory.show_supported() diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index e414670e..27d6602a 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -25,8 +25,9 @@ @ExperimentFactory.register class BraggPdExperiment(PdExperimentBase): - """Standard (Bragg) Powder Diffraction experiment class with - specific attributes. + """ + Standard (Bragg) Powder Diffraction experiment class with specific + attributes. """ type_info = TypeInfo( @@ -57,8 +58,8 @@ def __init__( self._background = BackgroundFactory.create(self._background_type) def _load_ascii_data_to_experiment(self, data_path: str) -> None: - """Load (x, y, sy) data from an ASCII file into the data - category. + """ + Load (x, y, sy) data from an ASCII file into the data category. The file format is space/column separated with 2 or 3 columns: ``x y [sy]``. If ``sy`` is missing, it is approximated as diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py index 1f0bc9cc..bf1ebece 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py @@ -23,8 +23,9 @@ @ExperimentFactory.register class CwlScExperiment(ScExperimentBase): - """Standard (Bragg) constant wavelength single srystal experiment - class with specific attributes. + """ + Standard (Bragg) constant wavelength single srystal experiment class + with specific attributes. """ type_info = TypeInfo( @@ -46,7 +47,8 @@ def __init__( super().__init__(name=name, type=type) def _load_ascii_data_to_experiment(self, data_path: str) -> None: - """Load measured data from an ASCII file into the data category. + """ + Load measured data from an ASCII file into the data category. The file format is space/column separated with 5 columns: ``h k l Iobs sIobs``. @@ -87,8 +89,9 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: @ExperimentFactory.register class TofScExperiment(ScExperimentBase): - """Standard (Bragg) time-of-flight single srystal experiment class - with specific attributes. + """ + Standard (Bragg) time-of-flight single srystal experiment class with + specific attributes. """ type_info = TypeInfo( @@ -110,7 +113,8 @@ def __init__( super().__init__(name=name, type=type) def _load_ascii_data_to_experiment(self, data_path: str) -> None: - """Load measured data from an ASCII file into the data category. + """ + Load measured data from an ASCII file into the data category. The file format is space/column separated with 6 columns: ``h k l Iobs sIobs wavelength``. diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index aaf16c89..99222161 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Factory for creating experiment instances from various inputs. +""" +Factory for creating experiment instances from various inputs. Provides individual class methods for each creation pathway: ``from_cif_path``, ``from_cif_str``, ``from_data_path``, and @@ -74,8 +75,8 @@ def _create_experiment_type( radiation_probe: str | None = None, scattering_type: str | None = None, ) -> ExperimentType: - """Construct an ExperimentType, using defaults for omitted - values. + """ + Construct an ExperimentType, using defaults for omitted values. """ # Note: validation of input values is done via Descriptor setter # methods diff --git a/src/easydiffraction/datablocks/experiment/item/total_pd.py b/src/easydiffraction/datablocks/experiment/item/total_pd.py index 1f912002..27acebcd 100644 --- a/src/easydiffraction/datablocks/experiment/item/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/total_pd.py @@ -42,7 +42,8 @@ def __init__( super().__init__(name=name, type=type) def _load_ascii_data_to_experiment(self, data_path: str) -> None: - """Loads x, y, sy values from an ASCII data file into the + """ + Loads x, y, sy values from an ASCII data file into the experiment. The file must be structured as: x y sy diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index b605b3cd..b94a3b8e 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Atom site category. +""" +Atom site category. Defines :class:`AtomSite` items and :class:`AtomSites` collection used in crystallographic structures. @@ -25,7 +26,8 @@ class AtomSite(CategoryItem): - """Single atom site with fractional coordinates and ADP. + """ + Single atom site with fractional coordinates and ADP. Attributes are represented by descriptors to support validation and CIF serialization. @@ -183,7 +185,8 @@ def _wyckoff_letter_default_value(self) -> str: @property def label(self) -> StringDescriptor: - """Unique identifier for the atom site. + """ + Unique identifier for the atom site. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -197,7 +200,8 @@ def label(self, value: str) -> None: @property def type_symbol(self) -> StringDescriptor: - """Chemical symbol of the atom at this site. + """ + Chemical symbol of the atom at this site. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -211,7 +215,8 @@ def type_symbol(self, value: str) -> None: @property def adp_type(self) -> StringDescriptor: - """Type of atomic displacement parameter (ADP) used (e.g., Biso, + """ + Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, Uani, Bani). Reading this property returns the underlying @@ -226,8 +231,9 @@ def adp_type(self, value: str) -> None: @property def wyckoff_letter(self) -> StringDescriptor: - """Wyckoff letter indicating the symmetry of the atom site - within the space group. + """ + Wyckoff letter indicating the symmetry of the atom site within + the space group. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -241,8 +247,8 @@ def wyckoff_letter(self, value: str) -> None: @property def fract_x(self) -> Parameter: - """Fractional x-coordinate of the atom site within the unit - cell. + """ + Fractional x-coordinate of the atom site within the unit cell. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -255,8 +261,8 @@ def fract_x(self, value: float) -> None: @property def fract_y(self) -> Parameter: - """Fractional y-coordinate of the atom site within the unit - cell. + """ + Fractional y-coordinate of the atom site within the unit cell. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -269,8 +275,8 @@ def fract_y(self, value: float) -> None: @property def fract_z(self) -> Parameter: - """Fractional z-coordinate of the atom site within the unit - cell. + """ + Fractional z-coordinate of the atom site within the unit cell. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -283,7 +289,8 @@ def fract_z(self, value: float) -> None: @property def occupancy(self) -> Parameter: - """Occupancy of the atom site, representing the fraction of the + """ + Occupancy of the atom site, representing the fraction of the site occupied by the atom type. Reading this property returns the underlying ``Parameter`` @@ -297,8 +304,9 @@ def occupancy(self, value: float) -> None: @property def b_iso(self) -> Parameter: - """Isotropic atomic displacement parameter (ADP) for the atom - site (Ų). + """ + Isotropic atomic displacement parameter (ADP) for the atom site + (Ų). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -328,7 +336,8 @@ def __init__(self) -> None: # ------------------------------------------------------------------ def _apply_atomic_coordinates_symmetry_constraints(self) -> None: - """Apply symmetry rules to fractional coordinates of every site. + """ + Apply symmetry rules to fractional coordinates of every site. Uses the parent structure's space-group symbol, IT coordinate system code and each atom's Wyckoff letter. Atoms without a diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 903b9b57..1e63bf46 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -16,7 +16,8 @@ @CellFactory.register class Cell(CategoryItem): - """Unit cell with lengths *a*, *b*, *c* and angles *alpha*, *beta*, + """ + Unit cell with lengths *a*, *b*, *c* and angles *alpha*, *beta*, *gamma*. All six lattice parameters are exposed as :class:`Parameter` @@ -100,7 +101,8 @@ def __init__(self) -> None: # ------------------------------------------------------------------ def _apply_cell_symmetry_constraints(self) -> None: - """Apply symmetry constraints to cell parameters in place. + """ + Apply symmetry constraints to cell parameters in place. Uses the parent structure's space-group symbol to determine which lattice parameters are dependent and sets them @@ -151,7 +153,8 @@ def _update( @property def length_a(self) -> Parameter: - """Length of the a axis of the unit cell (Å). + """ + Length of the a axis of the unit cell (Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -164,7 +167,8 @@ def length_a(self, value: float) -> None: @property def length_b(self) -> Parameter: - """Length of the b axis of the unit cell (Å). + """ + Length of the b axis of the unit cell (Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -177,7 +181,8 @@ def length_b(self, value: float) -> None: @property def length_c(self) -> Parameter: - """Length of the c axis of the unit cell (Å). + """ + Length of the c axis of the unit cell (Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -190,7 +195,8 @@ def length_c(self, value: float) -> None: @property def angle_alpha(self) -> Parameter: - """Angle between edges b and c (deg). + """ + Angle between edges b and c (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -203,7 +209,8 @@ def angle_alpha(self, value: float) -> None: @property def angle_beta(self) -> Parameter: - """Angle between edges a and c (deg). + """ + Angle between edges a and c (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -216,7 +223,8 @@ def angle_beta(self, value: float) -> None: @property def angle_gamma(self) -> Parameter: - """Angle between edges a and b (deg). + """ + Angle between edges a and b (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 47e29aa2..623fbe39 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -21,7 +21,8 @@ @SpaceGroupFactory.register class SpaceGroup(CategoryItem): - """Space group with Hermann–Mauguin symbol and IT coordinate system + """ + Space group with Hermann–Mauguin symbol and IT coordinate system code. Holds the space-group symbol (``name_h_m``) and the International @@ -84,7 +85,8 @@ def __init__(self) -> None: # ------------------------------------------------------------------ def _reset_it_coordinate_system_code(self) -> None: - """Reset the IT coordinate system code to the default for the + """ + Reset the IT coordinate system code to the default for the current group. """ self._it_coordinate_system_code.value = self._it_coordinate_system_code_default_value @@ -135,7 +137,8 @@ def _it_coordinate_system_code_default_value(self) -> str: @property def name_h_m(self) -> StringDescriptor: - """Hermann-Mauguin symbol of the space group. + """ + Hermann-Mauguin symbol of the space group. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -150,7 +153,8 @@ def name_h_m(self, value: str) -> None: @property def it_coordinate_system_code(self) -> StringDescriptor: - """A qualifier identifying which setting in IT is used. + """ + A qualifier identifying which setting in IT is used. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the diff --git a/src/easydiffraction/datablocks/structure/collection.py b/src/easydiffraction/datablocks/structure/collection.py index 79e4bd0f..801b416a 100644 --- a/src/easydiffraction/datablocks/structure/collection.py +++ b/src/easydiffraction/datablocks/structure/collection.py @@ -11,7 +11,8 @@ class Structures(DatablockCollection): - """Ordered collection of :class:`Structure` instances. + """ + Ordered collection of :class:`Structure` instances. Provides convenience ``add_from_*`` methods that mirror the :class:`StructureFactory` classmethods plus a bare :meth:`add` for diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index e78e8086..743a3589 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -245,8 +245,8 @@ def show_current_atom_sites_type(self) -> None: # ------------------------------------------------------------------ def show(self) -> None: - """Display an ASCII projection of the structure on a 2D - plane. + """ + Display an ASCII projection of the structure on a 2D plane. """ console.paragraph(f"Structure 🧩 '{self.name}'") console.print('Not implemented yet.') diff --git a/src/easydiffraction/datablocks/structure/item/factory.py b/src/easydiffraction/datablocks/structure/item/factory.py index 2078e68b..567d26dc 100644 --- a/src/easydiffraction/datablocks/structure/item/factory.py +++ b/src/easydiffraction/datablocks/structure/item/factory.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Factory for creating structure instances from various inputs. +""" +Factory for creating structure instances from various inputs. Provides individual class methods for each creation pathway: ``from_scratch``, ``from_cif_path``, or ``from_cif_str``. diff --git a/src/easydiffraction/display/__init__.py b/src/easydiffraction/display/__init__.py index 4f5877fb..25bccda4 100644 --- a/src/easydiffraction/display/__init__.py +++ b/src/easydiffraction/display/__init__.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Display subsystem for tables and plots. +""" +Display subsystem for tables and plots. This package contains user-facing facades and backend implementations to render tabular data and plots in different environments. diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index d33a553a..52c48214 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -17,7 +17,8 @@ class RendererBase(SingletonBase, ABC): - """Base class for display components with pluggable engines. + """ + Base class for display components with pluggable engines. Subclasses provide a factory and a default engine. This class manages the active backend instance and exposes helpers to inspect @@ -123,8 +124,8 @@ def supported_engines(cls) -> List[str]: @classmethod def descriptions(cls) -> List[Tuple[str, str]]: - """Return pairs of engine name and human-friendly - description. + """ + Return pairs of engine name and human-friendly description. """ items = cls._registry().items() return [(name, config.get('description')) for name, config in items] @@ -132,7 +133,8 @@ def descriptions(cls) -> List[Tuple[str, str]]: @classmethod @abstractmethod def _registry(cls) -> dict: - """Return engine registry. Implementations must provide this. + """ + Return engine registry. Implementations must provide this. The returned mapping should have keys as engine names and values as a config dict with 'description' and 'class'. Lazy imports diff --git a/src/easydiffraction/display/plotters/__init__.py b/src/easydiffraction/display/plotters/__init__.py index f365ad2c..09931ae8 100644 --- a/src/easydiffraction/display/plotters/__init__.py +++ b/src/easydiffraction/display/plotters/__init__.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Plotting backends. +""" +Plotting backends. This subpackage implements plotting engines used by the high-level plotting facade: diff --git a/src/easydiffraction/display/plotters/ascii.py b/src/easydiffraction/display/plotters/ascii.py index f3053535..8735a8a0 100644 --- a/src/easydiffraction/display/plotters/ascii.py +++ b/src/easydiffraction/display/plotters/ascii.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""ASCII plotting backend. +""" +ASCII plotting backend. Renders compact line charts in the terminal using ``asciichartpy``. This backend is well suited for quick feedback in CLI environments and keeps @@ -61,9 +62,9 @@ def plot_powder( """ Render a line plot for powder diffraction data. - Suitable for powder diffraction data where intensity is plotted against - an x-axis variable (2θ, TOF, d-spacing). Uses ASCII characters for - terminal display. + Suitable for powder diffraction data where intensity is plotted + against an x-axis variable (2θ, TOF, d-spacing). Uses ASCII + characters for terminal display. Parameters ---------- @@ -114,8 +115,8 @@ def plot_single_crystal( """ Render a scatter plot for single crystal diffraction data. - Creates an ASCII scatter plot showing measured vs calculated values with - a diagonal reference line. + Creates an ASCII scatter plot showing measured vs calculated + values with a diagonal reference line. Parameters ---------- diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index d6668985..d7ca594c 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -18,7 +18,8 @@ class XAxisType(str, Enum): - """X-axis types for diffraction plots. + """ + X-axis types for diffraction plots. Values match attribute names in data models for direct use with ``getattr(pattern, x_axis)``. @@ -151,7 +152,8 @@ class XAxisType(str, Enum): class PlotterBase(ABC): - """Abstract base for plotting backends. + """ + Abstract base for plotting backends. Implementations accept x values, multiple y-series, optional labels and render a plot to the chosen medium. @@ -175,8 +177,8 @@ def plot_powder( """ Render a line plot for powder diffraction data. - Suitable for powder diffraction data where intensity is plotted against - an x-axis variable (2θ, TOF, d-spacing). + Suitable for powder diffraction data where intensity is plotted + against an x-axis variable (2θ, TOF, d-spacing). Parameters ---------- @@ -208,8 +210,8 @@ def plot_single_crystal( """ Render a scatter plot for single crystal diffraction data. - Suitable for single crystal diffraction data where measured values are - plotted against calculated values with error bars. + Suitable for single crystal diffraction data where measured + values are plotted against calculated values with error bars. Parameters ---------- diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index 07ff1ec2..4df79cf0 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Plotly plotting backend. +""" +Plotly plotting backend. Provides an interactive plotting implementation using Plotly. In notebooks, figures are displayed inline; in other environments a browser @@ -95,8 +96,8 @@ def _get_single_crystal_trace( Returns ------- object - A configured :class:`plotly.graph_objects.Scatter` trace with - markers and error bars. + A configured :class:`plotly.graph_objects.Scatter` trace + with markers and error bars. """ trace = go.Scatter( x=x_calc, @@ -283,8 +284,8 @@ def plot_powder( """ Render a line plot for powder diffraction data. - Suitable for powder diffraction data where intensity is plotted against - an x-axis variable (2θ, TOF, d-spacing). + Suitable for powder diffraction data where intensity is plotted + against an x-axis variable (2θ, TOF, d-spacing). Parameters ---------- @@ -330,9 +331,9 @@ def plot_single_crystal( """ Render a scatter plot for single crystal diffraction data. - Suitable for single crystal diffraction data where measured values are - plotted against calculated values with error bars and a diagonal - reference line. + Suitable for single crystal diffraction data where measured + values are plotted against calculated values with error bars and + a diagonal reference line. Parameters ---------- diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 432bc88e..91584150 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Plotting facade for measured and calculated patterns. +""" +Plotting facade for measured and calculated patterns. Uses the common :class:`RendererBase` so plotters and tablers share a consistent configuration surface and engine handling. @@ -158,7 +159,8 @@ def _get_axes_labels( scattering_type: object, x_axis: object, ) -> list: - """Look up axis labels for the given experiment / x-axis + """ + Look up axis labels for the given experiment / x-axis combination. """ return DEFAULT_AXES_LABELS[(sample_form, scattering_type, x_axis)] @@ -185,7 +187,8 @@ def _prepare_powder_data( expt_name : str Experiment name for error messages. expt_type : object - Experiment type with sample_form, scattering, and beam enums. + Experiment type with sample_form, scattering, and beam + enums. x_min : object Optional minimum x-axis limit. x_max : object @@ -266,8 +269,8 @@ def _resolve_x_axis(self, expt_type: object, x: object) -> tuple: Parameters ---------- expt_type : object - Experiment type with sample_form, scattering_type, and beam_mode - enums. + Experiment type with sample_form, scattering_type, and + beam_mode enums. x : object Explicit x-axis type or ``None`` to auto-detect. @@ -382,8 +385,8 @@ def plot_meas( Parameters ---------- pattern : object - Object with x-axis arrays (``two_theta``, ``time_of_flight``, - ``d_spacing``) and ``meas`` array. + Object with x-axis arrays (``two_theta``, + ``time_of_flight``, ``d_spacing``) and ``meas`` array. expt_name : str Experiment name for the title. expt_type : object @@ -432,8 +435,8 @@ def plot_calc( Parameters ---------- pattern : object - Object with x-axis arrays (``two_theta``, ``time_of_flight``, - ``d_spacing``) and ``calc`` array. + Object with x-axis arrays (``two_theta``, + ``time_of_flight``, ``d_spacing``) and ``calc`` array. expt_name : str Experiment name for the title. expt_type : object @@ -485,8 +488,9 @@ def plot_meas_vs_calc( For powder diffraction: - x='two_theta', 'time_of_flight', or 'd_spacing' - Auto-detected from beam mode if not specified - For single crystal diffraction: - x='intensity_calc' (default): scatter - plot - x='d_spacing' or 'sin_theta_over_lambda': line plot + For single crystal diffraction: - x='intensity_calc' (default): + scatter plot - x='d_spacing' or 'sin_theta_over_lambda': line + plot Parameters ---------- @@ -495,7 +499,8 @@ def plot_meas_vs_calc( expt_name : str Experiment name for the title. expt_type : object - Experiment type with sample_form, scattering, and beam enums. + Experiment type with sample_form, scattering, and beam + enums. x_min : object, default=None Optional minimum x-axis limit. x_max : object, default=None diff --git a/src/easydiffraction/display/tablers/__init__.py b/src/easydiffraction/display/tablers/__init__.py index 7cbc22c0..6471fbff 100644 --- a/src/easydiffraction/display/tablers/__init__.py +++ b/src/easydiffraction/display/tablers/__init__.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Tabular rendering backends. +""" +Tabular rendering backends. This subpackage provides concrete implementations for rendering tables in different environments: diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index 74c66207..869c5a17 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Low-level backends for rendering tables. +""" +Low-level backends for rendering tables. This module defines the abstract base for tabular renderers and small helpers for consistent styling across terminal and notebook outputs. @@ -18,7 +19,8 @@ class TableBackendBase(ABC): - """Abstract base class for concrete table backends. + """ + Abstract base class for concrete table backends. Subclasses implement the ``render`` method which receives an index- aware pandas DataFrame and the alignment for each column header. @@ -50,7 +52,8 @@ def _format_value(self, value: object) -> object: return self._float_fmt(value) if isinstance(value, float) else str(value) def _is_dark_theme(self) -> bool: - """Return True when a dark theme is detected in Jupyter. + """ + Return True when a dark theme is detected in Jupyter. If not running inside Jupyter, return a sane default (True). """ @@ -112,7 +115,8 @@ def render( df : object Index-aware DataFrame with data to render. display_handle : object | None, default=None - Optional environment-specific handle to enable in-place updates. + Optional environment-specific handle to enable in-place + updates. Returns ------- diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index 7cbefd1d..4efef148 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -140,9 +140,9 @@ def _update_display(self, styler: object, display_handle: object) -> None: """ Single, consistent update path for Jupyter. - If a handle with ``update()`` is provided and it's a DisplayHandle, - update the output area in-place using HTML. Otherwise, display once via - IPython ``display()``. + If a handle with ``update()`` is provided and it's a + DisplayHandle, update the output area in-place using HTML. + Otherwise, display once via IPython ``display()``. Parameters ---------- @@ -185,8 +185,8 @@ def render( df : object DataFrame whose index is displayed as the first column. display_handle : object | None, default=None - Optional IPython DisplayHandle to update an existing output area - in place when running in Jupyter. + Optional IPython DisplayHandle to update an existing output + area in place when running in Jupyter. Returns ------- diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index 6b1dca71..fba2a400 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -111,9 +111,10 @@ def _update_display(self, table: Table, display_handle: object) -> None: Single, consistent update path for Jupyter and terminal. - With a handle that has ``update()``: * If it's an IPython - DisplayHandle, export to HTML and update. * Otherwise, treat it as a - terminal/live-like handle and update with the Rich renderable. - Without - a handle, print once to the shared console. + DisplayHandle, export to HTML and update. * Otherwise, treat it + as a terminal/live-like handle and update with the Rich + renderable. - Without a handle, print once to the shared + console. Parameters ---------- diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index b42bf109..70d1a994 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -23,7 +23,8 @@ class TableEngineEnum(str, Enum): @classmethod def default(cls) -> 'TableEngineEnum': - """Select default engine based on environment. + """ + Select default engine based on environment. Returns Pandas when running in Jupyter, otherwise Rich. """ @@ -71,12 +72,12 @@ def render(self, df: object, display_handle: object | None = None) -> object: Parameters ---------- df : object - DataFrame with a two-level column index where the second level - provides per-column alignment. + DataFrame with a two-level column index where the second + level provides per-column alignment. display_handle : object | None, default=None - Optional environment-specific handle used to update an existing - output area in-place (e.g., an IPython DisplayHandle or a - terminal live handle). + Optional environment-specific handle used to update an + existing output area in-place (e.g., an IPython + DisplayHandle or a terminal live handle). Returns ------- @@ -103,8 +104,8 @@ class TableRendererFactory(RendererFactoryBase): @classmethod def _registry(cls) -> dict: - """Build registry, adapting available engines to the - environment. + """ + Build registry, adapting available engines to the environment. - In Jupyter: expose both 'rich' and 'pandas'. - In terminal: expose only 'rich' (pandas is notebook-only). diff --git a/src/easydiffraction/display/utils.py b/src/easydiffraction/display/utils.py index a369c819..087f6378 100644 --- a/src/easydiffraction/display/utils.py +++ b/src/easydiffraction/display/utils.py @@ -18,8 +18,8 @@ class JupyterScrollManager: - """Ensures that Jupyter output cells are not scrollable (applied - once). + """ + Ensures that Jupyter output cells are not scrollable (applied once). """ _applied: ClassVar[bool] = False diff --git a/src/easydiffraction/io/cif/handler.py b/src/easydiffraction/io/cif/handler.py index 4d56ee09..0cd3b62b 100644 --- a/src/easydiffraction/io/cif/handler.py +++ b/src/easydiffraction/io/cif/handler.py @@ -6,7 +6,8 @@ class CifHandler: - """Canonical CIF handler used by descriptors/parameters. + """ + Canonical CIF handler used by descriptors/parameters. Holds CIF tags (names) and attaches to an owning descriptor so it can derive a stable uid if needed. diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index cc38ffc9..6a677d7f 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -23,7 +23,8 @@ def format_value(value: object) -> str: - """Format a single CIF value, quoting strings with whitespace, and + """ + Format a single CIF value, quoting strings with whitespace, and format floats with global precision. .. note:: The precision must be high enough so that the @@ -61,7 +62,8 @@ def format_value(value: object) -> str: def param_to_cif(param: object) -> str: - """Render a single descriptor/parameter to a CIF line. + """ + Render a single descriptor/parameter to a CIF line. Expects ``param`` to expose ``_cif_handler.names`` and ``value``. """ @@ -71,7 +73,8 @@ def param_to_cif(param: object) -> str: def category_item_to_cif(item: object) -> str: - """Render a CategoryItem-like object to CIF text. + """ + Render a CategoryItem-like object to CIF text. Expects ``item.parameters`` iterable of params with ``_cif_handler.names`` and ``value``. @@ -86,7 +89,8 @@ def category_collection_to_cif( collection: object, max_display: Optional[int] = 20, ) -> str: - """Render a CategoryCollection-like object to CIF text. + """ + Render a CategoryCollection-like object to CIF text. Uses first item to build loop header, then emits rows for each item. """ @@ -125,7 +129,8 @@ def category_collection_to_cif( def datablock_item_to_cif(datablock: object) -> str: - """Render a DatablockItem-like object to CIF text. + """ + Render a DatablockItem-like object to CIF text. Emits a data_ header and then concatenates category CIF sections. """ @@ -155,8 +160,8 @@ def datablock_collection_to_cif(collection: object) -> str: def project_info_to_cif(info: object) -> str: - """Render ProjectInfo to CIF text (id, title, description, - dates). + """ + Render ProjectInfo to CIF text (id, title, description, dates). """ name = f'{info.name}' diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index c015612a..bb6b991d 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -22,7 +22,8 @@ class Project(GuardedBase): - """Central API for managing a diffraction data analysis project. + """ + Central API for managing a diffraction data analysis project. Provides access to structures, experiments, analysis, and summary. """ @@ -74,8 +75,8 @@ def info(self) -> ProjectInfo: @property def name(self) -> str: - """Convenience property to access the project's name - directly. + """ + Convenience property to access the project's name directly. """ return self._info.name @@ -139,7 +140,8 @@ def as_cif(self) -> str: # ------------------------------------------ def load(self, dir_path: str) -> None: - """Load a project from a given directory. + """ + Load a project from a given directory. Loads project info, structures, experiments, etc. """ diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py index d5e8f826..3e1859c0 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_info.py @@ -12,8 +12,9 @@ class ProjectInfo(GuardedBase): - """Stores metadata about the project, such as name, title, - description, and file paths. + """ + Stores metadata about the project, such as name, title, description, + and file paths. """ def __init__( diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index 971835d3..45316d98 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -9,7 +9,8 @@ class Summary: - """Generates reports and exports results from the project. + """ + Generates reports and exports results from the project. This class collects and presents all relevant information about the fitted model, experiments, and analysis results. @@ -55,7 +56,8 @@ def show_project_info(self) -> None: print('\n'.join(desc_lines)) def show_crystallographic_data(self) -> None: - """Print crystallographic data including phase datablocks, space + """ + Print crystallographic data including phase datablocks, space groups, cell parameters, and atom sites. """ console.section('Crystallographic data') @@ -117,8 +119,9 @@ def show_crystallographic_data(self) -> None: ) def show_experimental_data(self) -> None: - """Print experimental data including experiment datablocks, - types, instrument settings, and peak profile information. + """ + Print experimental data including experiment datablocks, types, + instrument settings, and peak profile information. """ console.section('Experiments') @@ -177,7 +180,8 @@ def show_experimental_data(self) -> None: ) def show_fitting_details(self) -> None: - """Print fitting details including calculation and minimization + """ + Print fitting details including calculation and minimization engines, and fit quality metrics. """ console.section('Fitting') @@ -209,8 +213,8 @@ def show_fitting_details(self) -> None: # ------------------------------------------ def as_cif(self) -> str: - """Export the final fitted data and analysis results as CIF - format. + """ + Export the final fitted data and analysis results as CIF format. """ from easydiffraction.io.cif.serialize import summary_to_cif diff --git a/src/easydiffraction/utils/_vendored/__init__.py b/src/easydiffraction/utils/_vendored/__init__.py index 43a5d921..c124e1af 100644 --- a/src/easydiffraction/utils/_vendored/__init__.py +++ b/src/easydiffraction/utils/_vendored/__init__.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Vendored third-party modules. +""" +Vendored third-party modules. This package contains third-party code that has been vendored into the project to avoid external dependencies not available on conda-forge. @@ -11,13 +12,11 @@ To update, replace these files from upstream: - https://github.com/OpenMined/jupyter-dark-detect/blob/main/jupyter_dark_detect/__init__.py - - +- https://github.com/OpenMined/jupyter-dark-detect/blob/main/jupyter_dark_detect/detector.py Then run 'pixi run fix' to format and check for issues. -Modules: -theme_detect: -Custom wrapper around jupyter_dark_detect with optimized -detection order for EasyDiffraction's use case. +Modules: theme_detect: Custom wrapper around jupyter_dark_detect with +optimized detection order for EasyDiffraction's use case. """ diff --git a/src/easydiffraction/utils/_vendored/theme_detect.py b/src/easydiffraction/utils/_vendored/theme_detect.py index 956c09b3..9f75ee72 100644 --- a/src/easydiffraction/utils/_vendored/theme_detect.py +++ b/src/easydiffraction/utils/_vendored/theme_detect.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Jupyter theme detection with custom detection order. +""" +Jupyter theme detection with custom detection order. This module wraps the vendored jupyter_dark_detect package and provides a custom detection order optimized for EasyDiffraction's use case. @@ -12,15 +13,12 @@ 3. JavaScript DOM inspection (for browser-based environments) 4. System preferences (macOS, Windows) - fallback only -Note: - The detection order differs from upstream jupyter_dark_detect. - We prioritize JavaScript DOM inspection over system preferences - because the Jupyter theme may differ from the system theme. +Note: The detection order differs from upstream jupyter_dark_detect. We +prioritize JavaScript DOM inspection over system preferences because the +Jupyter theme may differ from the system theme. -Example: - >>> from easydiffraction.utils._vendored.theme_detect import is_dark - >>> if is_dark(): - ... print('Dark mode detected') +Example: >>> from easydiffraction.utils._vendored.theme_detect import +is_dark >>> if is_dark(): ... print('Dark mode detected') """ from __future__ import annotations @@ -37,7 +35,8 @@ def is_dark() -> bool: - """Check if the Jupyter environment is running in dark mode. + """ + Check if the Jupyter environment is running in dark mode. This function uses a custom detection order that prioritizes Jupyter-specific detection over system preferences. @@ -79,13 +78,14 @@ def is_dark() -> bool: def get_detection_result() -> dict[str, Optional[bool]]: - """Get results from all detection methods for debugging. + """ + Get results from all detection methods for debugging. Returns ------- dict[str, Optional[bool]] - Dictionary with detection method names as keys and - their results (True/False/None) as values. + Dictionary with detection method names as keys and their results + (True/False/None) as values. """ return { 'jupyterlab_settings': _check_jupyterlab_settings(), diff --git a/src/easydiffraction/utils/environment.py b/src/easydiffraction/utils/environment.py index b3308915..a14aa1fd 100644 --- a/src/easydiffraction/utils/environment.py +++ b/src/easydiffraction/utils/environment.py @@ -102,7 +102,8 @@ def in_github_ci() -> bool: def is_ipython_display_handle(obj: object) -> bool: - """Return True if ``obj`` is an IPython DisplayHandle instance. + """ + Return True if ``obj`` is an IPython DisplayHandle instance. Tries to import ``IPython.display.DisplayHandle`` and uses ``isinstance`` when available. Falls back to a conservative module @@ -126,7 +127,8 @@ def is_ipython_display_handle(obj: object) -> bool: def can_update_ipython_display() -> bool: - """Return True if IPython HTML display utilities are available. + """ + Return True if IPython HTML display utilities are available. This indicates we can safely construct ``IPython.display.HTML`` and update a display handle. @@ -140,7 +142,8 @@ def can_update_ipython_display() -> bool: def can_use_ipython_display(handle: object) -> bool: - """Return True if we can update the given IPython DisplayHandle. + """ + Return True if we can update the given IPython DisplayHandle. Combines type checking of the handle with availability of IPython HTML utilities. diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index 817fa7bf..d40d69ae 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Project-wide logging utilities built on top of Rich. +""" +Project-wide logging utilities built on top of Rich. Provides a shared Rich console, a compact/verbose logger with consistent formatting, Jupyter traceback handling, and a small printing façade @@ -43,7 +44,8 @@ class IconifiedRichHandler(RichHandler): - """RichHandler that uses icons for log levels in compact mode, Rich + """ + RichHandler that uses icons for log levels in compact mode, Rich default in verbose mode. """ @@ -100,8 +102,8 @@ def _detect_width() -> int: Returns ------- int - The detected terminal width, clamped at ``_MIN_CONSOLE_WIDTH`` - to avoid cramped layouts. + The detected terminal width, clamped at + ``_MIN_CONSOLE_WIDTH`` to avoid cramped layouts. """ min_width = ConsoleManager._MIN_CONSOLE_WIDTH try: @@ -191,9 +193,9 @@ def configure( ---------- logger : logging.Logger Logger instance to configure. - mode : Logger.Mode + mode : 'Logger.Mode' Output mode (compact or verbose). - level : Logger.Level + level : 'Logger.Level' Minimum log level to emit. rich_tracebacks : bool Whether to enable Rich tracebacks. @@ -282,7 +284,8 @@ def restore_original_hook() -> None: @staticmethod def _suppress_traceback(logger: object) -> object: """ - Build a Jupyter custom exception callback that logs only the message. + Build a Jupyter custom exception callback that logs only the + message. Parameters ---------- @@ -292,8 +295,9 @@ def _suppress_traceback(logger: object) -> object: Returns ------- object - A callable suitable for IPython's set_custom_exc that suppresses - full tracebacks and logs only the exception message. + A callable suitable for IPython's set_custom_exc that + suppresses full tracebacks and logs only the exception + message. """ def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: @@ -311,8 +315,8 @@ def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: @staticmethod def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: """ - Install a Jupyter/IPython custom exception handler that suppresses - tracebacks. + Install a Jupyter/IPython custom exception handler that + suppresses tracebacks. Parameters ---------- @@ -338,7 +342,8 @@ def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: class Logger: - """Centralized logging with Rich formatting and two modes. + """ + Centralized logging with Rich formatting and two modes. Environment variables: ED_LOG_MODE: set default mode ('verbose' or 'compact') ED_LOG_LEVEL: set default level ('DEBUG', 'INFO', etc.) @@ -395,7 +400,8 @@ def configure( reaction: Reaction | None = None, rich_tracebacks: bool | None = None, ) -> None: - """Configure logger. + """ + Configure logger. mode: default COMPACT in Jupyter else VERBOSE level: minimum log level rich_tracebacks: override automatic choice @@ -443,8 +449,8 @@ def configure( @classmethod def _install_jupyter_traceback_suppressor(cls) -> None: - """Install traceback suppressor in Jupyter, safely and lint- - clean. + """ + Install traceback suppressor in Jupyter, safely and lint- clean. """ ExceptionHookManager.install_jupyter_traceback_suppressor(cls._logger) @@ -528,15 +534,17 @@ def critical(cls, *messages: str, exc_type: type[BaseException] = RuntimeError) class ConsolePrinter: - """Printer utility that prints objects to the shared console with - left padding. + """ + Printer utility that prints objects to the shared console with left + padding. """ _console = ConsoleManager.get() @classmethod def print(cls, *objects: object, **kwargs: object) -> None: - """Print objects to the console with left padding. + """ + Print objects to the console with left padding. - Renderables (Rich types like Text, Table, Panel, etc.) are kept as-is. - Non-renderables (ints, floats, Path, etc.) are @@ -586,8 +594,9 @@ def section(cls, title: str) -> None: @classmethod def chapter(cls, title: str) -> None: - """Formats a chapter header with bold magenta text, uppercase, - and padding. + """ + Formats a chapter header with bold magenta text, uppercase, and + padding. """ width = ConsoleManager._detect_width() symbol = '—' diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index b83cda6e..e3cbaae8 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -49,8 +49,8 @@ def _validate_url(url: str) -> None: def _filename_for_id_from_url(data_id: int | str, url: str) -> str: - """Return local filename like 'ed-12.xye' using extension from the - URL. + """ + Return local filename like 'ed-12.xye' using extension from the URL. """ suffix = pathlib.Path(urlparse(url).path).suffix # includes leading dot ('.cif', '.xye', ...) # If URL has no suffix, fall back to no extension. @@ -58,7 +58,8 @@ def _filename_for_id_from_url(data_id: int | str, url: str) -> str: def _normalize_known_hash(value: str | None) -> str | None: - """Return pooch-compatible known_hash or None. + """ + Return pooch-compatible known_hash or None. Treat placeholder values like 'sha256:...' as unset. """ @@ -71,8 +72,8 @@ def _normalize_known_hash(value: str | None) -> str | None: def _fetch_data_index() -> dict: - """Fetch & cache the diffraction data index.json and return it as - dict. + """ + Fetch & cache the diffraction data index.json and return it as dict. """ index_url = 'https://raw.githubusercontent.com/easyscience/data/refs/heads/master/diffraction/index.json' _validate_url(index_url) @@ -98,8 +99,8 @@ def _fetch_data_index() -> dict: @functools.lru_cache(maxsize=1) def _fetch_tutorials_index() -> dict: """ - Fetch & cache the tutorials index.json from gh-pages and return it as - dict. + Fetch & cache the tutorials index.json from gh-pages and return it + as dict. The index is fetched from: https://easyscience.github.io/diffraction-lib/{version}/tutorials/index.json @@ -259,11 +260,12 @@ def stripped_package_version(package_name: str) -> str | None: def _is_dev_version(package_name: str) -> bool: """ - Check if the installed package version is a development/local version. + Check if the installed package version is a development/local + version. A version is considered "dev" if: - The raw version contains '+dev', - '+dirty', or '+devdirty' (local suffixes from versioningit) - The public - version is '999.0.0' (versioningit default-tag fallback) + '+dirty', or '+devdirty' (local suffixes from versioningit) - The + public version is '999.0.0' (versioningit default-tag fallback) Parameters ---------- @@ -312,7 +314,8 @@ def _get_version_for_url(package_name: str = 'easydiffraction') -> str: def _safe_urlopen(request_or_url: object) -> object: # type: ignore[no-untyped-def] - """Wrapper for urlopen with prior validation. + """ + Wrapper for urlopen with prior validation. Centralises lint suppression for validated HTTPS requests. """ @@ -347,7 +350,8 @@ def _resolve_tutorial_url(url_template: str) -> str: def list_tutorials() -> None: - """Display a table of available tutorial notebooks. + """ + Display a table of available tutorial notebooks. Shows tutorial ID, filename, title, and description for all tutorials available for the current version of easydiffraction. @@ -553,14 +557,16 @@ def tof_to_d( quad_eps: float = 1e-20, ) -> np.ndarray: """ - Convert time-of-flight (TOF) to d-spacing using a quadratic calibration. + Convert time-of-flight (TOF) to d-spacing using a quadratic + calibration. Model: TOF = offset + linear * d + quad * d² The function: - Uses a linear fallback when the quadratic term is - effectively zero. - Solves the quadratic for d and selects the smallest - positive, finite root. - Returns NaN where no valid solution exists. - - Expects ``tof`` as a NumPy array; output matches its shape. + effectively zero. - Solves the quadratic for d and selects the + smallest positive, finite root. - Returns NaN where no valid + solution exists. - Expects ``tof`` as a NumPy array; output matches + its shape. Parameters ---------- @@ -721,30 +727,30 @@ def get_value_from_xye_header(file_path: str, key: str) -> float: def str_to_ufloat(s: Optional[str], default: Optional[float] = None) -> UFloat: """ - Parse a CIF-style numeric string into a `ufloat` with an optional + Parse a CIF-style numeric string into a ``ufloat`` with an optional uncertainty. - Examples of supported input: - "3.566" → ufloat(3.566, nan) - "3.566(2)" - → ufloat(3.566, 0.002) - None → ufloat(default, nan) + Examples of supported input: - "3.566" → ufloat(3.566, nan) - + "3.566(2)" → ufloat(3.566, 0.002) - None → ufloat(default, nan) - Behavior: - If the input string contains a value with parentheses (e.g. - "3.566(2)"), the number in parentheses is interpreted as an estimated - standard deviation (esd) in the last digit(s). - If the input string has - no parentheses, an uncertainty of NaN is assigned to indicate "no esd - provided". - If parsing fails, the function falls back to the given - `default` value with uncertainty NaN. + Behavior: - If the input string contains a value with parentheses + (e.g. "3.566(2)"), the number in parentheses is interpreted as an + estimated standard deviation (esd) in the last digit(s). - If the + input string has no parentheses, an uncertainty of NaN is assigned + to indicate "no esd provided". - If parsing fails, the function + falls back to the given ``default`` value with uncertainty NaN. Parameters ---------- s : Optional[str] Numeric string in CIF format (e.g. "3.566", "3.566(2)") or None. default : Optional[float], default=None - Default value to use if `s` is None or parsing fails. + Default value to use if ``s`` is None or parsing fails. Returns ------- UFloat - An `uncertainties.UFloat` object with the parsed value and + An ``uncertainties.UFloat`` object with the parsed value and uncertainty. The uncertainty will be NaN if not specified or parsing failed. """ From 1787c16a0cf650e50da689f7527076c9921370e9 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 16:05:47 +0100 Subject: [PATCH 31/48] More rules --- pixi.lock | 4 ++-- pyproject.toml | 6 ++++-- src/easydiffraction/analysis/analysis.py | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pixi.lock b/pixi.lock index 4ae22895..59f2cf73 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+dev33 - sha256: 29514fa5b8b013c07c7dbb679a502c339da4c2e394c411e598406f1adddeedfa + version: 0.10.2+devdirty34 + sha256: b95efc28a0e9f68c9b10a008149288064f945ddfea0080ba6d16a487d37f1d28 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index 9029cef0..649b08f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -280,8 +280,6 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc # Temporary: 'DTZ005', - 'W505', - ] # Ignore specific rules in certain files or directories @@ -309,6 +307,10 @@ ignore = [ 'N801', 'N805', 'N812', + 'PLC', + 'PLE', + 'PLR', + 'PLW', 'SIM117', 'W505', ] diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index de8844b2..8799e2a0 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -35,9 +35,9 @@ class Analysis: computations across the project's structures and experiments. Typical usage: - - - Display or filter parameters to fit. - Select a calculator/minimizer - implementation. - Calculate patterns and run single or joint fits. + - Display or filter parameters to fit. + - Select a calculator/minimizer implementation. + - Calculate patterns and run single or joint fits. """ def __init__(self, project: object) -> None: From 3c4c28c2878c0238b1409ad8b9baff9f1d71b56d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 16:14:18 +0100 Subject: [PATCH 32/48] Bump dependencies --- pixi.lock | 519 ++++++++++++++++++++++++++---------------------------- pixi.toml | 2 +- 2 files changed, 253 insertions(+), 268 deletions(-) diff --git a/pixi.lock b/pixi.lock index 59f2cf73..014de013 100644 --- a/pixi.lock +++ b/pixi.lock @@ -17,11 +17,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda @@ -33,14 +33,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.8.2-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.12-hc97d973_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -102,7 +102,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -285,7 +285,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -322,11 +322,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-6_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-6_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.4-h991f03e_0.conda @@ -337,13 +337,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.2-h11316ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.32-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.52.0-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libuv-1.51.0-h58003a5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.1-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.7.0-hf6efa0e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.8.2-hf3170e9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.1-hb6871ef_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.12-h894a449_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -406,7 +406,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -589,7 +589,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -626,11 +626,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.8-h8d0574d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-6_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-6_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.2-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda @@ -641,13 +641,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.32-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.52.0-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.1-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.8.2-h7039424_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.12-h20e6be0_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -710,7 +710,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -892,7 +892,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -925,8 +925,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda @@ -940,7 +940,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.7.0-h80d1838_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -1005,7 +1005,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -1188,7 +1188,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -1234,11 +1234,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda @@ -1250,7 +1250,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda @@ -1258,7 +1258,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.8.2-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda @@ -1319,7 +1319,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -1504,7 +1504,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -1542,11 +1542,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-6_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-6_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.4-h991f03e_0.conda @@ -1556,13 +1556,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.2-h11316ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.32-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.52.0-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libuv-1.51.0-h58003a5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.1-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.7.0-hf6efa0e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.8.2-hf3170e9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.1-hb6871ef_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda @@ -1624,7 +1624,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -1809,7 +1809,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -1847,11 +1847,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.8-h8d0574d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-6_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-6_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.2-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda @@ -1861,13 +1861,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_18.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.32-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.52.0-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.1-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.8.2-h7039424_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda @@ -1929,7 +1929,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -2113,7 +2113,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -2147,8 +2147,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda @@ -2161,7 +2161,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.7.0-h80d1838_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda @@ -2225,7 +2225,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -2410,7 +2410,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -2457,11 +2457,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda @@ -2473,14 +2473,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.8.2-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.12-hc97d973_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -2542,7 +2542,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -2725,7 +2725,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -2762,11 +2762,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-6_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-6_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.4-h991f03e_0.conda @@ -2777,13 +2777,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.2-h11316ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.32-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.52.0-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libuv-1.51.0-h58003a5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.1-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.7.0-hf6efa0e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.8.2-hf3170e9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.1-hb6871ef_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.12-h894a449_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -2846,7 +2846,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -3029,7 +3029,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -3066,11 +3066,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gsl-2.8-h8d0574d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-6_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-6_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.2-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda @@ -3081,13 +3081,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.32-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.52.0-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.1-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.8.2-h7039424_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.12-h20e6be0_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -3150,7 +3150,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -3332,7 +3332,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -3365,8 +3365,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda @@ -3380,7 +3380,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.7.0-h80d1838_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda @@ -3445,7 +3445,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/87/10/2c7edbf230e5c507d38367af498fa94258ed97205d9b4b6f63a921fe9c49/dunamai-1.26.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl @@ -3628,7 +3628,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl @@ -4943,20 +4943,20 @@ packages: - dnspython>=2.0.0 - idna>=2.0.0 requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/06/42/75a6098682e3d4d7d5dfcb94d0dd48dccd9e106112992a99524243bc63e6/essdiffraction-26.3.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl name: essdiffraction - version: 26.3.0 - sha256: 3e3063d8af817ece183258b35579491d7edf85c4ca95fa3f1cef7877ad24365a + version: 26.4.0 + sha256: a58c1ac09c2196ae3769ab8c2d1f8c0152e4e1a69a02bc286caf419cdaa9ec1d requires_dist: - dask>=2022.1.0 - - essreduce>=26.2.1 + - essreduce>=26.4.0 - graphviz - numpy>=2 - plopp>=26.2.0 - pythreejs>=2.4.1 - sciline>=25.4.1 - scipp>=25.11.0 - - scippneutron>=25.2.0 + - scippneutron>=26.3.0 - scippnexus>=23.12.0 - tof>=25.12.0 - ncrystal[cif]>=4.1.0 @@ -6650,76 +6650,72 @@ packages: purls: [] size: 1229639 timestamp: 1770863511331 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - build_number: 5 - sha256: 18c72545080b86739352482ba14ba2c4815e19e26a7417ca21a95b76ec8da24c - md5: c160954f7418d7b6e87eaf05a8913fa9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda + build_number: 6 + sha256: 7bfe936dbb5db04820cf300a9cc1f5ee8d5302fc896c2d66e30f1ee2f20fbfd6 + md5: 6d6d225559bfa6e2f3c90ee9c03d4e2e depends: - - libopenblas >=0.3.30,<0.3.31.0a0 - - libopenblas >=0.3.30,<1.0a0 + - libopenblas >=0.3.32,<0.3.33.0a0 + - libopenblas >=0.3.32,<1.0a0 constrains: + - blas 2.306 openblas + - liblapack 3.11.0 6*_openblas + - liblapacke 3.11.0 6*_openblas + - libcblas 3.11.0 6*_openblas - mkl <2026 - - liblapack 3.11.0 5*_openblas - - libcblas 3.11.0 5*_openblas - - blas 2.305 openblas - - liblapacke 3.11.0 5*_openblas license: BSD-3-Clause - license_family: BSD purls: [] - size: 18213 - timestamp: 1765818813880 -- conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - build_number: 5 - sha256: 4754de83feafa6c0b41385f8dab1b13f13476232e16f524564a340871a9fc3bc - md5: 36d2e68a156692cbae776b75d6ca6eae + size: 18621 + timestamp: 1774503034895 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-6_he492b99_openblas.conda + build_number: 6 + sha256: 6865098475f3804208038d0c424edf926f4dc9eacaa568d14e29f59df53731fd + md5: 93e7fc07b395c9e1341d3944dcf2aced depends: - - libopenblas >=0.3.30,<0.3.31.0a0 - - libopenblas >=0.3.30,<1.0a0 + - libopenblas >=0.3.32,<0.3.33.0a0 + - libopenblas >=0.3.32,<1.0a0 constrains: - - liblapack 3.11.0 5*_openblas - - blas 2.305 openblas - - libcblas 3.11.0 5*_openblas + - libcblas 3.11.0 6*_openblas + - blas 2.306 openblas - mkl <2026 - - liblapacke 3.11.0 5*_openblas + - liblapacke 3.11.0 6*_openblas + - liblapack 3.11.0 6*_openblas license: BSD-3-Clause - license_family: BSD purls: [] - size: 18476 - timestamp: 1765819054657 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - build_number: 5 - sha256: 620a6278f194dcabc7962277da6835b1e968e46ad0c8e757736255f5ddbfca8d - md5: bcc025e2bbaf8a92982d20863fe1fb69 + size: 18724 + timestamp: 1774503646078 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-6_h51639a9_openblas.conda + build_number: 6 + sha256: 979227fc03628925037ab2dfda008eb7b5592644d9c2c21dd285cefe8c42553d + md5: e551103471911260488a02155cef9c94 depends: - - libopenblas >=0.3.30,<0.3.31.0a0 - - libopenblas >=0.3.30,<1.0a0 + - libopenblas >=0.3.32,<0.3.33.0a0 + - libopenblas >=0.3.32,<1.0a0 constrains: - - libcblas 3.11.0 5*_openblas - - liblapack 3.11.0 5*_openblas - - liblapacke 3.11.0 5*_openblas - - blas 2.305 openblas + - liblapacke 3.11.0 6*_openblas + - liblapack 3.11.0 6*_openblas + - blas 2.306 openblas + - libcblas 3.11.0 6*_openblas - mkl <2026 license: BSD-3-Clause - license_family: BSD purls: [] - size: 18546 - timestamp: 1765819094137 -- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - build_number: 5 - sha256: f0cb7b2697461a306341f7ff32d5b361bb84f3e94478464c1e27ee01fc8f276b - md5: f9decf88743af85c9c9e05556a4c47c0 + size: 18859 + timestamp: 1774504387211 +- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + build_number: 6 + sha256: 10c8054f007adca8c780cd8bb9335fa5d990f0494b825158d3157983a25b1ea2 + md5: 95543eec964b4a4a7ca3c4c9be481aa1 depends: - - mkl >=2025.3.0,<2026.0a0 + - mkl >=2025.3.1,<2026.0a0 constrains: - - liblapack 3.11.0 5*_mkl - - libcblas 3.11.0 5*_mkl - - blas 2.305 mkl - - liblapacke 3.11.0 5*_mkl + - blas 2.306 mkl + - liblapacke 3.11.0 6*_mkl + - liblapack 3.11.0 6*_mkl + - libcblas 3.11.0 6*_mkl license: BSD-3-Clause - license_family: BSD purls: [] - size: 67438 - timestamp: 1765819100043 + size: 68082 + timestamp: 1774503684284 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e md5: 72c8fd1af66bd67bf580645b426513ed @@ -6819,66 +6815,62 @@ packages: purls: [] size: 290754 timestamp: 1764018009077 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - build_number: 5 - sha256: 0cbdcc67901e02dc17f1d19e1f9170610bd828100dc207de4d5b6b8ad1ae7ad8 - md5: 6636a2b6f1a87572df2970d3ebc87cc0 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda + build_number: 6 + sha256: 57edafa7796f6fa3ebbd5367692dd4c7f552be42109c2dd1a7c89b55089bf374 + md5: 36ae340a916635b97ac8a0655ace2a35 depends: - - libblas 3.11.0 5_h4a7cf45_openblas + - libblas 3.11.0 6_h4a7cf45_openblas constrains: - - liblapacke 3.11.0 5*_openblas - - blas 2.305 openblas - - liblapack 3.11.0 5*_openblas + - blas 2.306 openblas + - liblapack 3.11.0 6*_openblas + - liblapacke 3.11.0 6*_openblas license: BSD-3-Clause - license_family: BSD purls: [] - size: 18194 - timestamp: 1765818837135 -- conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - build_number: 5 - sha256: 8077c29ea720bd152be6e6859a3765228cde51301fe62a3b3f505b377c2cb48c - md5: b31d771cbccff686e01a687708a7ca41 + size: 18622 + timestamp: 1774503050205 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-6_h9b27e0a_openblas.conda + build_number: 6 + sha256: 8422e1ce083e015bdb44addd25c9a8fe99aa9b0edbd9b7f1239b7ac1e3d04f77 + md5: 2a174868cb9e136c4e92b3ffc2815f04 depends: - - libblas 3.11.0 5_he492b99_openblas + - libblas 3.11.0 6_he492b99_openblas constrains: - - liblapack 3.11.0 5*_openblas - - blas 2.305 openblas - - liblapacke 3.11.0 5*_openblas + - liblapacke 3.11.0 6*_openblas + - blas 2.306 openblas + - liblapack 3.11.0 6*_openblas license: BSD-3-Clause - license_family: BSD purls: [] - size: 18484 - timestamp: 1765819073006 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - build_number: 5 - sha256: 38809c361bbd165ecf83f7f05fae9b791e1baa11e4447367f38ae1327f402fc0 - md5: efd8bd15ca56e9d01748a3beab8404eb + size: 18713 + timestamp: 1774503667477 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-6_hb0561ab_openblas.conda + build_number: 6 + sha256: 2e6b3e9b1ab672133b70fc6730e42290e952793f132cb5e72eee22835463eba0 + md5: 805c6d31c5621fd75e53dfcf21fb243a depends: - - libblas 3.11.0 5_h51639a9_openblas + - libblas 3.11.0 6_h51639a9_openblas constrains: - - liblapacke 3.11.0 5*_openblas - - liblapack 3.11.0 5*_openblas - - blas 2.305 openblas + - liblapacke 3.11.0 6*_openblas + - blas 2.306 openblas + - liblapack 3.11.0 6*_openblas license: BSD-3-Clause - license_family: BSD purls: [] - size: 18548 - timestamp: 1765819108956 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - build_number: 5 - sha256: 49dc59d8e58360920314b8d276dd80da7866a1484a9abae4ee2760bc68f3e68d - md5: b3fa8e8b55310ba8ef0060103afb02b5 + size: 18863 + timestamp: 1774504433388 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda + build_number: 6 + sha256: 02b2a2225f4899c6aaa1dc723e06b3f7a4903d2129988f91fc1527409b07b0a5 + md5: 9e4bf521c07f4d423cba9296b7927e3c depends: - - libblas 3.11.0 5_hf2e6a31_mkl + - libblas 3.11.0 6_hf2e6a31_mkl constrains: - - liblapack 3.11.0 5*_mkl - - liblapacke 3.11.0 5*_mkl - - blas 2.305 mkl + - blas 2.306 mkl + - liblapacke 3.11.0 6*_mkl + - liblapack 3.11.0 6*_mkl license: BSD-3-Clause - license_family: BSD purls: [] - size: 68079 - timestamp: 1765819124349 + size: 68221 + timestamp: 1774503722413 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda sha256: 46561199545890e050a8a90edcfce984e5f881da86b09388926e3a6c6b759dec md5: ed6f7b7a35f942a0301e581d72616f7d @@ -7328,51 +7320,48 @@ packages: purls: [] size: 33731 timestamp: 1750274110928 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - sha256: 199d79c237afb0d4780ccd2fbf829cea80743df60df4705202558675e07dd2c5 - md5: be43915efc66345cccb3c310b6ed0374 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda + sha256: 6dc30b28f32737a1c52dada10c8f3a41bc9e021854215efca04a7f00487d09d9 + md5: 89d61bc91d3f39fda0ca10fcd3c68594 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 constrains: - - openblas >=0.3.30,<0.3.31.0a0 + - openblas >=0.3.32,<0.3.33.0a0 license: BSD-3-Clause - license_family: BSD purls: [] - size: 5927939 - timestamp: 1763114673331 -- conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - sha256: ba642353f7f41ab2d2eb6410fbe522238f0f4483bcd07df30b3222b4454ee7cd - md5: 9241a65e6e9605e4581a2a8005d7f789 + size: 5928890 + timestamp: 1774471724897 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.32-openmp_h9e49c7b_0.conda + sha256: 6764229359cd927c9efc036930ba28f83436b8d6759c5ac4ea9242fc29a7184e + md5: 4058c5f8dbef6d28cb069f96b95ae6df depends: - - __osx >=10.13 + - __osx >=11.0 - libgfortran - libgfortran5 >=14.3.0 - llvm-openmp >=19.1.7 constrains: - - openblas >=0.3.30,<0.3.31.0a0 + - openblas >=0.3.32,<0.3.33.0a0 license: BSD-3-Clause - license_family: BSD purls: [] - size: 6268795 - timestamp: 1763117623665 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_4.conda - sha256: ebbbc089b70bcde87c4121a083c724330f02a690fb9d7c6cd18c30f1b12504fa - md5: a6f6d3a31bb29e48d37ce65de54e2df0 + size: 6289730 + timestamp: 1774474444702 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.32-openmp_he657e61_0.conda + sha256: 713e453bde3531c22a660577e59bf91ef578dcdfd5edb1253a399fa23514949a + md5: 3a1111a4b6626abebe8b978bb5a323bf depends: - __osx >=11.0 - libgfortran - libgfortran5 >=14.3.0 - llvm-openmp >=19.1.7 constrains: - - openblas >=0.3.30,<0.3.31.0a0 + - openblas >=0.3.32,<0.3.33.0a0 license: BSD-3-Clause - license_family: BSD purls: [] - size: 4284132 - timestamp: 1768547079205 + size: 4308797 + timestamp: 1774472508546 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda sha256: d716847b7deca293d2e49ed1c8ab9e4b9e04b9d780aea49a97c26925b28a7993 md5: fd893f6a3002a635b5e50ceb9dd2c0f4 @@ -8627,87 +8616,83 @@ packages: version: 1.10.0 sha256: 5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*' -- conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - sha256: 4791285a34195615e22a94dc62cbd43181548b34eb34e6cb1dcf8f469476a32e - md5: ba562095149fde99c700365d90e6d632 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.8.2-he4ff34a_0.conda + sha256: d1a673d1418d9e956b6e4e46c23e72a511c5c1d45dc5519c947457427036d5e2 + md5: baffb1570b3918c784d4490babc52fbf depends: - - __glibc >=2.28,<3.0.a0 - - libstdcxx >=14 - libgcc >=14 - - libnghttp2 >=1.67.0,<2.0a0 + - libstdcxx >=14 + - __glibc >=2.28,<3.0.a0 + - libnghttp2 >=1.68.1,<2.0a0 + - libuv >=1.51.0,<2.0a0 - c-ares >=1.34.6,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - libsqlite >=3.52.0,<4.0a0 + - icu >=78.3,<79.0a0 + - libzlib >=1.3.2,<2.0a0 + - libabseil >=20260107.1,<20260108.0a0 + - libabseil * cxx17* - zstd >=1.5.7,<1.6.0a0 - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - - libuv >=1.51.0,<2.0a0 - - openssl >=3.5.5,<4.0a0 - - libabseil >=20260107.1,<20260108.0a0 - - libabseil * cxx17* - - icu >=78.2,<79.0a0 - - libzlib >=1.3.1,<2.0a0 - - libsqlite >=3.51.2,<4.0a0 license: MIT - license_family: MIT purls: [] - size: 18875002 - timestamp: 1772300115686 -- conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.7.0-hf6efa0e_0.conda - sha256: a23a3b536f1ccd3c10a2cbdf8c638b601c51f6e9bbf1e6c708610cf5b1df3d0f - md5: 448185eb18358e4b5fe2a157cc7e415f + size: 18829340 + timestamp: 1774514313036 +- conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.8.2-hf3170e9_0.conda + sha256: 6e82ed9c2de2e5a472a9c25f7a4a3a296d33aa38b94151acbbb5a28754962d8d + md5: de36be6257a15d17e85c96b47d290f82 depends: - libcxx >=19 - - __osx >=10.15 - - openssl >=3.5.5,<4.0a0 - - c-ares >=1.34.6,<2.0a0 - - libuv >=1.51.0,<2.0a0 + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + - libnghttp2 >=1.68.1,<2.0a0 + - libabseil >=20260107.1,<20260108.0a0 + - libabseil * cxx17* + - icu >=78.3,<79.0a0 - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - - libabseil >=20260107.1,<20260108.0a0 - - libabseil * cxx17* - - libnghttp2 >=1.67.0,<2.0a0 - - icu >=78.2,<79.0a0 - - libsqlite >=3.51.2,<4.0a0 - - libzlib >=1.3.1,<2.0a0 + - libsqlite >=3.52.0,<4.0a0 + - libuv >=1.51.0,<2.0a0 + - c-ares >=1.34.6,<2.0a0 + - openssl >=3.5.5,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: MIT - license_family: MIT purls: [] - size: 18342335 - timestamp: 1772299132495 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - sha256: 6398a45d2bb943585540d98a9b1b3bed1fb48fabd327b56eba2ece00e3dd202f - md5: 22a4a51f30590f274bb3f831d911c09a + size: 18382168 + timestamp: 1774517889949 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.8.2-h7039424_0.conda + sha256: 4782b172b3b8a557b60bf5f591821cf100e2092ba7a5494ce047dfa41626de26 + md5: ca8277c52fdface8bb8ebff7cd9a6f56 depends: - - __osx >=11.0 - libcxx >=19 - - libsqlite >=3.51.2,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 + - __osx >=11.0 + - icu >=78.3,<79.0a0 - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 + - libnghttp2 >=1.68.1,<2.0a0 - libuv >=1.51.0,<2.0a0 + - libsqlite >=3.52.0,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - openssl >=3.5.5,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + - c-ares >=1.34.6,<2.0a0 - libabseil >=20260107.1,<20260108.0a0 - libabseil * cxx17* - - icu >=78.2,<79.0a0 - - c-ares >=1.34.6,<2.0a0 - - openssl >=3.5.5,<4.0a0 - - libnghttp2 >=1.67.0,<2.0a0 - - libzlib >=1.3.1,<2.0a0 license: MIT - license_family: MIT purls: [] - size: 17351648 - timestamp: 1772299122966 -- conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.7.0-h80d1838_0.conda - sha256: 0ea0ddad32366396d1beda7ce93ddd3d9f705286c1a4f99f05ec0049183c1e97 - md5: 61b62d3be12c9edbe34f202d51891927 + size: 17101803 + timestamp: 1774517834028 +- conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda + sha256: 5e38e51da1aa4bc352db9b4cec1c3e25811de0f4408edaa24e009a64de6dbfdf + md5: e626ee7934e4b7cb21ce6b721cff8677 license: MIT - license_family: MIT purls: [] - size: 31254428 - timestamp: 1772299132945 + size: 31271315 + timestamp: 1774517904472 - pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl name: notebook-shim version: 0.2.4 @@ -12402,45 +12387,45 @@ packages: version: 6.2.0 sha256: a152bf4f249c847a66497a4a95f63376ed68ac6abf092a2f7cfb29d044ecff44 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl name: tomli - version: 2.4.0 - sha256: d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa + version: 2.4.1 + sha256: 36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: tomli - version: 2.4.0 - sha256: 9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e + version: 2.4.1 + sha256: f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl name: tomli - version: 2.4.0 - sha256: 84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0 + version: 2.4.1 + sha256: eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl name: tomli - version: 2.4.0 - sha256: 3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87 + version: 2.4.1 + sha256: 5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: tomli - version: 2.4.0 - sha256: b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867 + version: 2.4.1 + sha256: 5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl name: tomli - version: 2.4.0 - sha256: 5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9 + version: 2.4.1 + sha256: 8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl name: tomli - version: 2.4.0 - sha256: 1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e + version: 2.4.1 + sha256: 4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl name: tomli - version: 2.4.0 - sha256: 5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76 + version: 2.4.1 + sha256: f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl name: toolz diff --git a/pixi.toml b/pixi.toml index 4020b449..629589f7 100644 --- a/pixi.toml +++ b/pixi.toml @@ -94,8 +94,8 @@ default = { features = ['default', 'py-max'] } unit-tests = 'python -m pytest tests/unit/ --color=yes -v' integration-tests = 'python -m pytest tests/integration/ --color=yes -n auto -v' -notebook-tests = 'python -m pytest --nbmake docs/docs/tutorials/ --nbmake-timeout=600 --color=yes -n auto -v' script-tests = 'python -m pytest tools/test_scripts.py --color=yes -n auto -v' +notebook-tests = 'python -m pytest --nbmake docs/docs/tutorials/ --nbmake-timeout=600 --color=yes -n auto -v' test = { depends-on = ['unit-tests'] } From fddac95bb9040ace1cfec17cde326be16b98f9f5 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 17:18:13 +0100 Subject: [PATCH 33/48] Add some missing docstrings to public methods --- .../analysis/calculators/base.py | 2 + .../analysis/calculators/crysfml.py | 1 + .../analysis/calculators/cryspy.py | 1 + .../analysis/calculators/pdffit.py | 33 +++++ .../analysis/categories/aliases/default.py | 2 + .../categories/constraints/default.py | 2 + .../analysis/categories/fit_mode/enums.py | 2 + .../analysis/categories/fit_mode/fit_mode.py | 1 + .../joint_fit_experiments/default.py | 2 + .../analysis/fit_helpers/tracking.py | 9 ++ src/easydiffraction/analysis/fitting.py | 13 ++ src/easydiffraction/core/category.py | 5 + src/easydiffraction/core/datablock.py | 10 +- src/easydiffraction/core/guard.py | 1 + .../categories/background/chebyshev.py | 6 +- .../categories/background/line_segment.py | 3 + .../experiment/categories/data/bragg_pd.py | 11 ++ .../experiment/categories/data/bragg_sc.py | 27 ++-- .../experiment/categories/data/total_pd.py | 12 +- .../categories/excluded_regions/default.py | 3 + .../experiment/categories/extinction/shelx.py | 2 + .../experiment/categories/instrument/cwl.py | 2 + .../experiment/categories/instrument/tof.py | 5 + .../categories/linked_crystal/default.py | 2 + .../categories/linked_phases/default.py | 2 + .../experiment/categories/peak/cwl_mixins.py | 11 ++ .../experiment/categories/peak/tof_mixins.py | 10 ++ .../categories/peak/total_mixins.py | 6 + .../datablocks/experiment/item/bragg_pd.py | 1 + .../datablocks/experiment/item/enums.py | 79 ++++++++++++ .../categories/atom_sites/default.py | 63 ++++++++++ .../structure/categories/cell/default.py | 42 +++++++ .../categories/space_group/default.py | 14 +++ src/easydiffraction/display/base.py | 15 +++ src/easydiffraction/display/tables.py | 7 ++ src/easydiffraction/io/cif/serialize.py | 25 ++++ src/easydiffraction/project/project.py | 48 ++++++++ src/easydiffraction/project/project_info.py | 28 +++++ src/easydiffraction/summary/summary.py | 1 + src/easydiffraction/utils/environment.py | 17 +++ src/easydiffraction/utils/logging.py | 116 +++++++++++++++++- src/easydiffraction/utils/utils.py | 16 +++ 42 files changed, 637 insertions(+), 21 deletions(-) diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index 7ff6bc92..5dceb01c 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -17,11 +17,13 @@ class CalculatorBase(ABC): @property @abstractmethod def name(self) -> str: + """Short identifier of the calculation engine.""" pass @property @abstractmethod def engine_imported(self) -> bool: + """Whether the underlying calculation library could be imported.""" pass @abstractmethod diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 2fdf8736..9396c16d 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -41,6 +41,7 @@ class CrysfmlCalculator(CalculatorBase): @property def name(self) -> str: + """Short identifier of this calculator engine.""" return 'crysfml' def calculate_structure_factors( diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 8e00c20a..6344f178 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -50,6 +50,7 @@ class CryspyCalculator(CalculatorBase): @property def name(self) -> str: + """Short identifier of this calculator engine.""" return 'cryspy' def __init__(self) -> None: diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py index 28806f31..5602d949 100644 --- a/src/easydiffraction/analysis/calculators/pdffit.py +++ b/src/easydiffraction/analysis/calculators/pdffit.py @@ -39,6 +39,9 @@ # print("⚠️ 'pdffit' module not found. This calculation engine will # not be available.") PdfFit = None + redirect_stdout = None + pdffit_cif_parser = None + _pdffit_devnull = None @CalculatorFactory.register @@ -53,9 +56,25 @@ class PdffitCalculator(CalculatorBase): @property def name(self) -> str: + """Short identifier of this calculator engine.""" return 'pdffit' def calculate_structure_factors(self, structures: object, experiments: object) -> list: + """ + Placeholder — PDF does not compute HKL structure factors. + + Parameters + ---------- + structures : object + Unused; kept for interface consistency. + experiments : object + Unused; kept for interface consistency. + + Returns + ------- + list + An empty list. + """ # PDF doesn't compute HKL but we keep interface consistent # Intentionally unused, required by public API/signature del structures, experiments @@ -68,6 +87,20 @@ def calculate_pattern( experiment: ExperimentBase, called_by_minimizer: bool = False, ) -> None: + """ + Calculate the PDF pattern using PDFfit2. + + Parameters + ---------- + structure : Structure + The structure object supplying atom sites and cell + parameters. + experiment : ExperimentBase + The experiment object supplying instrument and peak + parameters. + called_by_minimizer : bool, default=False + Unused; kept for interface consistency. + """ # Intentionally unused, required by public API/signature del called_by_minimizer diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 63cc5f28..a39e17ef 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -69,6 +69,7 @@ def label(self) -> StringDescriptor: @label.setter def label(self, value: str) -> None: + """Set the alias label.""" self._label.value = value @property @@ -84,6 +85,7 @@ def param_uid(self) -> StringDescriptor: @param_uid.setter def param_uid(self, value: str) -> None: + """Set the parameter UID.""" self._param_uid.value = value diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index ebc51aa8..509049bf 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -67,6 +67,7 @@ def lhs_alias(self) -> StringDescriptor: @lhs_alias.setter def lhs_alias(self, value: str) -> None: + """Set the left-hand side alias string.""" self._lhs_alias.value = value @property @@ -82,6 +83,7 @@ def rhs_expr(self) -> StringDescriptor: @rhs_expr.setter def rhs_expr(self, value: str) -> None: + """Set the right-hand side expression string.""" self._rhs_expr.value = value diff --git a/src/easydiffraction/analysis/categories/fit_mode/enums.py b/src/easydiffraction/analysis/categories/fit_mode/enums.py index 7f11b33b..5e904eae 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/enums.py +++ b/src/easydiffraction/analysis/categories/fit_mode/enums.py @@ -15,9 +15,11 @@ class FitModeEnum(str, Enum): @classmethod def default(cls) -> FitModeEnum: + """Return the default fit mode (SINGLE).""" return cls.SINGLE def description(self) -> str: + """Return a human-readable description of this fit mode.""" if self is FitModeEnum.SINGLE: return 'Independent fitting of each experiment; no shared parameters' elif self is FitModeEnum.JOINT: diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index b0589a2e..302dcf3c 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -61,4 +61,5 @@ def mode(self) -> StringDescriptor: @mode.setter def mode(self, value: str) -> None: + """Set the fitting strategy value.""" self._mode.value = value diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index 454c2524..9b69f172 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -70,6 +70,7 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: + """Set the experiment identifier.""" self._id.value = value @property @@ -85,6 +86,7 @@ def weight(self) -> NumericDescriptor: @weight.setter def weight(self, value: float) -> None: + """Set the experiment weight factor.""" self._weight.value = value diff --git a/src/easydiffraction/analysis/fit_helpers/tracking.py b/src/easydiffraction/analysis/fit_helpers/tracking.py index 05b607c1..eb32f3ea 100644 --- a/src/easydiffraction/analysis/fit_helpers/tracking.py +++ b/src/easydiffraction/analysis/fit_helpers/tracking.py @@ -46,9 +46,18 @@ def __init__(self, live: object) -> None: self._live = live def update(self, renderable: object) -> None: + """ + Refresh the live display with a new renderable. + + Parameters + ---------- + renderable : object + A Rich-compatible renderable to display. + """ self._live.update(renderable, refresh=True) def close(self) -> None: + """Stop the live display, suppressing any errors.""" with suppress(Exception): self._live.stop() diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index e2d14a1b..302db905 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -64,6 +64,19 @@ def fit( param._fit_start_value = param.value def objective_function(engine_params: Dict[str, Any]) -> np.ndarray: + """ + Evaluate the residual for the current minimizer parameters. + + Parameters + ---------- + engine_params : Dict[str, Any] + Parameter values provided by the minimizer engine. + + Returns + ------- + np.ndarray + Residual array passed back to the minimizer. + """ return self._residual_function( engine_params=engine_params, parameters=params, diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index f94f5bec..5ad10739 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -36,6 +36,9 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def unique_name(self) -> str: + """ + Fully qualified name combining datablock, category, and entry. + """ parts = [ self._identity.datablock_entry_name, self._identity.category_code, @@ -47,6 +50,7 @@ def unique_name(self) -> str: @property def parameters(self) -> list: + """All GenericDescriptorBase instances on this item.""" return [v for v in vars(self).values() if isinstance(v, GenericDescriptorBase)] @property @@ -198,6 +202,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def unique_name(self) -> str | None: + """Return None; collections do not carry their own unique name.""" return None @property diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 2583079f..02241e91 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -63,10 +63,15 @@ def _update_categories( @property def unique_name(self) -> str | None: + """Unique name of this datablock item (from identity).""" return self._identity.datablock_entry_name @property def categories(self) -> list: + """ + All category objects in this datablock, sorted by update + priority. + """ cats = [ v for v in vars(self).values() if isinstance(v, (CategoryItem, CategoryCollection)) ] @@ -153,6 +158,7 @@ def __str__(self) -> str: @property def unique_name(self) -> str | None: + """Return None; collections do not carry their own unique name.""" return None @property @@ -163,14 +169,14 @@ def parameters(self) -> list: params.extend(db.parameters) return params - # was in class AbstractDatablock(ABC): @property def fittable_parameters(self) -> list: + """All non-constrained Parameter instances in this collection.""" return [p for p in self.parameters if isinstance(p, Parameter) and not p.constrained] - # was in class AbstractDatablock(ABC): @property def free_parameters(self) -> list: + """All fittable parameters that are currently marked as free.""" return [p for p in self.fittable_parameters if p.free] @property diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index 581f9722..5f553d50 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -121,6 +121,7 @@ def _log_name(self) -> str: @property def unique_name(self) -> str: + """Fallback unique name: the class name.""" return type(self).__name__ # @property diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 4cbef2c0..0c8af42c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -98,10 +98,8 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: + """Set the polynomial term identifier.""" self._id.value = value - - @property - def order(self) -> NumericDescriptor: """ Order used in a Chebyshev polynomial background term. @@ -113,6 +111,7 @@ def order(self) -> NumericDescriptor: @order.setter def order(self, value: float) -> None: + """Set the polynomial order.""" self._order.value = value @property @@ -127,6 +126,7 @@ def coef(self) -> Parameter: @coef.setter def coef(self, value: float) -> None: + """Set the polynomial coefficient.""" self._coef.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index 91c8b402..da8e3916 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -106,6 +106,7 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: + """Set the line segment identifier.""" self._id.value = value @property @@ -122,6 +123,7 @@ def x(self) -> NumericDescriptor: @x.setter def x(self, value: float) -> None: + """Set the x-coordinate of the control point.""" self._x.value = value @property @@ -137,6 +139,7 @@ def y(self) -> Parameter: @y.setter def y(self, value: float) -> None: + """Set the intensity of the control point.""" self._y.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 29f363b1..95d32cb7 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -409,6 +409,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def calc_status(self) -> np.ndarray: + """Refinement-status flags for each data point as an array.""" return np.fromiter( (p.calc_status.value for p in self._items), dtype=object, # TODO: needed? DataTypes.NUMERIC? @@ -416,6 +417,7 @@ def calc_status(self) -> np.ndarray: @property def d_spacing(self) -> np.ndarray: + """D-spacing values for active (non-excluded) data points.""" return np.fromiter( (p.d_spacing.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? @@ -423,6 +425,7 @@ def d_spacing(self) -> np.ndarray: @property def intensity_meas(self) -> np.ndarray: + """Measured intensities for active data points.""" return np.fromiter( (p.intensity_meas.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? @@ -430,6 +433,12 @@ def intensity_meas(self) -> np.ndarray: @property def intensity_meas_su(self) -> np.ndarray: + """ + Standard uncertainties of the measured intensities. + + Values smaller than 0.0001 are replaced with 1.0 to prevent + fitting failures. + """ # TODO: The following is a temporary workaround to handle zero # or near-zero uncertainties in the data, when dats is loaded # from CIF files. This is necessary because zero uncertainties @@ -452,6 +461,7 @@ def intensity_meas_su(self) -> np.ndarray: @property def intensity_calc(self) -> np.ndarray: + """Calculated intensities for active data points.""" return np.fromiter( (p.intensity_calc.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? @@ -459,6 +469,7 @@ def intensity_calc(self) -> np.ndarray: @property def intensity_bkg(self) -> np.ndarray: + """Background intensities for active data points.""" return np.fromiter( (p.intensity_bkg.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 00519b8b..816b81a4 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -370,63 +370,72 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def d_spacing(self) -> np.ndarray: + """D-spacing values for all reflection data points.""" return np.fromiter( (p.d_spacing.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def sin_theta_over_lambda(self) -> np.ndarray: + """sinθ/λ values for all reflection data points.""" return np.fromiter( (p.sin_theta_over_lambda.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def index_h(self) -> np.ndarray: + """Miller h indices for all reflection data points.""" return np.fromiter( (p.index_h.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def index_k(self) -> np.ndarray: + """Miller k indices for all reflection data points.""" return np.fromiter( (p.index_k.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def index_l(self) -> np.ndarray: + """Miller l indices for all reflection data points.""" return np.fromiter( (p.index_l.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def intensity_meas(self) -> np.ndarray: + """Measured structure-factor intensities for all reflections.""" return np.fromiter( (p.intensity_meas.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def intensity_meas_su(self) -> np.ndarray: + """Standard uncertainties of the measured intensities.""" return np.fromiter( (p.intensity_meas_su.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def intensity_calc(self) -> np.ndarray: + """Calculated structure-factor intensities for all reflections.""" return np.fromiter( (p.intensity_calc.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def wavelength(self) -> np.ndarray: + """Wavelengths associated with each reflection.""" return np.fromiter( (p.wavelength.value for p in self._items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index b4957ddb..a53ae060 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -276,30 +276,34 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def calc_status(self) -> np.ndarray: + """Refinement-status flags for each data point as an array.""" return np.fromiter( (p.calc_status.value for p in self._items), - dtype=object, # TODO: needed? DataTypes.NUMERIC? + dtype=object, ) @property def intensity_meas(self) -> np.ndarray: + """Measured G(r) values for active data points.""" return np.fromiter( (p.g_r_meas.value for p in self._calc_items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def intensity_meas_su(self) -> np.ndarray: + """Standard uncertainties of the measured G(r) values.""" return np.fromiter( (p.g_r_meas_su.value for p in self._calc_items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property def intensity_calc(self) -> np.ndarray: + """Calculated G(r) values for active data points.""" return np.fromiter( (p.g_r_calc.value for p in self._calc_items), - dtype=float, # TODO: needed? DataTypes.NUMERIC? + dtype=float, ) @property diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 4344e45e..7cc40287 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -83,6 +83,7 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: + """Set the excluded-region identifier.""" self._id.value = value @property @@ -98,6 +99,7 @@ def start(self) -> NumericDescriptor: @start.setter def start(self, value: float) -> None: + """Set the start of the excluded region.""" self._start.value = value @property @@ -113,6 +115,7 @@ def end(self) -> NumericDescriptor: @end.setter def end(self, value: float) -> None: + """Set the end of the excluded region.""" self._end.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index 816729bf..dab297ce 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -79,6 +79,7 @@ def mosaicity(self) -> Parameter: @mosaicity.setter def mosaicity(self, value: float) -> None: + """Set the mosaicity value.""" self._mosaicity.value = value @property @@ -93,4 +94,5 @@ def radius(self) -> Parameter: @radius.setter def radius(self, value: float) -> None: + """Set the crystal radius.""" self._radius.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index c850d198..52b34d51 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -47,6 +47,7 @@ def setup_wavelength(self) -> Parameter: @setup_wavelength.setter def setup_wavelength(self, value: float) -> None: + """Set the incident wavelength.""" self._setup_wavelength.value = value @@ -112,4 +113,5 @@ def calib_twotheta_offset(self) -> Parameter: @calib_twotheta_offset.setter def calib_twotheta_offset(self, value: float) -> None: + """Set the 2θ instrument misalignment offset.""" self._calib_twotheta_offset.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index 413a6609..7beb7bc8 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -110,6 +110,7 @@ def setup_twotheta_bank(self) -> Parameter: @setup_twotheta_bank.setter def setup_twotheta_bank(self, value: float) -> None: + """Set the detector bank 2θ position.""" self._setup_twotheta_bank.value = value @property @@ -124,6 +125,7 @@ def calib_d_to_tof_offset(self) -> Parameter: @calib_d_to_tof_offset.setter def calib_d_to_tof_offset(self, value: float) -> None: + """Set the TOF calibration offset.""" self._calib_d_to_tof_offset.value = value @property @@ -138,6 +140,7 @@ def calib_d_to_tof_linear(self) -> Parameter: @calib_d_to_tof_linear.setter def calib_d_to_tof_linear(self, value: float) -> None: + """Set the TOF linear calibration coefficient.""" self._calib_d_to_tof_linear.value = value @property @@ -152,6 +155,7 @@ def calib_d_to_tof_quad(self) -> Parameter: @calib_d_to_tof_quad.setter def calib_d_to_tof_quad(self, value: float) -> None: + """Set the TOF quadratic calibration coefficient.""" self._calib_d_to_tof_quad.value = value @property @@ -166,4 +170,5 @@ def calib_d_to_tof_recip(self) -> Parameter: @calib_d_to_tof_recip.setter def calib_d_to_tof_recip(self, value: float) -> None: + """Set the TOF reciprocal velocity calibration coefficient.""" self._calib_d_to_tof_recip.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index 08ebb9b3..c49d8f54 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -75,6 +75,7 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: + """Set the linked-crystal identifier.""" self._id.value = value @property @@ -89,4 +90,5 @@ def scale(self) -> Parameter: @scale.setter def scale(self, value: float) -> None: + """Set the linked-crystal scale factor.""" self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index 79fcb17c..0039c194 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -65,6 +65,7 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: + """Set the linked-phase identifier.""" self._id.value = value @property @@ -79,6 +80,7 @@ def scale(self) -> Parameter: @scale.setter def scale(self, value: float) -> None: + """Set the linked-phase scale factor.""" self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 67874d0b..76de5ad1 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -90,6 +90,7 @@ def broad_gauss_u(self) -> Parameter: @broad_gauss_u.setter def broad_gauss_u(self, value: float) -> None: + """Set the Gaussian broadening coefficient U.""" self._broad_gauss_u.value = value @property @@ -105,6 +106,7 @@ def broad_gauss_v(self) -> Parameter: @broad_gauss_v.setter def broad_gauss_v(self, value: float) -> None: + """Set the Gaussian broadening coefficient V.""" self._broad_gauss_v.value = value @property @@ -120,6 +122,7 @@ def broad_gauss_w(self) -> Parameter: @broad_gauss_w.setter def broad_gauss_w(self, value: float) -> None: + """Set the Gaussian broadening coefficient W.""" self._broad_gauss_w.value = value @property @@ -135,6 +138,7 @@ def broad_lorentz_x(self) -> Parameter: @broad_lorentz_x.setter def broad_lorentz_x(self, value: float) -> None: + """Set the Lorentzian broadening coefficient X.""" self._broad_lorentz_x.value = value @property @@ -150,6 +154,7 @@ def broad_lorentz_y(self) -> Parameter: @broad_lorentz_y.setter def broad_lorentz_y(self, value: float) -> None: + """Set the Lorentzian broadening coefficient Y.""" self._broad_lorentz_y.value = value @@ -216,6 +221,7 @@ def asym_empir_1(self) -> Parameter: @asym_empir_1.setter def asym_empir_1(self, value: float) -> None: + """Set empirical asymmetry coefficient p1.""" self._asym_empir_1.value = value @property @@ -230,6 +236,7 @@ def asym_empir_2(self) -> Parameter: @asym_empir_2.setter def asym_empir_2(self, value: float) -> None: + """Set empirical asymmetry coefficient p2.""" self._asym_empir_2.value = value @property @@ -244,6 +251,7 @@ def asym_empir_3(self) -> Parameter: @asym_empir_3.setter def asym_empir_3(self, value: float) -> None: + """Set empirical asymmetry coefficient p3.""" self._asym_empir_3.value = value @property @@ -258,6 +266,7 @@ def asym_empir_4(self) -> Parameter: @asym_empir_4.setter def asym_empir_4(self, value: float) -> None: + """Set empirical asymmetry coefficient p4.""" self._asym_empir_4.value = value @@ -304,6 +313,7 @@ def asym_fcj_1(self) -> Parameter: @asym_fcj_1.setter def asym_fcj_1(self, value: float) -> None: + """Set FCJ asymmetry parameter 1.""" self._asym_fcj_1.value = value @property @@ -318,4 +328,5 @@ def asym_fcj_2(self) -> Parameter: @asym_fcj_2.setter def asym_fcj_2(self, value: float) -> None: + """Set FCJ asymmetry parameter 2.""" self._asym_fcj_2.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index 1d0b5fab..6960b022 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -121,6 +121,7 @@ def broad_gauss_sigma_0(self) -> Parameter: @broad_gauss_sigma_0.setter def broad_gauss_sigma_0(self, value: float) -> None: + """Set Gaussian broadening coefficient σ₀.""" self._broad_gauss_sigma_0.value = value @property @@ -135,6 +136,7 @@ def broad_gauss_sigma_1(self) -> Parameter: @broad_gauss_sigma_1.setter def broad_gauss_sigma_1(self, value: float) -> None: + """Set Gaussian broadening coefficient σ₁.""" self._broad_gauss_sigma_1.value = value @property @@ -150,6 +152,7 @@ def broad_gauss_sigma_2(self) -> Parameter: @broad_gauss_sigma_2.setter def broad_gauss_sigma_2(self, value: float) -> None: + """Set Gaussian broadening coefficient σ₂.""" self._broad_gauss_sigma_2.value = value @property @@ -165,6 +168,7 @@ def broad_lorentz_gamma_0(self) -> Parameter: @broad_lorentz_gamma_0.setter def broad_lorentz_gamma_0(self, value: float) -> None: + """Set Lorentzian broadening coefficient γ₀.""" self._broad_lorentz_gamma_0.value = value @property @@ -180,6 +184,7 @@ def broad_lorentz_gamma_1(self) -> Parameter: @broad_lorentz_gamma_1.setter def broad_lorentz_gamma_1(self, value: float) -> None: + """Set Lorentzian broadening coefficient γ₁.""" self._broad_lorentz_gamma_1.value = value @property @@ -195,6 +200,7 @@ def broad_lorentz_gamma_2(self) -> Parameter: @broad_lorentz_gamma_2.setter def broad_lorentz_gamma_2(self, value: float) -> None: + """Set Lorentzian broadening coefficient γ₂.""" self._broad_lorentz_gamma_2.value = value @property @@ -210,6 +216,7 @@ def broad_mix_beta_0(self) -> Parameter: @broad_mix_beta_0.setter def broad_mix_beta_0(self, value: float) -> None: + """Set mixing parameter β₀.""" self._broad_mix_beta_0.value = value @property @@ -225,6 +232,7 @@ def broad_mix_beta_1(self) -> Parameter: @broad_mix_beta_1.setter def broad_mix_beta_1(self, value: float) -> None: + """Set mixing parameter β₁.""" self._broad_mix_beta_1.value = value @@ -267,6 +275,7 @@ def asym_alpha_0(self) -> Parameter: @asym_alpha_0.setter def asym_alpha_0(self, value: float) -> None: + """Set Ikeda-Carpenter asymmetry parameter α₀.""" self._asym_alpha_0.value = value @property @@ -281,4 +290,5 @@ def asym_alpha_1(self) -> Parameter: @asym_alpha_1.setter def asym_alpha_1(self, value: float) -> None: + """Set Ikeda-Carpenter asymmetry parameter α₁.""" self._asym_alpha_1.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index f772a74b..89097ccd 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -102,6 +102,7 @@ def damp_q(self) -> Parameter: @damp_q.setter def damp_q(self, value: float) -> None: + """Set the Q-resolution damping factor.""" self._damp_q.value = value @property @@ -117,6 +118,7 @@ def broad_q(self) -> Parameter: @broad_q.setter def broad_q(self, value: float) -> None: + """Set the quadratic PDF peak broadening coefficient.""" self._broad_q.value = value @property @@ -132,6 +134,7 @@ def cutoff_q(self) -> Parameter: @cutoff_q.setter def cutoff_q(self, value: float) -> None: + """Set the Q-value cutoff for the Fourier transform.""" self._cutoff_q.value = value @property @@ -146,6 +149,7 @@ def sharp_delta_1(self) -> Parameter: @sharp_delta_1.setter def sharp_delta_1(self, value: float) -> None: + """Set the PDF peak sharpening coefficient delta_1.""" self._sharp_delta_1.value = value @property @@ -160,6 +164,7 @@ def sharp_delta_2(self) -> Parameter: @sharp_delta_2.setter def sharp_delta_2(self, value: float) -> None: + """Set the PDF peak sharpening coefficient delta_2.""" self._sharp_delta_2.value = value @property @@ -175,4 +180,5 @@ def damp_particle_diameter(self) -> Parameter: @damp_particle_diameter.setter def damp_particle_diameter(self, value: float) -> None: + """Set the particle diameter for spherical envelope damping.""" self._damp_particle_diameter.value = value diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index 27d6602a..6f744e78 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -195,6 +195,7 @@ def background_type(self, new_type: str) -> None: @property def background(self) -> object: + """Active background model for this experiment.""" return self._background def show_supported_background_types(self) -> None: diff --git a/src/easydiffraction/datablocks/experiment/item/enums.py b/src/easydiffraction/datablocks/experiment/item/enums.py index 1813ac50..78866a8d 100644 --- a/src/easydiffraction/datablocks/experiment/item/enums.py +++ b/src/easydiffraction/datablocks/experiment/item/enums.py @@ -13,9 +13,23 @@ class SampleFormEnum(str, Enum): @classmethod def default(cls) -> 'SampleFormEnum': + """Return the default sample form (POWDER). + + Returns + ------- + SampleFormEnum + The default enum member. + """ return cls.POWDER def description(self) -> str: + """Return a human-readable description of this sample form. + + Returns + ------- + str + Description string for the current enum member. + """ if self is SampleFormEnum.POWDER: return 'Powdered or polycrystalline sample.' elif self is SampleFormEnum.SINGLE_CRYSTAL: @@ -30,9 +44,23 @@ class ScatteringTypeEnum(str, Enum): @classmethod def default(cls) -> 'ScatteringTypeEnum': + """Return the default scattering type (BRAGG). + + Returns + ------- + ScatteringTypeEnum + The default enum member. + """ return cls.BRAGG def description(self) -> str: + """Return a human-readable description of this scattering type. + + Returns + ------- + str + Description string for the current enum member. + """ if self is ScatteringTypeEnum.BRAGG: return 'Bragg diffraction for conventional structure refinement.' elif self is ScatteringTypeEnum.TOTAL: @@ -47,9 +75,23 @@ class RadiationProbeEnum(str, Enum): @classmethod def default(cls) -> 'RadiationProbeEnum': + """Return the default radiation probe (NEUTRON). + + Returns + ------- + RadiationProbeEnum + The default enum member. + """ return cls.NEUTRON def description(self) -> str: + """Return a human-readable description of this radiation probe. + + Returns + ------- + str + Description string for the current enum member. + """ if self is RadiationProbeEnum.NEUTRON: return 'Neutron diffraction.' elif self is RadiationProbeEnum.XRAY: @@ -65,9 +107,23 @@ class BeamModeEnum(str, Enum): @classmethod def default(cls) -> 'BeamModeEnum': + """Return the default beam mode (CONSTANT_WAVELENGTH). + + Returns + ------- + BeamModeEnum + The default enum member. + """ return cls.CONSTANT_WAVELENGTH def description(self) -> str: + """Return a human-readable description of this beam mode. + + Returns + ------- + str + Description string for the current enum member. + """ if self is BeamModeEnum.CONSTANT_WAVELENGTH: return 'Constant wavelength (CW) diffraction.' elif self is BeamModeEnum.TIME_OF_FLIGHT: @@ -104,6 +160,22 @@ def default( scattering_type: ScatteringTypeEnum | None = None, beam_mode: BeamModeEnum | None = None, ) -> 'PeakProfileTypeEnum': + """Return the default peak profile type for a given mode. + + Parameters + ---------- + scattering_type : ScatteringTypeEnum | None, default=None + Scattering type; defaults to ``ScatteringTypeEnum.default()`` + when ``None``. + beam_mode : BeamModeEnum | None, default=None + Beam mode; defaults to ``BeamModeEnum.default()`` when + ``None``. + + Returns + ------- + PeakProfileTypeEnum + The default profile type for the given combination. + """ if scattering_type is None: scattering_type = ScatteringTypeEnum.default() if beam_mode is None: @@ -119,6 +191,13 @@ def default( }[(scattering_type, beam_mode)] def description(self) -> str: + """Return a human-readable description of this peak profile type. + + Returns + ------- + str + Description string for the current enum member. + """ if self is PeakProfileTypeEnum.PSEUDO_VOIGT: return 'Pseudo-Voigt profile' elif self is PeakProfileTypeEnum.SPLIT_PSEUDO_VOIGT: diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index b94a3b8e..a97ae016 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -196,6 +196,13 @@ def label(self) -> StringDescriptor: @label.setter def label(self, value: str) -> None: + """Set the atom site label. + + Parameters + ---------- + value : str + New label value. + """ self._label.value = value @property @@ -211,6 +218,13 @@ def type_symbol(self) -> StringDescriptor: @type_symbol.setter def type_symbol(self, value: str) -> None: + """Set the chemical symbol of the atom at this site. + + Parameters + ---------- + value : str + New chemical symbol value. + """ self._type_symbol.value = value @property @@ -227,6 +241,13 @@ def adp_type(self) -> StringDescriptor: @adp_type.setter def adp_type(self, value: str) -> None: + """Set the atomic displacement parameter type. + + Parameters + ---------- + value : str + New ADP type value (e.g., ``'Biso'``). + """ self._adp_type.value = value @property @@ -243,6 +264,13 @@ def wyckoff_letter(self) -> StringDescriptor: @wyckoff_letter.setter def wyckoff_letter(self, value: str) -> None: + """Set the Wyckoff letter for this atom site. + + Parameters + ---------- + value : str + New Wyckoff letter value. + """ self._wyckoff_letter.value = value @property @@ -257,6 +285,13 @@ def fract_x(self) -> Parameter: @fract_x.setter def fract_x(self, value: float) -> None: + """Set the fractional x-coordinate. + + Parameters + ---------- + value : float + New fractional x-coordinate value. + """ self._fract_x.value = value @property @@ -271,6 +306,13 @@ def fract_y(self) -> Parameter: @fract_y.setter def fract_y(self, value: float) -> None: + """Set the fractional y-coordinate. + + Parameters + ---------- + value : float + New fractional y-coordinate value. + """ self._fract_y.value = value @property @@ -285,6 +327,13 @@ def fract_z(self) -> Parameter: @fract_z.setter def fract_z(self, value: float) -> None: + """Set the fractional z-coordinate. + + Parameters + ---------- + value : float + New fractional z-coordinate value. + """ self._fract_z.value = value @property @@ -300,6 +349,13 @@ def occupancy(self) -> Parameter: @occupancy.setter def occupancy(self, value: float) -> None: + """Set the occupancy of this atom site. + + Parameters + ---------- + value : float + New occupancy value. + """ self._occupancy.value = value @property @@ -315,6 +371,13 @@ def b_iso(self) -> Parameter: @b_iso.setter def b_iso(self, value: float) -> None: + """Set the isotropic atomic displacement parameter. + + Parameters + ---------- + value : float + New B_iso value in Ų. + """ self._b_iso.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 1e63bf46..2f3c432a 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -163,6 +163,13 @@ def length_a(self) -> Parameter: @length_a.setter def length_a(self, value: float) -> None: + """Set the length of the a axis. + + Parameters + ---------- + value : float + New length value in Å. + """ self._length_a.value = value @property @@ -177,6 +184,13 @@ def length_b(self) -> Parameter: @length_b.setter def length_b(self, value: float) -> None: + """Set the length of the b axis. + + Parameters + ---------- + value : float + New length value in Å. + """ self._length_b.value = value @property @@ -191,6 +205,13 @@ def length_c(self) -> Parameter: @length_c.setter def length_c(self, value: float) -> None: + """Set the length of the c axis. + + Parameters + ---------- + value : float + New length value in Å. + """ self._length_c.value = value @property @@ -205,6 +226,13 @@ def angle_alpha(self) -> Parameter: @angle_alpha.setter def angle_alpha(self, value: float) -> None: + """Set the angle alpha (between b and c axes). + + Parameters + ---------- + value : float + New angle value in degrees. + """ self._angle_alpha.value = value @property @@ -219,6 +247,13 @@ def angle_beta(self) -> Parameter: @angle_beta.setter def angle_beta(self, value: float) -> None: + """Set the angle beta (between a and c axes). + + Parameters + ---------- + value : float + New angle value in degrees. + """ self._angle_beta.value = value @property @@ -233,4 +268,11 @@ def angle_gamma(self) -> Parameter: @angle_gamma.setter def angle_gamma(self, value: float) -> None: + """Set the angle gamma (between a and b axes). + + Parameters + ---------- + value : float + New angle value in degrees. + """ self._angle_gamma.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 623fbe39..1ca8820f 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -148,6 +148,13 @@ def name_h_m(self) -> StringDescriptor: @name_h_m.setter def name_h_m(self, value: str) -> None: + """Set the Hermann-Mauguin symbol and reset the coordinate code. + + Parameters + ---------- + value : str + New Hermann-Mauguin symbol. + """ self._name_h_m.value = value self._reset_it_coordinate_system_code() @@ -164,4 +171,11 @@ def it_coordinate_system_code(self) -> StringDescriptor: @it_coordinate_system_code.setter def it_coordinate_system_code(self, value: str) -> None: + """Set the IT coordinate system code. + + Parameters + ---------- + value : str + New IT coordinate system code. + """ self._it_coordinate_system_code.value = value diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index 52c48214..4e8a23ca 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -43,10 +43,25 @@ def _default_engine(cls) -> str: @property def engine(self) -> str: + """Return the name of the currently active rendering engine. + + Returns + ------- + str + Identifier of the active engine. + """ return self._engine @engine.setter def engine(self, new_engine: str) -> None: + """Switch to a different rendering engine. + + Parameters + ---------- + new_engine : str + Identifier of the engine to activate. Must be a key + returned by ``_factory()._registry()``. + """ if new_engine == self._engine: log.info(f"Engine is already set to '{new_engine}'. No change made.") return diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index 70d1a994..410ee693 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -35,6 +35,13 @@ def default(cls) -> 'TableEngineEnum': return cls.RICH def description(self) -> str: + """Return a human-readable description of this table engine. + + Returns + ------- + str + Description string for the current enum member. + """ if self is TableEngineEnum.RICH: return 'Console rendering with Rich' elif self is TableEngineEnum.PANDAS: diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index 6a677d7f..3da63697 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -243,6 +243,17 @@ def param_from_cif( block: gemmi.cif.Block, idx: int = 0, ) -> None: + """Populate a single descriptor from a CIF block. + + Parameters + ---------- + self : GenericDescriptorBase + The descriptor instance to populate. + block : gemmi.cif.Block + Parsed CIF block to read values from. + idx : int, default=0 + Row index used when the tag belongs to a loop. + """ found_values: list[Any] = [] # Try to find the value(s) from the CIF block iterating over @@ -294,6 +305,20 @@ def category_collection_from_cif( self: CategoryCollection, block: gemmi.cif.Block, ) -> None: + """Populate a CategoryCollection from a CIF loop. + + Parameters + ---------- + self : CategoryCollection + The collection instance to populate. + block : gemmi.cif.Block + Parsed CIF block to read the loop from. + + Raises + ------ + ValueError + If the collection has no ``_item_type`` defined. + """ # TODO: Find a better way and then remove TODO in the AtomSite # class # TODO: Rename to _item_cls? diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index bb6b991d..b226c979 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -82,6 +82,13 @@ def name(self) -> str: @property def full_name(self) -> str: + """Return the full project name (alias for :attr:`name`). + + Returns + ------- + str + The project name. + """ return self.name @property @@ -232,6 +239,19 @@ def plot_meas( x_max: float | None = None, x: object | None = None, ) -> None: + """Plot measured diffraction data for an experiment. + + Parameters + ---------- + expt_name : str + Name of the experiment to plot. + x_min : float | None, default=None + Lower bound for the x-axis range. + x_max : float | None, default=None + Upper bound for the x-axis range. + x : object | None, default=None + Optional explicit x-axis data to override stored values. + """ self._update_categories(expt_name) experiment = self.experiments[expt_name] @@ -251,6 +271,19 @@ def plot_calc( x_max: float | None = None, x: object | None = None, ) -> None: + """Plot calculated diffraction pattern for an experiment. + + Parameters + ---------- + expt_name : str + Name of the experiment to plot. + x_min : float | None, default=None + Lower bound for the x-axis range. + x_max : float | None, default=None + Upper bound for the x-axis range. + x : object | None, default=None + Optional explicit x-axis data to override stored values. + """ self._update_categories(expt_name) experiment = self.experiments[expt_name] @@ -271,6 +304,21 @@ def plot_meas_vs_calc( show_residual: bool = False, x: object | None = None, ) -> None: + """Plot measured vs calculated data for an experiment. + + Parameters + ---------- + expt_name : str + Name of the experiment to plot. + x_min : float | None, default=None + Lower bound for the x-axis range. + x_max : float | None, default=None + Upper bound for the x-axis range. + show_residual : bool, default=False + When ``True``, include the residual (difference) curve. + x : object | None, default=None + Optional explicit x-axis data to override stored values. + """ self._update_categories(expt_name) experiment = self.experiments[expt_name] diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py index 3e1859c0..a04c72d0 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_info.py @@ -39,6 +39,13 @@ def name(self) -> str: @name.setter def name(self, value: str) -> None: + """Set the project name. + + Parameters + ---------- + value : str + New project name. + """ self._name = value @property @@ -53,6 +60,13 @@ def title(self) -> str: @title.setter def title(self, value: str) -> None: + """Set the project title. + + Parameters + ---------- + value : str + New project title. + """ self._title = value @property @@ -62,6 +76,13 @@ def description(self) -> str: @description.setter def description(self, value: str) -> None: + """Set the project description (whitespace normalized). + + Parameters + ---------- + value : str + New description text. + """ self._description = ' '.join(value.split()) @property @@ -71,6 +92,13 @@ def path(self) -> pathlib.Path: @path.setter def path(self, value: object) -> None: + """Set the project directory path. + + Parameters + ---------- + value : object + New path as a :class:`str` or :class:`pathlib.Path`. + """ # Accept str or Path; normalize to Path self._path = pathlib.Path(value) diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index 45316d98..619b5f32 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -32,6 +32,7 @@ def __init__(self, project: object) -> None: # ------------------------------------------ def show_report(self) -> None: + """Print a full project report covering all sections.""" self.show_project_info() self.show_crystallographic_data() self.show_experimental_data() diff --git a/src/easydiffraction/utils/environment.py b/src/easydiffraction/utils/environment.py index a14aa1fd..c0deba61 100644 --- a/src/easydiffraction/utils/environment.py +++ b/src/easydiffraction/utils/environment.py @@ -9,10 +9,27 @@ def in_pytest() -> bool: + """ + Determine whether the code is running inside a pytest session. + + Returns + ------- + bool + True if pytest is loaded, False otherwise. + """ return 'pytest' in sys.modules def in_warp() -> bool: + """ + Determine whether the terminal is the Warp terminal emulator. + + Returns + ------- + bool + True if the TERM_PROGRAM environment variable equals + ``'WarpTerminal'``, False otherwise. + """ return os.getenv('TERM_PROGRAM') == 'WarpTerminal' diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index d40d69ae..84b409da 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -62,6 +62,19 @@ def __init__(self, *args: object, mode: str = 'compact', **kwargs: object) -> No self.mode = mode def get_level_text(self, record: logging.LogRecord) -> Text: + """ + Return an icon or level name for the log record. + + Parameters + ---------- + record : logging.LogRecord + The log record being rendered. + + Returns + ------- + Text + A Rich Text object with the level indicator. + """ if self.mode == 'compact': icon = self._icons.get(record.levelno, record.levelname) if in_warp() and not in_jupyter() and icon in ['⚠️', '⚙️', 'ℹ️']: @@ -72,9 +85,21 @@ def get_level_text(self, record: logging.LogRecord) -> Text: return super().get_level_text(record) def render_message(self, record: logging.LogRecord, message: str) -> Text: - # In compact mode, let the icon come from get_level_text and - # keep the message body unadorned. In verbose mode, defer to - # RichHandler. + """ + Render the log message body as a Rich Text object. + + Parameters + ---------- + record : logging.LogRecord + The log record being rendered. + message : str + Pre-formatted log message string. + + Returns + ------- + Text + A Rich Text object with the rendered message. + """ if self.mode == 'compact': try: return Text.from_markup(message) @@ -237,6 +262,7 @@ def aligned_excepthook( exc: BaseException, tb: 'TracebackType | None', ) -> None: + """Log the exception with full traceback via Rich.""" original_args = getattr(exc, 'args', tuple()) message = str(exc) with suppress(Exception): @@ -269,6 +295,7 @@ def compact_excepthook( exc: BaseException, _tb: 'TracebackType | None', ) -> None: + """Log the exception message and exit.""" logger.error(str(exc)) raise SystemExit(1) @@ -301,6 +328,7 @@ def _suppress_traceback(logger: object) -> object: """ def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: + """Log only the exception message, suppressing the traceback.""" try: _evalue = ( args[2] if len(args) > 2 else kwargs.get('_evalue') or kwargs.get('evalue') @@ -358,6 +386,7 @@ class Mode(Enum): @classmethod def default(cls) -> Logger.Mode: + """Return the default output mode (compact).""" return cls.COMPACT class Level(IntEnum): @@ -371,6 +400,7 @@ class Level(IntEnum): @classmethod def default(cls) -> Logger.Level: + """Return the default log level (WARNING).""" return cls.WARNING class Reaction(Enum): @@ -381,6 +411,7 @@ class Reaction(Enum): @classmethod def default(cls) -> Logger.Reaction: + """Return the default error reaction (RAISE).""" return cls.RAISE # --- Internal state --- @@ -457,14 +488,38 @@ def _install_jupyter_traceback_suppressor(cls) -> None: # ===== Helpers ===== @classmethod def set_mode(cls, mode: Mode) -> None: + """ + Set the output mode and reconfigure the logger. + + Parameters + ---------- + mode : Mode + The desired output mode (VERBOSE or COMPACT). + """ cls.configure(mode=mode, level=cls.Level(cls._logger.level)) @classmethod def set_level(cls, level: Level) -> None: + """ + Set the minimum log level and reconfigure the logger. + + Parameters + ---------- + level : Level + The desired minimum log level. + """ cls.configure(mode=cls._mode, level=level) @classmethod def mode(cls) -> Mode: + """ + Return the currently active output mode. + + Returns + ------- + Mode + The current Logger.Mode value. + """ return cls._mode @classmethod @@ -509,22 +564,68 @@ def handle( @classmethod def debug(cls, *messages: str) -> None: + """ + Log one or more messages at DEBUG level. + + Parameters + ---------- + *messages : str + Message parts joined with a space before logging. + """ cls.handle(*messages, level=cls.Level.DEBUG, exc_type=None) @classmethod def info(cls, *messages: str) -> None: + """ + Log one or more messages at INFO level. + + Parameters + ---------- + *messages : str + Message parts joined with a space before logging. + """ cls.handle(*messages, level=cls.Level.INFO, exc_type=None) @classmethod def warning(cls, *messages: str, exc_type: type[BaseException] | None = None) -> None: + """ + Log one or more messages at WARNING level. + + Parameters + ---------- + *messages : str + Message parts joined with a space before logging. + exc_type : type[BaseException] | None, default=None + If provided, raise this exception type instead of logging. + """ cls.handle(*messages, level=cls.Level.WARNING, exc_type=exc_type) @classmethod def error(cls, *messages: str, exc_type: type[BaseException] = AttributeError) -> None: + """ + Log one or more messages at ERROR level. + + Parameters + ---------- + *messages : str + Message parts joined with a space before logging. + exc_type : type[BaseException], default=AttributeError + Exception type to raise in VERBOSE/COMPACT mode. + """ cls.handle(*messages, level=cls.Level.ERROR, exc_type=exc_type) @classmethod def critical(cls, *messages: str, exc_type: type[BaseException] = RuntimeError) -> None: + """ + Log one or more messages at CRITICAL level. + + Parameters + ---------- + *messages : str + Message parts joined with a space before logging. + exc_type : type[BaseException], default=RuntimeError + Exception type to raise in VERBOSE/COMPACT mode. + """ cls.handle(*messages, level=cls.Level.CRITICAL, exc_type=exc_type) @@ -570,6 +671,15 @@ def print(cls, *objects: object, **kwargs: object) -> None: @classmethod def paragraph(cls, title: str) -> None: + """ + Print a bold blue paragraph heading. + + Parameters + ---------- + title : str + Heading text; substrings enclosed in single quotes are + rendered without the bold-blue style. + """ parts = re.split(r"('.*?')", title) text = Text() for part in parts: diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index e3cbaae8..9971a8ca 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -516,6 +516,22 @@ def render_table( columns_headers: object = None, display_handle: object = None, ) -> None: + """ + Render tabular data to the active display backend. + + Parameters + ---------- + columns_data : object + A list of rows, where each row is a list of cell values. + columns_alignment : object + A list of alignment strings (e.g. ``'left'``, ``'right'``, + ``'center'``) matching the number of columns. + columns_headers : object, default=None + Optional list of column header strings. + display_handle : object, default=None + Optional display handle for in-place updates (e.g. in Jupyter + or a terminal Live context). + """ headers = [ (col, align) for col, align in zip(columns_headers, columns_alignment, strict=False) ] From f2672190cf8a07e2f7fb74512e6213a37c0c2868 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 19:54:56 +0100 Subject: [PATCH 34/48] New combination of rules --- pixi.lock | 4 +- pyproject.toml | 9 ++- .../analysis/calculators/base.py | 4 +- .../analysis/categories/aliases/default.py | 2 - .../categories/constraints/default.py | 6 +- .../analysis/categories/fit_mode/fit_mode.py | 1 - .../joint_fit_experiments/default.py | 6 +- src/easydiffraction/core/category.py | 4 +- src/easydiffraction/core/datablock.py | 8 ++- .../categories/background/chebyshev.py | 4 +- .../categories/background/line_segment.py | 21 ++----- .../experiment/categories/data/bragg_pd.py | 19 +++--- .../experiment/categories/data/bragg_sc.py | 16 ++--- .../experiment/categories/data/total_pd.py | 14 ++--- .../categories/excluded_regions/default.py | 5 +- .../categories/experiment_type/default.py | 24 +++---- .../experiment/categories/extinction/shelx.py | 6 +- .../experiment/categories/instrument/cwl.py | 6 +- .../experiment/categories/instrument/tof.py | 9 +-- .../categories/linked_crystal/default.py | 6 +- .../categories/linked_phases/default.py | 4 +- .../experiment/categories/peak/cwl.py | 2 +- .../experiment/categories/peak/cwl_mixins.py | 42 +++++-------- .../experiment/categories/peak/tof.py | 2 +- .../experiment/categories/peak/tof_mixins.py | 50 +++++---------- .../categories/peak/total_mixins.py | 32 ++++------ .../datablocks/experiment/item/enums.py | 44 ++++++++----- .../categories/atom_sites/default.py | 63 ------------------- .../structure/categories/cell/default.py | 48 +------------- .../categories/space_group/default.py | 14 ----- src/easydiffraction/display/base.py | 6 +- src/easydiffraction/display/tables.py | 3 +- src/easydiffraction/io/cif/serialize.py | 6 +- src/easydiffraction/project/project.py | 12 ++-- src/easydiffraction/project/project_info.py | 12 ++-- src/easydiffraction/utils/logging.py | 4 +- src/easydiffraction/utils/utils.py | 4 +- tools/find_missing_docstrings.py | 44 +++++++++++++ 38 files changed, 220 insertions(+), 346 deletions(-) create mode 100644 tools/find_missing_docstrings.py diff --git a/pixi.lock b/pixi.lock index 014de013..9bca01f3 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty34 - sha256: b95efc28a0e9f68c9b10a008149288064f945ddfea0080ba6d16a487d37f1d28 + version: 0.10.2+devdirty37 + sha256: 720a9d523687c2effefefbc159a74223d0746814d2fb351030677345ef64bdce requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index 649b08f6..d31ae21f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -216,6 +216,7 @@ quote-style = 'single' # But double quotes in docstrings (PEP 8, PEP 25 select = [ # Various rules #'C90', # https://docs.astral.sh/ruff/rules/#mccabe-c90 + 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d 'F', # https://docs.astral.sh/ruff/rules/#pyflakes-f 'FLY', # https://docs.astral.sh/ruff/rules/#flynt-fly #'FURB', # https://docs.astral.sh/ruff/rules/#refurb-furb @@ -275,10 +276,10 @@ select = [ # Ignore specific rules globally ignore = [ 'COM812', # https://docs.astral.sh/ruff/rules/missing-trailing-comma/ - # The following is replaced by [tool.pydoclint] - 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d + # The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint] 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc # Temporary: + #'D205', 'DTZ005', ] @@ -289,6 +290,7 @@ ignore = [ ] 'tests/**' = [ 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'S101', # https://docs.astral.sh/ruff/rules/assert/ @@ -341,6 +343,9 @@ max-complexity = 10 max-line-length = 99 # See also `line-length` in [tool.ruff] max-doc-length = 72 +[tool.ruff.lint.pydocstyle] +convention = 'numpy' + ############################# # Configuration for pydoclint ############################# diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index 5dceb01c..14d76977 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -23,7 +23,9 @@ def name(self) -> str: @property @abstractmethod def engine_imported(self) -> bool: - """Whether the underlying calculation library could be imported.""" + """ + Whether the underlying calculation library could be imported. + """ pass @abstractmethod diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index a39e17ef..63cc5f28 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -69,7 +69,6 @@ def label(self) -> StringDescriptor: @label.setter def label(self, value: str) -> None: - """Set the alias label.""" self._label.value = value @property @@ -85,7 +84,6 @@ def param_uid(self) -> StringDescriptor: @param_uid.setter def param_uid(self, value: str) -> None: - """Set the parameter UID.""" self._param_uid.value = value diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 509049bf..71d61d95 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -21,9 +21,7 @@ class Constraint(CategoryItem): - """ - Single constraint item. - """ + """Single constraint item.""" def __init__(self) -> None: super().__init__() @@ -67,7 +65,6 @@ def lhs_alias(self) -> StringDescriptor: @lhs_alias.setter def lhs_alias(self, value: str) -> None: - """Set the left-hand side alias string.""" self._lhs_alias.value = value @property @@ -83,7 +80,6 @@ def rhs_expr(self) -> StringDescriptor: @rhs_expr.setter def rhs_expr(self, value: str) -> None: - """Set the right-hand side expression string.""" self._rhs_expr.value = value diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py index 302dcf3c..b0589a2e 100644 --- a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py +++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py @@ -61,5 +61,4 @@ def mode(self) -> StringDescriptor: @mode.setter def mode(self, value: str) -> None: - """Set the fitting strategy value.""" self._mode.value = value diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py index 9b69f172..b94bd7de 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py @@ -24,9 +24,7 @@ class JointFitExperiment(CategoryItem): - """ - A single joint-fit entry. - """ + """A single joint-fit entry.""" def __init__(self) -> None: super().__init__() @@ -70,7 +68,6 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: - """Set the experiment identifier.""" self._id.value = value @property @@ -86,7 +83,6 @@ def weight(self) -> NumericDescriptor: @weight.setter def weight(self, value: float) -> None: - """Set the experiment weight factor.""" self._weight.value = value diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index 5ad10739..e70ec913 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -202,7 +202,9 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def unique_name(self) -> str | None: - """Return None; collections do not carry their own unique name.""" + """ + Return None; collections do not carry their own unique name. + """ return None @property diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 02241e91..677563a9 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -158,7 +158,9 @@ def __str__(self) -> str: @property def unique_name(self) -> str | None: - """Return None; collections do not carry their own unique name.""" + """ + Return None; collections do not carry their own unique name. + """ return None @property @@ -171,7 +173,9 @@ def parameters(self) -> list: @property def fittable_parameters(self) -> list: - """All non-constrained Parameter instances in this collection.""" + """ + All non-constrained Parameter instances in this collection. + """ return [p for p in self.parameters if isinstance(p, Parameter) and not p.constrained] @property diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 0c8af42c..3ed8e332 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -49,7 +49,7 @@ def __init__(self) -> None: self._id = StringDescriptor( name='id', - description='Identifier for this background polynomial term.', + description='Identifier for this background polynomial term', value_spec=AttributeSpec( default='0', # TODO: the following pattern is valid for dict key @@ -98,7 +98,6 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: - """Set the polynomial term identifier.""" self._id.value = value """ Order used in a Chebyshev polynomial background term. @@ -126,7 +125,6 @@ def coef(self) -> Parameter: @coef.setter def coef(self, value: float) -> None: - """Set the polynomial coefficient.""" self._coef.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index da8e3916..4573272f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -41,7 +41,7 @@ def __init__(self) -> None: self._id = StringDescriptor( name='id', - description='Identifier for this background line segment.', + description='Identifier for this background line segment', value_spec=AttributeSpec( default='0', # TODO: the following pattern is valid for dict key @@ -53,10 +53,7 @@ def __init__(self) -> None: ) self._x = NumericDescriptor( name='x', - description=( - 'X-coordinates used to create many straight-line segments ' - 'representing the background in a calculated diffractogram.' - ), + description='X-coordinates used to create many straight-line segments', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(), @@ -70,10 +67,7 @@ def __init__(self) -> None: ) self._y = Parameter( name='y', # TODO: rename to intensity - description=( - 'Intensity used to create many straight-line segments ' - 'representing the background in a calculated diffractogram' - ), + description='Intensity used to create many straight-line segments', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(), @@ -106,14 +100,12 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: - """Set the line segment identifier.""" self._id.value = value @property def x(self) -> NumericDescriptor: """ - X-coordinates used to create many straight-line segments - representing the background in a calculated diffractogram. + X-coordinates used to create many straight-line segments. Reading this property returns the underlying ``NumericDescriptor`` object. Assigning to it updates the @@ -123,14 +115,12 @@ def x(self) -> NumericDescriptor: @x.setter def x(self, value: float) -> None: - """Set the x-coordinate of the control point.""" self._x.value = value @property def y(self) -> Parameter: """ - Intensity used to create many straight-line segments - representing the background in a calculated diffractogram. + Intensity used to create many straight-line segments. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -139,7 +129,6 @@ def y(self) -> Parameter: @y.setter def y(self, value: float) -> None: - """Set the intensity of the control point.""" self._y.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 95d32cb7..1978608c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -34,7 +34,7 @@ def __init__(self) -> None: self._point_id = StringDescriptor( name='point_id', - description='Identifier for this data point in the dataset.', + description='Identifier for this data point in the dataset', value_spec=AttributeSpec( default='0', # TODO: the following pattern is valid for dict key @@ -50,7 +50,7 @@ def __init__(self) -> None: ) self._d_spacing = NumericDescriptor( name='d_spacing', - description='d-spacing value corresponding to this data point.', + description='d-spacing value corresponding to this data point', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(ge=0), @@ -73,7 +73,7 @@ def __init__(self) -> None: ) self._intensity_meas_su = NumericDescriptor( name='intensity_meas_su', - description='Standard uncertainty of the measured intensity at this data point.', + description='Standard uncertainty of the measured intensity at this point', value_spec=AttributeSpec( default=1.0, validator=RangeValidator(ge=0), @@ -87,7 +87,7 @@ def __init__(self) -> None: ) self._intensity_calc = NumericDescriptor( name='intensity_calc', - description='Intensity value for a computed diffractogram at this data point.', + description='Intensity of a computed diffractogram at this point', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(ge=0), @@ -96,7 +96,7 @@ def __init__(self) -> None: ) self._intensity_bkg = NumericDescriptor( name='intensity_bkg', - description='Intensity value for a computed background at this data point.', + description='Intensity of a computed background at this point', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(ge=0), @@ -105,7 +105,7 @@ def __init__(self) -> None: ) self._calc_status = StringDescriptor( name='calc_status', - description='Status code of the data point in the calculation process.', + description='Status code of the data point in the calculation process', value_spec=AttributeSpec( default='incl', # TODO: Make Enum validator=MembershipValidator(allowed=['incl', 'excl']), @@ -155,8 +155,7 @@ def intensity_meas(self) -> NumericDescriptor: @property def intensity_meas_su(self) -> NumericDescriptor: """ - Standard uncertainty of the measured intensity at this data - point. + Standard uncertainty of the measured intensity at this point. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -166,7 +165,7 @@ def intensity_meas_su(self) -> NumericDescriptor: @property def intensity_calc(self) -> NumericDescriptor: """ - Intensity value for a computed diffractogram at this data point. + Intensity of a computed diffractogram at this point. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -176,7 +175,7 @@ def intensity_calc(self) -> NumericDescriptor: @property def intensity_bkg(self) -> NumericDescriptor: """ - Intensity value for a computed background at this data point. + Intensity of a computed background at this point. Reading this property returns the underlying ``NumericDescriptor`` object. diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 816b81a4..47834bd6 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -35,7 +35,7 @@ def __init__(self) -> None: self._id = StringDescriptor( name='id', - description='Identifier of the reflection.', + description='Identifier of the reflection', value_spec=AttributeSpec( default='0', # TODO: the following pattern is valid for dict key @@ -47,7 +47,7 @@ def __init__(self) -> None: ) self._d_spacing = NumericDescriptor( name='d_spacing', - description='The distance between lattice planes in the crystal for this reflection.', + description='The distance between lattice planes in the crystal for this reflection', units='Å', value_spec=AttributeSpec( default=0.0, @@ -57,7 +57,7 @@ def __init__(self) -> None: ) self._sin_theta_over_lambda = NumericDescriptor( name='sin_theta_over_lambda', - description='The sin(θ)/λ value for this reflection.', + description='The sin(θ)/λ value for this reflection', units='Å⁻¹', value_spec=AttributeSpec( default=0.0, @@ -67,7 +67,7 @@ def __init__(self) -> None: ) self._index_h = NumericDescriptor( name='index_h', - description='Miller index h of a measured reflection.', + description='Miller index h of a measured reflection', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(), @@ -76,7 +76,7 @@ def __init__(self) -> None: ) self._index_k = NumericDescriptor( name='index_k', - description='Miller index k of a measured reflection.', + description='Miller index k of a measured reflection', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(), @@ -85,7 +85,7 @@ def __init__(self) -> None: ) self._index_l = NumericDescriptor( name='index_l', - description='Miller index l of a measured reflection.', + description='Miller index l of a measured reflection', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(), @@ -426,7 +426,9 @@ def intensity_meas_su(self) -> np.ndarray: @property def intensity_calc(self) -> np.ndarray: - """Calculated structure-factor intensities for all reflections.""" + """ + Calculated structure-factor intensities for all reflections. + """ return np.fromiter( (p.intensity_calc.value for p in self._items), dtype=float, diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index a53ae060..d4ab006e 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -38,7 +38,7 @@ def __init__(self) -> None: self._point_id = StringDescriptor( name='point_id', - description='Identifier for this data point in the dataset.', + description='Identifier for this data point in the dataset', value_spec=AttributeSpec( default='0', validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'), @@ -51,7 +51,7 @@ def __init__(self) -> None: ) self._r = NumericDescriptor( name='r', - description='Interatomic distance in real space.', + description='Interatomic distance in real space', units='Å', value_spec=AttributeSpec( default=0.0, @@ -65,7 +65,7 @@ def __init__(self) -> None: ) self._g_r_meas = NumericDescriptor( name='g_r_meas', - description='Measured pair distribution function G(r).', + description='Measured pair distribution function G(r)', value_spec=AttributeSpec( default=0.0, ), @@ -77,7 +77,7 @@ def __init__(self) -> None: ) self._g_r_meas_su = NumericDescriptor( name='g_r_meas_su', - description='Standard uncertainty of measured G(r).', + description='Standard uncertainty of measured G(r)', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(ge=0), @@ -90,7 +90,7 @@ def __init__(self) -> None: ) self._g_r_calc = NumericDescriptor( name='g_r_calc', - description='Calculated pair distribution function G(r).', + description='Calculated pair distribution function G(r)', value_spec=AttributeSpec( default=0.0, ), @@ -102,7 +102,7 @@ def __init__(self) -> None: ) self._calc_status = StringDescriptor( name='calc_status', - description='Status code of the data point in calculation.', + description='Status code of the data point in calculation', value_spec=AttributeSpec( default='incl', validator=MembershipValidator(allowed=['incl', 'excl']), @@ -321,7 +321,7 @@ class TotalData(TotalDataBase): transformed to r-space. """ - type_info = TypeInfo(tag='total-pd', description='Total scattering (PDF) data') + type_info = TypeInfo(tag='total-pd', description='Total scattering (PDF) data',) compatibility = Compatibility( sample_form=frozenset({SampleFormEnum.POWDER}), scattering_type=frozenset({ScatteringTypeEnum.TOTAL}), diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index 7cc40287..fdf130c2 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -35,7 +35,7 @@ def __init__(self) -> None: # TODO: Add point_id as for the background self._id = StringDescriptor( name='id', - description='Identifier for this excluded region.', + description='Identifier for this excluded region', value_spec=AttributeSpec( default='0', # TODO: the following pattern is valid for dict key @@ -83,7 +83,6 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: - """Set the excluded-region identifier.""" self._id.value = value @property @@ -99,7 +98,6 @@ def start(self) -> NumericDescriptor: @start.setter def start(self, value: float) -> None: - """Set the start of the excluded region.""" self._start.value = value @property @@ -115,7 +113,6 @@ def end(self) -> NumericDescriptor: @end.setter def end(self, value: float) -> None: - """Set the end of the excluded region.""" self._end.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index ce7ebe3f..0ace392f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -41,8 +41,7 @@ def __init__(self) -> None: self._sample_form = StringDescriptor( name='sample_form', - description='Specifies whether the diffraction data corresponds to ' - 'powder diffraction or single crystal diffraction', + description='Powder diffraction or single crystal diffraction', value_spec=AttributeSpec( default=SampleFormEnum.default().value, validator=MembershipValidator(allowed=[member.value for member in SampleFormEnum]), @@ -52,8 +51,7 @@ def __init__(self) -> None: self._beam_mode = StringDescriptor( name='beam_mode', - description='Defines whether the measurement is performed with a ' - 'constant wavelength (CW) or time-of-flight (TOF) method', + description='Constant wavelength (CW) or time-of-flight (TOF) measurement', value_spec=AttributeSpec( default=BeamModeEnum.default().value, validator=MembershipValidator(allowed=[member.value for member in BeamModeEnum]), @@ -62,7 +60,7 @@ def __init__(self) -> None: ) self._radiation_probe = StringDescriptor( name='radiation_probe', - description='Specifies whether the measurement uses neutrons or X-rays', + description='Neutron or X-ray diffraction measurement', value_spec=AttributeSpec( default=RadiationProbeEnum.default().value, validator=MembershipValidator( @@ -73,9 +71,7 @@ def __init__(self) -> None: ) self._scattering_type = StringDescriptor( name='scattering_type', - description='Specifies whether the experiment uses Bragg scattering ' - '(for conventional structure refinement) or total scattering ' - '(for pair distribution function analysis - PDF)', + description='Conventional Bragg diffraction or total scattering (PDF)', value_spec=AttributeSpec( default=ScatteringTypeEnum.default().value, validator=MembershipValidator( @@ -110,8 +106,7 @@ def _set_scattering_type(self, value: str) -> None: @property def sample_form(self) -> StringDescriptor: """ - Specifies whether the diffraction data corresponds to powder - diffraction or single crystal diffraction. + Powder diffraction or single crystal diffraction. Reading this property returns the underlying ``StringDescriptor`` object. @@ -121,8 +116,7 @@ def sample_form(self) -> StringDescriptor: @property def beam_mode(self) -> StringDescriptor: """ - Defines whether the measurement is performed with a constant - wavelength (CW) or time-of-flight (TOF) method. + Constant wavelength (CW) or time-of-flight (TOF) measurement. Reading this property returns the underlying ``StringDescriptor`` object. @@ -132,7 +126,7 @@ def beam_mode(self) -> StringDescriptor: @property def radiation_probe(self) -> StringDescriptor: """ - Specifies whether the measurement uses neutrons or X-rays. + Neutron or X-ray diffraction measurement. Reading this property returns the underlying ``StringDescriptor`` object. @@ -142,9 +136,7 @@ def radiation_probe(self) -> StringDescriptor: @property def scattering_type(self) -> StringDescriptor: """ - Specifies whether the experiment uses Bragg scattering (for - conventional structure refinement) or total scattering (for pair - distribution function analysis - PDF). + Conventional Bragg diffraction or total scattering (PDF). Reading this property returns the underlying ``StringDescriptor`` object. diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index dab297ce..f6731bb2 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -34,7 +34,7 @@ def __init__(self) -> None: self._mosaicity = Parameter( name='mosaicity', - description='Mosaicity value for extinction correction.', + description='Mosaicity value for extinction correction', units='deg', value_spec=AttributeSpec( default=1.0, @@ -48,7 +48,7 @@ def __init__(self) -> None: ) self._radius = Parameter( name='radius', - description='Crystal radius for extinction correction.', + description='Crystal radius for extinction correction', units='µm', value_spec=AttributeSpec( default=1.0, @@ -79,7 +79,6 @@ def mosaicity(self) -> Parameter: @mosaicity.setter def mosaicity(self, value: float) -> None: - """Set the mosaicity value.""" self._mosaicity.value = value @property @@ -94,5 +93,4 @@ def radius(self) -> Parameter: @radius.setter def radius(self, value: float) -> None: - """Set the crystal radius.""" self._radius.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 52b34d51..7b2f799e 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -47,13 +47,12 @@ def setup_wavelength(self) -> Parameter: @setup_wavelength.setter def setup_wavelength(self, value: float) -> None: - """Set the incident wavelength.""" self._setup_wavelength.value = value @InstrumentFactory.register class CwlScInstrument(CwlInstrumentBase): - type_info = TypeInfo(tag='cwl-sc', description='CW single-crystal diffractometer') + type_info = TypeInfo(tag='cwl-sc', description='CW single-crystal diffractometer',) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}), @@ -69,7 +68,7 @@ def __init__(self) -> None: @InstrumentFactory.register class CwlPdInstrument(CwlInstrumentBase): - type_info = TypeInfo(tag='cwl-pd', description='CW powder diffractometer') + type_info = TypeInfo(tag='cwl-pd', description='CW powder diffractometer',) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG, ScatteringTypeEnum.TOTAL}), beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}), @@ -113,5 +112,4 @@ def calib_twotheta_offset(self) -> Parameter: @calib_twotheta_offset.setter def calib_twotheta_offset(self, value: float) -> None: - """Set the 2θ instrument misalignment offset.""" self._calib_twotheta_offset.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index 7beb7bc8..faa791c5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -18,7 +18,7 @@ @InstrumentFactory.register class TofScInstrument(InstrumentBase): - type_info = TypeInfo(tag='tof-sc', description='TOF single-crystal diffractometer') + type_info = TypeInfo(tag='tof-sc', description='TOF single-crystal diffractometer',) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}), @@ -34,7 +34,7 @@ def __init__(self) -> None: @InstrumentFactory.register class TofPdInstrument(InstrumentBase): - type_info = TypeInfo(tag='tof-pd', description='TOF powder diffractometer') + type_info = TypeInfo(tag='tof-pd', description='TOF powder diffractometer',) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}), @@ -110,7 +110,6 @@ def setup_twotheta_bank(self) -> Parameter: @setup_twotheta_bank.setter def setup_twotheta_bank(self, value: float) -> None: - """Set the detector bank 2θ position.""" self._setup_twotheta_bank.value = value @property @@ -125,7 +124,6 @@ def calib_d_to_tof_offset(self) -> Parameter: @calib_d_to_tof_offset.setter def calib_d_to_tof_offset(self, value: float) -> None: - """Set the TOF calibration offset.""" self._calib_d_to_tof_offset.value = value @property @@ -140,7 +138,6 @@ def calib_d_to_tof_linear(self) -> Parameter: @calib_d_to_tof_linear.setter def calib_d_to_tof_linear(self, value: float) -> None: - """Set the TOF linear calibration coefficient.""" self._calib_d_to_tof_linear.value = value @property @@ -155,7 +152,6 @@ def calib_d_to_tof_quad(self) -> Parameter: @calib_d_to_tof_quad.setter def calib_d_to_tof_quad(self, value: float) -> None: - """Set the TOF quadratic calibration coefficient.""" self._calib_d_to_tof_quad.value = value @property @@ -170,5 +166,4 @@ def calib_d_to_tof_recip(self) -> Parameter: @calib_d_to_tof_recip.setter def calib_d_to_tof_recip(self, value: float) -> None: - """Set the TOF reciprocal velocity calibration coefficient.""" self._calib_d_to_tof_recip.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index c49d8f54..f7b6efa3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -39,7 +39,7 @@ def __init__(self) -> None: self._id = StringDescriptor( name='id', - description='Identifier of the linked crystal.', + description='Identifier of the linked crystal', value_spec=AttributeSpec( default='Si', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), @@ -48,7 +48,7 @@ def __init__(self) -> None: ) self._scale = Parameter( name='scale', - description='Scale factor of the linked crystal.', + description='Scale factor of the linked crystal', value_spec=AttributeSpec( default=1.0, validator=RangeValidator(), @@ -75,7 +75,6 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: - """Set the linked-crystal identifier.""" self._id.value = value @property @@ -90,5 +89,4 @@ def scale(self) -> Parameter: @scale.setter def scale(self, value: float) -> None: - """Set the linked-crystal scale factor.""" self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py index 0039c194..97d03c66 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py @@ -28,7 +28,7 @@ def __init__(self) -> None: self._id = StringDescriptor( name='id', - description='Identifier of the linked phase.', + description='Identifier of the linked phase', value_spec=AttributeSpec( default='Si', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), @@ -65,7 +65,6 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: - """Set the linked-phase identifier.""" self._id.value = value @property @@ -80,7 +79,6 @@ def scale(self) -> Parameter: @scale.setter def scale(self, value: float) -> None: - """Set the linked-phase scale factor.""" self._scale.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py index 4bb036eb..dc3dbbd6 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py @@ -24,7 +24,7 @@ class CwlPseudoVoigt( ): """Constant-wavelength pseudo-Voigt peak shape.""" - type_info = TypeInfo(tag='pseudo-voigt', description='Pseudo-Voigt profile') + type_info = TypeInfo(tag='pseudo-voigt', description='Pseudo-Voigt profile',) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}), diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index 76de5ad1..f6d7ad11 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -22,8 +22,7 @@ def __init__(self) -> None: self._broad_gauss_u: Parameter = Parameter( name='broad_gauss_u', - description='Gaussian broadening coefficient (dependent on ' - 'sample size and instrument resolution)', + description='Gaussian broadening (sample size and instrument resolution)', units='deg²', value_spec=AttributeSpec( default=0.01, @@ -33,7 +32,7 @@ def __init__(self) -> None: ) self._broad_gauss_v: Parameter = Parameter( name='broad_gauss_v', - description='Gaussian broadening coefficient (instrumental broadening contribution)', + description='Gaussian broadening (instrumental broadening contribution)', units='deg²', value_spec=AttributeSpec( default=-0.01, @@ -43,7 +42,7 @@ def __init__(self) -> None: ) self._broad_gauss_w: Parameter = Parameter( name='broad_gauss_w', - description='Gaussian broadening coefficient (instrumental broadening contribution)', + description='Gaussian broadening (instrumental broadening contribution)', units='deg²', value_spec=AttributeSpec( default=0.02, @@ -53,7 +52,7 @@ def __init__(self) -> None: ) self._broad_lorentz_x: Parameter = Parameter( name='broad_lorentz_x', - description='Lorentzian broadening coefficient (dependent on sample strain effects)', + description='Lorentzian broadening (sample strain effects)', units='deg', value_spec=AttributeSpec( default=0.0, @@ -63,8 +62,7 @@ def __init__(self) -> None: ) self._broad_lorentz_y: Parameter = Parameter( name='broad_lorentz_y', - description='Lorentzian broadening coefficient (dependent on ' - 'microstructural defects and strain)', + description='Lorentzian broadening (microstructural defects and strain)', units='deg', value_spec=AttributeSpec( default=0.0, @@ -80,8 +78,8 @@ def __init__(self) -> None: @property def broad_gauss_u(self) -> Parameter: """ - Gaussian broadening coefficient (dependent on sample size and - instrument resolution) (deg²). + Gaussian broadening (sample size and instrument resolution) + (deg²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -90,14 +88,13 @@ def broad_gauss_u(self) -> Parameter: @broad_gauss_u.setter def broad_gauss_u(self, value: float) -> None: - """Set the Gaussian broadening coefficient U.""" self._broad_gauss_u.value = value @property def broad_gauss_v(self) -> Parameter: """ - Gaussian broadening coefficient (instrumental broadening - contribution) (deg²). + Gaussian broadening (instrumental broadening contribution) + (deg²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -106,14 +103,13 @@ def broad_gauss_v(self) -> Parameter: @broad_gauss_v.setter def broad_gauss_v(self, value: float) -> None: - """Set the Gaussian broadening coefficient V.""" self._broad_gauss_v.value = value @property def broad_gauss_w(self) -> Parameter: """ - Gaussian broadening coefficient (instrumental broadening - contribution) (deg²). + Gaussian broadening (instrumental broadening contribution) + (deg²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -122,14 +118,12 @@ def broad_gauss_w(self) -> Parameter: @broad_gauss_w.setter def broad_gauss_w(self, value: float) -> None: - """Set the Gaussian broadening coefficient W.""" self._broad_gauss_w.value = value @property def broad_lorentz_x(self) -> Parameter: """ - Lorentzian broadening coefficient (dependent on sample strain - effects) (deg). + Lorentzian broadening (sample strain effects) (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -138,14 +132,13 @@ def broad_lorentz_x(self) -> Parameter: @broad_lorentz_x.setter def broad_lorentz_x(self, value: float) -> None: - """Set the Lorentzian broadening coefficient X.""" self._broad_lorentz_x.value = value @property def broad_lorentz_y(self) -> Parameter: """ - Lorentzian broadening coefficient (dependent on microstructural - defects and strain) (deg). + Lorentzian broadening (microstructural defects and strain) + (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -154,7 +147,6 @@ def broad_lorentz_y(self) -> Parameter: @broad_lorentz_y.setter def broad_lorentz_y(self, value: float) -> None: - """Set the Lorentzian broadening coefficient Y.""" self._broad_lorentz_y.value = value @@ -221,7 +213,6 @@ def asym_empir_1(self) -> Parameter: @asym_empir_1.setter def asym_empir_1(self, value: float) -> None: - """Set empirical asymmetry coefficient p1.""" self._asym_empir_1.value = value @property @@ -236,7 +227,6 @@ def asym_empir_2(self) -> Parameter: @asym_empir_2.setter def asym_empir_2(self, value: float) -> None: - """Set empirical asymmetry coefficient p2.""" self._asym_empir_2.value = value @property @@ -251,7 +241,6 @@ def asym_empir_3(self) -> Parameter: @asym_empir_3.setter def asym_empir_3(self, value: float) -> None: - """Set empirical asymmetry coefficient p3.""" self._asym_empir_3.value = value @property @@ -266,7 +255,6 @@ def asym_empir_4(self) -> Parameter: @asym_empir_4.setter def asym_empir_4(self, value: float) -> None: - """Set empirical asymmetry coefficient p4.""" self._asym_empir_4.value = value @@ -313,7 +301,6 @@ def asym_fcj_1(self) -> Parameter: @asym_fcj_1.setter def asym_fcj_1(self, value: float) -> None: - """Set FCJ asymmetry parameter 1.""" self._asym_fcj_1.value = value @property @@ -328,5 +315,4 @@ def asym_fcj_2(self) -> Parameter: @asym_fcj_2.setter def asym_fcj_2(self, value: float) -> None: - """Set FCJ asymmetry parameter 2.""" self._asym_fcj_2.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py index 2ee2a579..39ddc556 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py @@ -23,7 +23,7 @@ class TofPseudoVoigt( ): """Time-of-flight pseudo-Voigt peak shape.""" - type_info = TypeInfo(tag='tof-pseudo-voigt', description='TOF pseudo-Voigt profile') + type_info = TypeInfo(tag='tof-pseudo-voigt', description='TOF pseudo-Voigt profile',) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}), diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index 6960b022..8093d877 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -24,7 +24,7 @@ def __init__(self) -> None: self._broad_gauss_sigma_0 = Parameter( name='gauss_sigma_0', - description='Gaussian broadening coefficient (instrumental resolution)', + description='Gaussian broadening (instrumental resolution)', units='µs²', value_spec=AttributeSpec( default=0.0, @@ -34,7 +34,7 @@ def __init__(self) -> None: ) self._broad_gauss_sigma_1 = Parameter( name='gauss_sigma_1', - description='Gaussian broadening coefficient (dependent on d-spacing)', + description='Gaussian broadening (dependent on d-spacing)', units='µs/Å', value_spec=AttributeSpec( default=0.0, @@ -44,7 +44,7 @@ def __init__(self) -> None: ) self._broad_gauss_sigma_2 = Parameter( name='gauss_sigma_2', - description='Gaussian broadening coefficient (instrument-dependent term)', + description='Gaussian broadening (instrument-dependent term)', units='µs²/Ų', value_spec=AttributeSpec( default=0.0, @@ -54,7 +54,7 @@ def __init__(self) -> None: ) self._broad_lorentz_gamma_0 = Parameter( name='lorentz_gamma_0', - description='Lorentzian broadening coefficient (dependent on microstrain effects)', + description='Lorentzian broadening (microstrain effects)', units='µs', value_spec=AttributeSpec( default=0.0, @@ -64,7 +64,7 @@ def __init__(self) -> None: ) self._broad_lorentz_gamma_1 = Parameter( name='lorentz_gamma_1', - description='Lorentzian broadening coefficient (dependent on d-spacing)', + description='Lorentzian broadening (dependent on d-spacing)', units='µs/Å', value_spec=AttributeSpec( default=0.0, @@ -74,7 +74,7 @@ def __init__(self) -> None: ) self._broad_lorentz_gamma_2 = Parameter( name='lorentz_gamma_2', - description='Lorentzian broadening coefficient (instrument-dependent term)', + description='Lorentzian broadening (instrument-dependent term)', units='µs²/Ų', value_spec=AttributeSpec( default=0.0, @@ -84,8 +84,7 @@ def __init__(self) -> None: ) self._broad_mix_beta_0 = Parameter( name='mix_beta_0', - description='Mixing parameter. Defines the ratio of Gaussian ' - 'to Lorentzian contributions in TOF profiles', + description='Ratio of Gaussian to Lorentzian contributions', units='deg', value_spec=AttributeSpec( default=0.0, @@ -95,8 +94,7 @@ def __init__(self) -> None: ) self._broad_mix_beta_1 = Parameter( name='mix_beta_1', - description='Mixing parameter. Defines the ratio of Gaussian ' - 'to Lorentzian contributions in TOF profiles', + description='Ratio of Gaussian to Lorentzian contributions', units='deg', value_spec=AttributeSpec( default=0.0, @@ -112,7 +110,7 @@ def __init__(self) -> None: @property def broad_gauss_sigma_0(self) -> Parameter: """ - Gaussian broadening coefficient (instrumental resolution) (µs²). + Gaussian broadening (instrumental resolution) (µs²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -121,13 +119,12 @@ def broad_gauss_sigma_0(self) -> Parameter: @broad_gauss_sigma_0.setter def broad_gauss_sigma_0(self, value: float) -> None: - """Set Gaussian broadening coefficient σ₀.""" self._broad_gauss_sigma_0.value = value @property def broad_gauss_sigma_1(self) -> Parameter: """ - Gaussian broadening coefficient (dependent on d-spacing) (µs/Å). + Gaussian broadening (dependent on d-spacing) (µs/Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -136,14 +133,12 @@ def broad_gauss_sigma_1(self) -> Parameter: @broad_gauss_sigma_1.setter def broad_gauss_sigma_1(self, value: float) -> None: - """Set Gaussian broadening coefficient σ₁.""" self._broad_gauss_sigma_1.value = value @property def broad_gauss_sigma_2(self) -> Parameter: """ - Gaussian broadening coefficient (instrument-dependent term) - (µs²/Ų). + Gaussian broadening (instrument-dependent term) (µs²/Ų). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -152,14 +147,12 @@ def broad_gauss_sigma_2(self) -> Parameter: @broad_gauss_sigma_2.setter def broad_gauss_sigma_2(self, value: float) -> None: - """Set Gaussian broadening coefficient σ₂.""" self._broad_gauss_sigma_2.value = value @property def broad_lorentz_gamma_0(self) -> Parameter: """ - Lorentzian broadening coefficient (dependent on microstrain - effects) (µs). + Lorentzian broadening (microstrain effects) (µs). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -168,14 +161,12 @@ def broad_lorentz_gamma_0(self) -> Parameter: @broad_lorentz_gamma_0.setter def broad_lorentz_gamma_0(self, value: float) -> None: - """Set Lorentzian broadening coefficient γ₀.""" self._broad_lorentz_gamma_0.value = value @property def broad_lorentz_gamma_1(self) -> Parameter: """ - Lorentzian broadening coefficient (dependent on d-spacing) - (µs/Å). + Lorentzian broadening (dependent on d-spacing) (µs/Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -184,14 +175,12 @@ def broad_lorentz_gamma_1(self) -> Parameter: @broad_lorentz_gamma_1.setter def broad_lorentz_gamma_1(self, value: float) -> None: - """Set Lorentzian broadening coefficient γ₁.""" self._broad_lorentz_gamma_1.value = value @property def broad_lorentz_gamma_2(self) -> Parameter: """ - Lorentzian broadening coefficient (instrument-dependent term) - (µs²/Ų). + Lorentzian broadening (instrument-dependent term) (µs²/Ų). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -200,14 +189,12 @@ def broad_lorentz_gamma_2(self) -> Parameter: @broad_lorentz_gamma_2.setter def broad_lorentz_gamma_2(self, value: float) -> None: - """Set Lorentzian broadening coefficient γ₂.""" self._broad_lorentz_gamma_2.value = value @property def broad_mix_beta_0(self) -> Parameter: """ - Mixing parameter. Defines the ratio of Gaussian to Lorentzian - contributions in TOF profiles (deg). + Ratio of Gaussian to Lorentzian contributions (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -216,14 +203,12 @@ def broad_mix_beta_0(self) -> Parameter: @broad_mix_beta_0.setter def broad_mix_beta_0(self, value: float) -> None: - """Set mixing parameter β₀.""" self._broad_mix_beta_0.value = value @property def broad_mix_beta_1(self) -> Parameter: """ - Mixing parameter. Defines the ratio of Gaussian to Lorentzian - contributions in TOF profiles (deg). + Ratio of Gaussian to Lorentzian contributions (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -232,7 +217,6 @@ def broad_mix_beta_1(self) -> Parameter: @broad_mix_beta_1.setter def broad_mix_beta_1(self, value: float) -> None: - """Set mixing parameter β₁.""" self._broad_mix_beta_1.value = value @@ -275,7 +259,6 @@ def asym_alpha_0(self) -> Parameter: @asym_alpha_0.setter def asym_alpha_0(self, value: float) -> None: - """Set Ikeda-Carpenter asymmetry parameter α₀.""" self._asym_alpha_0.value = value @property @@ -290,5 +273,4 @@ def asym_alpha_1(self) -> Parameter: @asym_alpha_1.setter def asym_alpha_1(self, value: float) -> None: - """Set Ikeda-Carpenter asymmetry parameter α₁.""" self._asym_alpha_1.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 89097ccd..0fd6b14c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -23,8 +23,7 @@ def __init__(self) -> None: self._damp_q = Parameter( name='damp_q', - description='Instrumental Q-resolution damping factor ' - '(affects high-r PDF peak amplitude)', + description='Instrumental Q-resolution damping (affects high-r PDF peak amplitude)', units='Å⁻¹', value_spec=AttributeSpec( default=0.05, @@ -34,8 +33,7 @@ def __init__(self) -> None: ) self._broad_q = Parameter( name='broad_q', - description='Quadratic PDF peak broadening coefficient ' - '(thermal and model uncertainty contribution)', + description='Quadratic peak broadening (thermal and model uncertainty contribution)', units='Å⁻²', value_spec=AttributeSpec( default=0.0, @@ -45,8 +43,7 @@ def __init__(self) -> None: ) self._cutoff_q = Parameter( name='cutoff_q', - description='Q-value cutoff applied to model PDF for Fourier ' - 'transform (controls real-space resolution)', + description='Q-value cutoff for Fourier transform (controls real-space resolution)', units='Å⁻¹', value_spec=AttributeSpec( default=25.0, @@ -76,7 +73,7 @@ def __init__(self) -> None: ) self._damp_particle_diameter = Parameter( name='damp_particle_diameter', - description='Particle diameter for spherical envelope damping correction in PDF', + description='Particle diameter for spherical envelope damping correction', units='Å', value_spec=AttributeSpec( default=0.0, @@ -92,8 +89,8 @@ def __init__(self) -> None: @property def damp_q(self) -> Parameter: """ - Instrumental Q-resolution damping factor (affects high-r PDF - peak amplitude) (Å⁻¹). + Instrumental Q-resolution damping (affects high-r PDF peak + amplitude) (Å⁻¹). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -102,14 +99,13 @@ def damp_q(self) -> Parameter: @damp_q.setter def damp_q(self, value: float) -> None: - """Set the Q-resolution damping factor.""" self._damp_q.value = value @property def broad_q(self) -> Parameter: """ - Quadratic PDF peak broadening coefficient (thermal and model - uncertainty contribution) (Å⁻²). + Quadratic peak broadening (thermal and model uncertainty + contribution) (Å⁻²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -118,14 +114,13 @@ def broad_q(self) -> Parameter: @broad_q.setter def broad_q(self, value: float) -> None: - """Set the quadratic PDF peak broadening coefficient.""" self._broad_q.value = value @property def cutoff_q(self) -> Parameter: """ - Q-value cutoff applied to model PDF for Fourier transform - (controls real-space resolution) (Å⁻¹). + Q-value cutoff for Fourier transform (controls real-space + resolution) (Å⁻¹). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -134,7 +129,6 @@ def cutoff_q(self) -> Parameter: @cutoff_q.setter def cutoff_q(self, value: float) -> None: - """Set the Q-value cutoff for the Fourier transform.""" self._cutoff_q.value = value @property @@ -149,7 +143,6 @@ def sharp_delta_1(self) -> Parameter: @sharp_delta_1.setter def sharp_delta_1(self, value: float) -> None: - """Set the PDF peak sharpening coefficient delta_1.""" self._sharp_delta_1.value = value @property @@ -164,14 +157,12 @@ def sharp_delta_2(self) -> Parameter: @sharp_delta_2.setter def sharp_delta_2(self, value: float) -> None: - """Set the PDF peak sharpening coefficient delta_2.""" self._sharp_delta_2.value = value @property def damp_particle_diameter(self) -> Parameter: """ - Particle diameter for spherical envelope damping correction in - PDF (Å). + Particle diameter for spherical envelope damping correction (Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -180,5 +171,4 @@ def damp_particle_diameter(self) -> Parameter: @damp_particle_diameter.setter def damp_particle_diameter(self, value: float) -> None: - """Set the particle diameter for spherical envelope damping.""" self._damp_particle_diameter.value = value diff --git a/src/easydiffraction/datablocks/experiment/item/enums.py b/src/easydiffraction/datablocks/experiment/item/enums.py index 78866a8d..2375c38e 100644 --- a/src/easydiffraction/datablocks/experiment/item/enums.py +++ b/src/easydiffraction/datablocks/experiment/item/enums.py @@ -13,17 +13,19 @@ class SampleFormEnum(str, Enum): @classmethod def default(cls) -> 'SampleFormEnum': - """Return the default sample form (POWDER). + """ + Return the default sample form (POWDER). Returns ------- - SampleFormEnum + 'SampleFormEnum' The default enum member. """ return cls.POWDER def description(self) -> str: - """Return a human-readable description of this sample form. + """ + Return a human-readable description of this sample form. Returns ------- @@ -44,17 +46,19 @@ class ScatteringTypeEnum(str, Enum): @classmethod def default(cls) -> 'ScatteringTypeEnum': - """Return the default scattering type (BRAGG). + """ + Return the default scattering type (BRAGG). Returns ------- - ScatteringTypeEnum + 'ScatteringTypeEnum' The default enum member. """ return cls.BRAGG def description(self) -> str: - """Return a human-readable description of this scattering type. + """ + Return a human-readable description of this scattering type. Returns ------- @@ -75,17 +79,19 @@ class RadiationProbeEnum(str, Enum): @classmethod def default(cls) -> 'RadiationProbeEnum': - """Return the default radiation probe (NEUTRON). + """ + Return the default radiation probe (NEUTRON). Returns ------- - RadiationProbeEnum + 'RadiationProbeEnum' The default enum member. """ return cls.NEUTRON def description(self) -> str: - """Return a human-readable description of this radiation probe. + """ + Return a human-readable description of this radiation probe. Returns ------- @@ -107,17 +113,19 @@ class BeamModeEnum(str, Enum): @classmethod def default(cls) -> 'BeamModeEnum': - """Return the default beam mode (CONSTANT_WAVELENGTH). + """ + Return the default beam mode (CONSTANT_WAVELENGTH). Returns ------- - BeamModeEnum + 'BeamModeEnum' The default enum member. """ return cls.CONSTANT_WAVELENGTH def description(self) -> str: - """Return a human-readable description of this beam mode. + """ + Return a human-readable description of this beam mode. Returns ------- @@ -160,20 +168,21 @@ def default( scattering_type: ScatteringTypeEnum | None = None, beam_mode: BeamModeEnum | None = None, ) -> 'PeakProfileTypeEnum': - """Return the default peak profile type for a given mode. + """ + Return the default peak profile type for a given mode. Parameters ---------- scattering_type : ScatteringTypeEnum | None, default=None - Scattering type; defaults to ``ScatteringTypeEnum.default()`` - when ``None``. + Scattering type; defaults to + ``ScatteringTypeEnum.default()`` when ``None``. beam_mode : BeamModeEnum | None, default=None Beam mode; defaults to ``BeamModeEnum.default()`` when ``None``. Returns ------- - PeakProfileTypeEnum + 'PeakProfileTypeEnum' The default profile type for the given combination. """ if scattering_type is None: @@ -191,7 +200,8 @@ def default( }[(scattering_type, beam_mode)] def description(self) -> str: - """Return a human-readable description of this peak profile type. + """ + Return a human-readable description of this peak profile type. Returns ------- diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index a97ae016..b94a3b8e 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -196,13 +196,6 @@ def label(self) -> StringDescriptor: @label.setter def label(self, value: str) -> None: - """Set the atom site label. - - Parameters - ---------- - value : str - New label value. - """ self._label.value = value @property @@ -218,13 +211,6 @@ def type_symbol(self) -> StringDescriptor: @type_symbol.setter def type_symbol(self, value: str) -> None: - """Set the chemical symbol of the atom at this site. - - Parameters - ---------- - value : str - New chemical symbol value. - """ self._type_symbol.value = value @property @@ -241,13 +227,6 @@ def adp_type(self) -> StringDescriptor: @adp_type.setter def adp_type(self, value: str) -> None: - """Set the atomic displacement parameter type. - - Parameters - ---------- - value : str - New ADP type value (e.g., ``'Biso'``). - """ self._adp_type.value = value @property @@ -264,13 +243,6 @@ def wyckoff_letter(self) -> StringDescriptor: @wyckoff_letter.setter def wyckoff_letter(self, value: str) -> None: - """Set the Wyckoff letter for this atom site. - - Parameters - ---------- - value : str - New Wyckoff letter value. - """ self._wyckoff_letter.value = value @property @@ -285,13 +257,6 @@ def fract_x(self) -> Parameter: @fract_x.setter def fract_x(self, value: float) -> None: - """Set the fractional x-coordinate. - - Parameters - ---------- - value : float - New fractional x-coordinate value. - """ self._fract_x.value = value @property @@ -306,13 +271,6 @@ def fract_y(self) -> Parameter: @fract_y.setter def fract_y(self, value: float) -> None: - """Set the fractional y-coordinate. - - Parameters - ---------- - value : float - New fractional y-coordinate value. - """ self._fract_y.value = value @property @@ -327,13 +285,6 @@ def fract_z(self) -> Parameter: @fract_z.setter def fract_z(self, value: float) -> None: - """Set the fractional z-coordinate. - - Parameters - ---------- - value : float - New fractional z-coordinate value. - """ self._fract_z.value = value @property @@ -349,13 +300,6 @@ def occupancy(self) -> Parameter: @occupancy.setter def occupancy(self, value: float) -> None: - """Set the occupancy of this atom site. - - Parameters - ---------- - value : float - New occupancy value. - """ self._occupancy.value = value @property @@ -371,13 +315,6 @@ def b_iso(self) -> Parameter: @b_iso.setter def b_iso(self, value: float) -> None: - """Set the isotropic atomic displacement parameter. - - Parameters - ---------- - value : float - New B_iso value in Ų. - """ self._b_iso.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 2f3c432a..9c62baa6 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -35,7 +35,7 @@ def __init__(self) -> None: self._length_a = Parameter( name='length_a', - description='Length of the a axis of the unit cell.', + description='Length of the a axis of the unit cell', units='Å', value_spec=AttributeSpec( default=10.0, @@ -45,7 +45,7 @@ def __init__(self) -> None: ) self._length_b = Parameter( name='length_b', - description='Length of the b axis of the unit cell.', + description='Length of the b axis of the unit cell', units='Å', value_spec=AttributeSpec( default=10.0, @@ -55,7 +55,7 @@ def __init__(self) -> None: ) self._length_c = Parameter( name='length_c', - description='Length of the c axis of the unit cell.', + description='Length of the c axis of the unit cell', units='Å', value_spec=AttributeSpec( default=10.0, @@ -163,13 +163,6 @@ def length_a(self) -> Parameter: @length_a.setter def length_a(self, value: float) -> None: - """Set the length of the a axis. - - Parameters - ---------- - value : float - New length value in Å. - """ self._length_a.value = value @property @@ -184,13 +177,6 @@ def length_b(self) -> Parameter: @length_b.setter def length_b(self, value: float) -> None: - """Set the length of the b axis. - - Parameters - ---------- - value : float - New length value in Å. - """ self._length_b.value = value @property @@ -205,13 +191,6 @@ def length_c(self) -> Parameter: @length_c.setter def length_c(self, value: float) -> None: - """Set the length of the c axis. - - Parameters - ---------- - value : float - New length value in Å. - """ self._length_c.value = value @property @@ -226,13 +205,6 @@ def angle_alpha(self) -> Parameter: @angle_alpha.setter def angle_alpha(self, value: float) -> None: - """Set the angle alpha (between b and c axes). - - Parameters - ---------- - value : float - New angle value in degrees. - """ self._angle_alpha.value = value @property @@ -247,13 +219,6 @@ def angle_beta(self) -> Parameter: @angle_beta.setter def angle_beta(self, value: float) -> None: - """Set the angle beta (between a and c axes). - - Parameters - ---------- - value : float - New angle value in degrees. - """ self._angle_beta.value = value @property @@ -268,11 +233,4 @@ def angle_gamma(self) -> Parameter: @angle_gamma.setter def angle_gamma(self, value: float) -> None: - """Set the angle gamma (between a and b axes). - - Parameters - ---------- - value : float - New angle value in degrees. - """ self._angle_gamma.value = value diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 1ca8820f..623fbe39 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -148,13 +148,6 @@ def name_h_m(self) -> StringDescriptor: @name_h_m.setter def name_h_m(self, value: str) -> None: - """Set the Hermann-Mauguin symbol and reset the coordinate code. - - Parameters - ---------- - value : str - New Hermann-Mauguin symbol. - """ self._name_h_m.value = value self._reset_it_coordinate_system_code() @@ -171,11 +164,4 @@ def it_coordinate_system_code(self) -> StringDescriptor: @it_coordinate_system_code.setter def it_coordinate_system_code(self, value: str) -> None: - """Set the IT coordinate system code. - - Parameters - ---------- - value : str - New IT coordinate system code. - """ self._it_coordinate_system_code.value = value diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index 4e8a23ca..cc9d0cdd 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -43,7 +43,8 @@ def _default_engine(cls) -> str: @property def engine(self) -> str: - """Return the name of the currently active rendering engine. + """ + Return the name of the currently active rendering engine. Returns ------- @@ -54,7 +55,8 @@ def engine(self) -> str: @engine.setter def engine(self, new_engine: str) -> None: - """Switch to a different rendering engine. + """ + Switch to a different rendering engine. Parameters ---------- diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index 410ee693..e192e131 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -35,7 +35,8 @@ def default(cls) -> 'TableEngineEnum': return cls.RICH def description(self) -> str: - """Return a human-readable description of this table engine. + """ + Return a human-readable description of this table engine. Returns ------- diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index 3da63697..af10e413 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -243,7 +243,8 @@ def param_from_cif( block: gemmi.cif.Block, idx: int = 0, ) -> None: - """Populate a single descriptor from a CIF block. + """ + Populate a single descriptor from a CIF block. Parameters ---------- @@ -305,7 +306,8 @@ def category_collection_from_cif( self: CategoryCollection, block: gemmi.cif.Block, ) -> None: - """Populate a CategoryCollection from a CIF loop. + """ + Populate a CategoryCollection from a CIF loop. Parameters ---------- diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index b226c979..6e738c81 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -82,7 +82,8 @@ def name(self) -> str: @property def full_name(self) -> str: - """Return the full project name (alias for :attr:`name`). + """ + Return the full project name (alias for :attr:`name`). Returns ------- @@ -239,7 +240,8 @@ def plot_meas( x_max: float | None = None, x: object | None = None, ) -> None: - """Plot measured diffraction data for an experiment. + """ + Plot measured diffraction data for an experiment. Parameters ---------- @@ -271,7 +273,8 @@ def plot_calc( x_max: float | None = None, x: object | None = None, ) -> None: - """Plot calculated diffraction pattern for an experiment. + """ + Plot calculated diffraction pattern for an experiment. Parameters ---------- @@ -304,7 +307,8 @@ def plot_meas_vs_calc( show_residual: bool = False, x: object | None = None, ) -> None: - """Plot measured vs calculated data for an experiment. + """ + Plot measured vs calculated data for an experiment. Parameters ---------- diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py index a04c72d0..1afa4cbc 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_info.py @@ -39,7 +39,8 @@ def name(self) -> str: @name.setter def name(self, value: str) -> None: - """Set the project name. + """ + Set the project name. Parameters ---------- @@ -60,7 +61,8 @@ def title(self) -> str: @title.setter def title(self, value: str) -> None: - """Set the project title. + """ + Set the project title. Parameters ---------- @@ -76,7 +78,8 @@ def description(self) -> str: @description.setter def description(self, value: str) -> None: - """Set the project description (whitespace normalized). + """ + Set the project description (whitespace normalized). Parameters ---------- @@ -92,7 +95,8 @@ def path(self) -> pathlib.Path: @path.setter def path(self, value: object) -> None: - """Set the project directory path. + """ + Set the project directory path. Parameters ---------- diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index 84b409da..61937bf5 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -328,7 +328,9 @@ def _suppress_traceback(logger: object) -> object: """ def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: - """Log only the exception message, suppressing the traceback.""" + """ + Log only the exception message, suppressing the traceback. + """ try: _evalue = ( args[2] if len(args) > 2 else kwargs.get('_evalue') or kwargs.get('evalue') diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 9971a8ca..1f357333 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -529,8 +529,8 @@ def render_table( columns_headers : object, default=None Optional list of column header strings. display_handle : object, default=None - Optional display handle for in-place updates (e.g. in Jupyter - or a terminal Live context). + Optional display handle for in-place updates (e.g. in Jupyter or + a terminal Live context). """ headers = [ (col, align) for col, align in zip(columns_headers, columns_alignment, strict=False) diff --git a/tools/find_missing_docstrings.py b/tools/find_missing_docstrings.py new file mode 100644 index 00000000..cb04709f --- /dev/null +++ b/tools/find_missing_docstrings.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +"""Find all public methods/functions missing docstrings in src/.""" +import ast +import os +import sys + + +def has_docstring(node): + return ( + isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) + and node.body + and isinstance(node.body[0], ast.Expr) + and isinstance(node.body[0].value, ast.Constant) + and isinstance(node.body[0].value.value, str) + ) + + +def is_public(name): + return not name.startswith('_') + + +results = [] +src_root = sys.argv[1] if len(sys.argv) > 1 else 'src' +for root, dirs, files in os.walk(src_root): + dirs[:] = sorted([d for d in dirs if d != '__pycache__' and d != '_vendored']) + for f in sorted(files): + if not f.endswith('.py'): + continue + path = os.path.join(root, f) + try: + with open(path) as fh: + tree = ast.parse(fh.read()) + except Exception as e: + print(f'Error parsing {path}: {e}') + continue + for node in ast.walk(tree): + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + if is_public(node.name) and not has_docstring(node): + results.append(f'{path}:{node.lineno}: {node.name}') + +for r in results: + print(r) +print(f'Total: {len(results)} methods missing docstrings') + From a4a4a4c9ec612c4fdf6c28d09e88c0668d554fe5 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 21:11:03 +0100 Subject: [PATCH 35/48] Make all docstring summaries to take single line --- src/easydiffraction/analysis/analysis.py | 28 +++++------------- .../analysis/calculators/base.py | 12 ++------ .../analysis/calculators/crysfml.py | 6 ++-- .../analysis/calculators/cryspy.py | 12 +++----- .../joint_fit_experiments/factory.py | 4 +-- .../analysis/fit_helpers/metrics.py | 9 ++---- src/easydiffraction/analysis/fitting.py | 5 ++-- .../analysis/minimizers/base.py | 5 +--- .../analysis/minimizers/dfols.py | 5 +--- src/easydiffraction/core/category.py | 8 ++--- src/easydiffraction/core/datablock.py | 20 ++++--------- src/easydiffraction/core/diagnostic.py | 4 +-- src/easydiffraction/core/guard.py | 17 +++-------- src/easydiffraction/core/metadata.py | 3 +- src/easydiffraction/core/singleton.py | 9 ++---- src/easydiffraction/core/variable.py | 15 ++-------- .../crystallography/crystallography.py | 6 ++-- .../experiment/categories/data/bragg_pd.py | 25 +++++----------- .../experiment/categories/data/bragg_sc.py | 27 ++++++----------- .../experiment/categories/data/total_pd.py | 4 +-- .../categories/excluded_regions/factory.py | 4 +-- .../categories/experiment_type/default.py | 4 +-- .../experiment/categories/extinction/shelx.py | 4 +-- .../categories/linked_crystal/default.py | 5 +--- .../experiment/categories/peak/cwl_mixins.py | 22 ++++++-------- .../categories/peak/total_mixins.py | 23 ++++++--------- .../datablocks/experiment/item/base.py | 23 ++++----------- .../datablocks/experiment/item/bragg_pd.py | 5 +--- .../datablocks/experiment/item/bragg_sc.py | 10 ++----- .../datablocks/experiment/item/factory.py | 4 +-- .../datablocks/experiment/item/total_pd.py | 3 +- .../categories/atom_sites/default.py | 12 +++----- .../structure/categories/cell/default.py | 3 +- .../categories/space_group/default.py | 8 ++--- .../datablocks/structure/item/base.py | 4 +-- src/easydiffraction/display/base.py | 4 +-- src/easydiffraction/display/plotting.py | 5 +--- src/easydiffraction/display/utils.py | 4 +-- src/easydiffraction/io/cif/serialize.py | 7 ++--- src/easydiffraction/project/project.py | 4 +-- src/easydiffraction/project/project_info.py | 5 +--- src/easydiffraction/summary/summary.py | 19 +++--------- src/easydiffraction/utils/logging.py | 29 +++++-------------- src/easydiffraction/utils/utils.py | 29 ++++++------------- 44 files changed, 129 insertions(+), 335 deletions(-) diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 8799e2a0..5abe5a87 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -253,10 +253,7 @@ def _get_params_as_dataframe( return df def show_all_params(self) -> None: - """ - Print a table with all parameters for structures and - experiments. - """ + """Print all parameters for structures and experiments.""" structures_params = self.project.structures.parameters experiments_params = self.project.experiments.parameters @@ -286,9 +283,7 @@ def show_all_params(self) -> None: tabler.render(filtered_df) def show_fittable_params(self) -> None: - """ - Print a table with parameters that can be included in fitting. - """ + """Print a table with parameters that can be included in fitting.""" structures_params = self.project.structures.fittable_parameters experiments_params = self.project.experiments.fittable_parameters @@ -320,9 +315,7 @@ def show_fittable_params(self) -> None: tabler.render(filtered_df) def show_free_params(self) -> None: - """ - Print a table with only currently-free (varying) parameters. - """ + """Print a table with only currently-free (varying) parameters.""" structures_params = self.project.structures.free_parameters experiments_params = self.project.experiments.free_parameters free_params = structures_params + experiments_params @@ -482,9 +475,7 @@ def show_current_minimizer(self) -> None: @staticmethod def show_available_minimizers() -> None: - """ - Print a table of available minimizer drivers on this system. - """ + """Print a table of available minimizer drivers on this system.""" MinimizerFactory.show_supported() @property @@ -590,9 +581,7 @@ def show_constraints(self) -> None: ) def apply_constraints(self) -> None: - """ - Apply the currently defined constraints to the active project. - """ + """Apply the currently defined constraints to the active project.""" if not self.constraints._items: log.warning('No constraints defined.') return @@ -603,8 +592,7 @@ def apply_constraints(self) -> None: def fit(self) -> None: """ - Execute fitting using the selected mode, calculator and - minimizer. + Execute fitting for all experiments. This method performs the optimization but does not display results automatically. Call :meth:`show_fit_results` after @@ -736,9 +724,7 @@ def as_cif(self) -> str: return analysis_to_cif(self) def show_as_cif(self) -> None: - """ - Render the analysis section as CIF in a formatted console view. - """ + """Render the analysis section as CIF in a formatted console view.""" cif_text: str = self.as_cif() paragraph_title: str = 'Analysis 🧮 info as cif' console.paragraph(paragraph_title) diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index 14d76977..78f78a30 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -23,9 +23,7 @@ def name(self) -> str: @property @abstractmethod def engine_imported(self) -> bool: - """ - Whether the underlying calculation library could be imported. - """ + """Whether the underlying calculation library could be imported.""" pass @abstractmethod @@ -35,10 +33,7 @@ def calculate_structure_factors( experiment: ExperimentBase, called_by_minimizer: bool, ) -> None: - """ - Calculate structure factors for a single structure and - experiment. - """ + """Calculate structure factors for one structure-experiment pair.""" pass @abstractmethod @@ -49,8 +44,7 @@ def calculate_pattern( called_by_minimizer: bool, ) -> np.ndarray: """ - Calculate the diffraction pattern for a single structure and - experiment. + Calculate diffraction pattern for one structure-experiment pair. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 9396c16d..29a46ab9 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -73,8 +73,7 @@ def calculate_pattern( called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: """ - Calculates the diffraction pattern using Crysfml for the given - structure and experiment. + Calculate the diffraction pattern using Crysfml. Parameters ---------- @@ -135,8 +134,7 @@ def _crysfml_dict( experiment: ExperimentBase, ) -> Dict[str, Union[ExperimentBase, Structure]]: """ - Converts the structure and experiment into a dictionary format - for Crysfml. + Convert structure and experiment into a Crysfml dictionary. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 6344f178..6f1e4ec0 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -64,8 +64,7 @@ def calculate_structure_factors( called_by_minimizer: bool = False, ) -> None: """ - Raises a NotImplementedError as HKL calculation is not - implemented. + Raise NotImplementedError as HKL calculation is not implemented. Parameters ---------- @@ -124,8 +123,7 @@ def calculate_pattern( called_by_minimizer: bool = False, ) -> Union[np.ndarray, List[float]]: """ - Calculates the diffraction pattern using Cryspy for the given - structure and experiment. + Calculate the diffraction pattern using Cryspy. We only recreate the cryspy_obj if this method is - NOT called by the minimizer, or - the cryspy_dict is NOT yet created. In @@ -204,8 +202,7 @@ def _recreate_cryspy_dict( experiment: ExperimentBase, ) -> Dict[str, Any]: """ - Recreates the Cryspy dictionary for the given structure and - experiment. + Recreate the Cryspy dictionary for structure and experiment. Parameters ---------- @@ -324,8 +321,7 @@ def _recreate_cryspy_obj( experiment: ExperimentBase, ) -> object: """ - Recreates the Cryspy object for the given structure and - experiment. + Recreate the Cryspy object for structure and experiment. Parameters ---------- diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py index f02ef6ee..64e6bbc7 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py @@ -1,8 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -""" -Joint-fit-experiments factory — delegates entirely to ``FactoryBase``. -""" +"""Joint-fit-experiments factory — delegates entirely to ``FactoryBase``.""" from __future__ import annotations diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index e5ccdad9..f6b3b3aa 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -15,8 +15,7 @@ def calculate_r_factor( y_calc: np.ndarray, ) -> float: """ - Calculate the R-factor (reliability factor) between observed and - calculated data. + Calculate the R-factor between observed and calculated data. Parameters ---------- @@ -43,8 +42,7 @@ def calculate_weighted_r_factor( weights: np.ndarray, ) -> float: """ - Calculate the weighted R-factor between observed and calculated - data. + Calculate the weighted R-factor between observed and calculated data. Parameters ---------- @@ -154,8 +152,7 @@ def get_reliability_inputs( experiments: Experiments, ) -> Tuple[np.ndarray, np.ndarray, Optional[np.ndarray]]: """ - Collect observed and calculated data points for reliability - calculations. + Collect observed and calculated data for reliability calculations. Parameters ---------- diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index 302db905..b7076204 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -159,8 +159,9 @@ def _residual_function( analysis: object = None, ) -> np.ndarray: """ - Residual function computes the difference between measured and - calculated patterns. It updates the parameter values according + Compute residuals between measured and calculated patterns. + + It updates the parameter values according to the optimizer-provided engine_params. Parameters diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index c3d666a9..77a21b63 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -92,10 +92,7 @@ def _sync_result_to_parameters( raw_result: object, parameters: List[object], ) -> None: - """ - Copy values from ``raw_result`` back to ``parameters`` in- - place. - """ + """Copy values from ``raw_result`` back to ``parameters`` in-place.""" pass def _finalize_fit( diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index 1c323963..1f47a5e8 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -16,10 +16,7 @@ @MinimizerFactory.register class DfolsMinimizer(MinimizerBase): - """ - Minimizer using the DFO-LS package (Derivative-Free Optimization for - Least-Squares). - """ + """Minimizer using DFO-LS (derivative-free least-squares).""" type_info = TypeInfo( tag='dfols', diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index e70ec913..010cba2a 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -36,9 +36,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def unique_name(self) -> str: - """ - Fully qualified name combining datablock, category, and entry. - """ + """Fully qualified name combining datablock, category, and entry.""" parts = [ self._identity.datablock_entry_name, self._identity.category_code, @@ -202,9 +200,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def unique_name(self) -> str | None: - """ - Return None; collections do not carry their own unique name. - """ + """Return None; collections do not carry their own unique name.""" return None @property diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 677563a9..16e4c92e 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -68,10 +68,7 @@ def unique_name(self) -> str | None: @property def categories(self) -> list: - """ - All category objects in this datablock, sorted by update - priority. - """ + """All category objects in this datablock by priority.""" cats = [ v for v in vars(self).values() if isinstance(v, (CategoryItem, CategoryCollection)) ] @@ -80,9 +77,7 @@ def categories(self) -> list: @property def parameters(self) -> list: - """ - All parameters from all categories contained in this datablock. - """ + """All parameters from all categories contained in this datablock.""" params = [] for v in self.categories: params.extend(v.parameters) @@ -124,8 +119,7 @@ def help(self) -> None: class DatablockCollection(CollectionBase): """ - Handles top-level category collections (e.g. Structures, - Experiments). + Collection of top-level datablocks (e.g. Structures, Experiments). Each item is a DatablockItem. @@ -158,9 +152,7 @@ def __str__(self) -> str: @property def unique_name(self) -> str | None: - """ - Return None; collections do not carry their own unique name. - """ + """Return None; collections do not carry their own unique name.""" return None @property @@ -173,9 +165,7 @@ def parameters(self) -> list: @property def fittable_parameters(self) -> list: - """ - All non-constrained Parameter instances in this collection. - """ + """All non-constrained Parameter instances in this collection.""" return [p for p in self.parameters if isinstance(p, Parameter) and not p.constrained] @property diff --git a/src/easydiffraction/core/diagnostic.py b/src/easydiffraction/core/diagnostic.py index 3132ed7a..f6248e8f 100644 --- a/src/easydiffraction/core/diagnostic.py +++ b/src/easydiffraction/core/diagnostic.py @@ -57,9 +57,7 @@ def attr_error( allowed: set[str], label: str = 'Allowed', ) -> None: - """ - Log access to an unknown attribute and suggest closest key. - """ + """Log access to an unknown attribute and suggest closest key.""" suggestion = Diagnostics._build_suggestion(key, allowed) # Use consistent (label) logic for allowed hint = suggestion or Diagnostics._build_allowed(allowed, label=label) diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index 5f553d50..586a8e72 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -12,9 +12,7 @@ class GuardedBase(ABC): - """ - Base class enforcing controlled attribute access and parent linkage. - """ + """Base class enforcing controlled attribute access and parent linkage.""" _diagnoser = Diagnostics() @@ -80,8 +78,7 @@ def _assign_attr(self, key: str, value: object) -> None: @classmethod def _iter_properties(cls) -> Generator[tuple[str, property], None, None]: """ - Iterate over all public properties defined in the class - hierarchy. + Iterate over all public properties in the class hierarchy. Yields ------ @@ -136,19 +133,13 @@ def unique_name(self) -> str: @property @abstractmethod def parameters(self) -> list: - """ - Return a list of parameter objects (to be implemented by - subclasses). - """ + """Return a list of parameters (implemented by subclasses).""" raise NotImplementedError @property @abstractmethod def as_cif(self) -> str: - """ - Return CIF representation of this object (to be implemented by - subclasses). - """ + """Return CIF representation (implemented by subclasses).""" raise NotImplementedError @staticmethod diff --git a/src/easydiffraction/core/metadata.py b/src/easydiffraction/core/metadata.py index a5d7e8f6..318d64bb 100644 --- a/src/easydiffraction/core/metadata.py +++ b/src/easydiffraction/core/metadata.py @@ -19,8 +19,7 @@ @dataclass(frozen=True) class TypeInfo: """ - Stable identity and human-readable description for a factory- - created class. + Stable identity and description for a factory-created class. Attributes ---------- diff --git a/src/easydiffraction/core/singleton.py b/src/easydiffraction/core/singleton.py index d7f0d14c..a4b75648 100644 --- a/src/easydiffraction/core/singleton.py +++ b/src/easydiffraction/core/singleton.py @@ -86,8 +86,7 @@ def replace_uid(self, old_uid: str, new_uid: str) -> None: # when removing constraints class ConstraintsHandler(SingletonBase): """ - Manages user-defined parameter constraints using aliases and - expressions. + Manage parameter constraints using aliases and expressions. Uses the asteval interpreter for safe evaluation of mathematical expressions. Constraints are defined as: lhs_alias = @@ -126,11 +125,7 @@ def set_constraints(self, constraints: object) -> None: self._parse_constraints() def _parse_constraints(self) -> None: - """ - Converts raw expression input into a normalized internal list of - (lhs_alias, rhs_expr) pairs, stripping whitespace and skipping - invalid entries. - """ + """Parse raw expressions into (lhs_alias, rhs_expr) pairs.""" self._parsed_constraints = [] for expr_obj in self._constraints: diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 1390565e..d4733747 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -107,10 +107,7 @@ def name(self) -> str: @property def unique_name(self) -> str: - """ - Fully qualified name including datablock, category and entry - name. - """ + """Fully qualified name: datablock, category and entry.""" parts = [ self._identity.datablock_entry_name, self._identity.category_code, @@ -120,10 +117,7 @@ def unique_name(self) -> str: return '.'.join(filter(None, parts)) def _parent_of_type(self, cls: type) -> object | None: - """ - Walk up the parent chain and return the first parent of type - ``cls``. - """ + """Traverse parents and return the first of type cls.""" obj = getattr(self, '_parent', None) visited = set() while obj is not None and id(obj) not in visited: @@ -346,10 +340,7 @@ def free(self, v: bool) -> None: @property def uncertainty(self) -> float | None: - """ - Estimated standard uncertainty of the fitted value, if - available. - """ + """Estimated standard uncertainty of the fitted value.""" return self._uncertainty @uncertainty.setter diff --git a/src/easydiffraction/crystallography/crystallography.py b/src/easydiffraction/crystallography/crystallography.py index 1d7707f6..bc90383f 100644 --- a/src/easydiffraction/crystallography/crystallography.py +++ b/src/easydiffraction/crystallography/crystallography.py @@ -22,8 +22,7 @@ def apply_cell_symmetry_constraints( name_hm: str, ) -> Dict[str, float]: """ - Apply symmetry constraints to unit cell parameters based on space - group. + Apply symmetry constraints to unit cell parameters. Parameters ---------- @@ -97,8 +96,7 @@ def apply_atom_site_symmetry_constraints( wyckoff_letter: str, ) -> Dict[str, Any]: """ - Apply symmetry constraints to atomic coordinates based on site - symmetry. + Apply symmetry constraints to atom site coordinates. Parameters ---------- diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 1978608c..dbeac7c7 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -59,7 +59,7 @@ def __init__(self) -> None: ) self._intensity_meas = NumericDescriptor( name='intensity_meas', - description='Intensity recorded at each measurement point as a function of angle/time', + description='Intensity recorded at each measurement point (angle/time)', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(ge=0), @@ -144,8 +144,7 @@ def d_spacing(self) -> NumericDescriptor: @property def intensity_meas(self) -> NumericDescriptor: """ - Intensity recorded at each measurement point as a function of - angle/time. + Intensity recorded at each measurement point (angle/time). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -194,9 +193,7 @@ def calc_status(self) -> StringDescriptor: class PdCwlDataPointMixin: - """ - Mixin for powder diffraction data points with constant wavelength. - """ + """Mixin for powder diffraction data points with constant wavelength.""" def __init__(self) -> None: super().__init__() @@ -277,9 +274,7 @@ class PdCwlDataPoint( # But also says, that in fact, it is just for consistency. And both # orders work. ): - """ - Powder diffraction data point for constant-wavelength experiments. - """ + """Powder diffraction data point for constant-wavelength experiments.""" def __init__(self) -> None: super().__init__() @@ -326,9 +321,7 @@ def _set_intensity_meas(self, values: object) -> None: p.intensity_meas._value = v def _set_intensity_meas_su(self, values: object) -> None: - """ - Helper method to set standard uncertainty of measured intensity. - """ + """Helper method to set standard uncertainty of measured intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas_su._value = v @@ -531,9 +524,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def two_theta(self) -> np.ndarray: - """ - Get the 2θ values for data points included in calculations. - """ + """Get the 2θ values for data points included in calculations.""" return np.fromiter( (p.two_theta.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? @@ -608,9 +599,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def time_of_flight(self) -> np.ndarray: - """ - Get the TOF values for data points included in calculations. - """ + """Get the TOF values for data points included in calculations.""" return np.fromiter( (p.time_of_flight.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 47834bd6..58d68808 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -26,9 +26,7 @@ class Refln(CategoryItem): - """ - Single reflection for single crystal diffraction data category. - """ + """Single reflection for single crystal diffraction data category.""" def __init__(self) -> None: super().__init__() @@ -47,7 +45,7 @@ def __init__(self) -> None: ) self._d_spacing = NumericDescriptor( name='d_spacing', - description='The distance between lattice planes in the crystal for this reflection', + description='Distance between lattice planes for this reflection', units='Å', value_spec=AttributeSpec( default=0.0, @@ -112,7 +110,7 @@ def __init__(self) -> None: ) self._intensity_calc = NumericDescriptor( name='intensity_calc', - description='The intensity of the reflection calculated from the atom site data.', + description='Intensity of the reflection calculated from atom site data', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(ge=0), @@ -121,7 +119,7 @@ def __init__(self) -> None: ) self._wavelength = NumericDescriptor( name='wavelength', - description='The mean wavelength of radiation used to measure this reflection.', + description='Mean wavelength of radiation for this reflection', units='Å', value_spec=AttributeSpec( default=0.0, @@ -150,8 +148,7 @@ def id(self) -> StringDescriptor: @property def d_spacing(self) -> NumericDescriptor: """ - The distance between lattice planes in the crystal for this - reflection (Å). + Distance between lattice planes for this reflection (Å). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -221,8 +218,7 @@ def intensity_meas_su(self) -> NumericDescriptor: @property def intensity_calc(self) -> NumericDescriptor: """ - The intensity of the reflection calculated from the atom site - data. + Intensity of the reflection calculated from atom site data. Reading this property returns the underlying ``NumericDescriptor`` object. @@ -232,8 +228,7 @@ def intensity_calc(self) -> NumericDescriptor: @property def wavelength(self) -> NumericDescriptor: """ - The mean wavelength of radiation used to measure this reflection - (Å). + Mean wavelength of radiation for this reflection (Å). Reading this property returns the underlying ``NumericDescriptor`` object. @@ -300,9 +295,7 @@ def _set_intensity_meas(self, values: object) -> None: p.intensity_meas._value = v def _set_intensity_meas_su(self, values: object) -> None: - """ - Helper method to set standard uncertainty of measured intensity. - """ + """Helper method to set standard uncertainty of measured intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas_su._value = v @@ -426,9 +419,7 @@ def intensity_meas_su(self) -> np.ndarray: @property def intensity_calc(self) -> np.ndarray: - """ - Calculated structure-factor intensities for all reflections. - """ + """Calculated structure-factor intensities for all reflections.""" return np.fromiter( (p.intensity_calc.value for p in self._items), dtype=float, diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index d4ab006e..f21ca237 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -204,9 +204,7 @@ def _set_g_r_meas(self, values: object) -> None: p.g_r_meas._value = v def _set_g_r_meas_su(self, values: object) -> None: - """ - Helper method to set standard uncertainty of measured G(r). - """ + """Helper method to set standard uncertainty of measured G(r).""" for p, v in zip(self._items, values, strict=True): p.g_r_meas_su._value = v diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py index e12fb0c0..7fa9bf08 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py @@ -1,8 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -""" -Excluded-regions factory — delegates entirely to ``FactoryBase``. -""" +"""Excluded-regions factory — delegates entirely to ``FactoryBase``.""" from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 0ace392f..9340ed51 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -27,9 +27,7 @@ @ExperimentTypeFactory.register class ExperimentType(CategoryItem): - """ - Container of categorical attributes defining experiment flavor. - """ + """Container of categorical attributes defining experiment flavor.""" type_info = TypeInfo( tag='default', diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index f6731bb2..1eed5efb 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -17,9 +17,7 @@ @ExtinctionFactory.register class ShelxExtinction(CategoryItem): - """ - Shelx-style isotropic extinction correction for single crystals. - """ + """Shelx-style isotropic extinction correction for single crystals.""" type_info = TypeInfo( tag='shelx', diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py index f7b6efa3..d441aa9c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py @@ -21,10 +21,7 @@ @LinkedCrystalFactory.register class LinkedCrystal(CategoryItem): - """ - Linked crystal category for referencing from the experiment for - single crystal diffraction. - """ + """Linked crystal reference for single-crystal diffraction.""" type_info = TypeInfo( tag='default', diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index f6d7ad11..6e4f29c8 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -22,7 +22,7 @@ def __init__(self) -> None: self._broad_gauss_u: Parameter = Parameter( name='broad_gauss_u', - description='Gaussian broadening (sample size and instrument resolution)', + description='Gaussian broadening from sample size and resolution', units='deg²', value_spec=AttributeSpec( default=0.01, @@ -32,7 +32,7 @@ def __init__(self) -> None: ) self._broad_gauss_v: Parameter = Parameter( name='broad_gauss_v', - description='Gaussian broadening (instrumental broadening contribution)', + description='Gaussian broadening instrumental contribution', units='deg²', value_spec=AttributeSpec( default=-0.01, @@ -42,7 +42,7 @@ def __init__(self) -> None: ) self._broad_gauss_w: Parameter = Parameter( name='broad_gauss_w', - description='Gaussian broadening (instrumental broadening contribution)', + description='Gaussian broadening instrumental contribution', units='deg²', value_spec=AttributeSpec( default=0.02, @@ -52,7 +52,7 @@ def __init__(self) -> None: ) self._broad_lorentz_x: Parameter = Parameter( name='broad_lorentz_x', - description='Lorentzian broadening (sample strain effects)', + description='Lorentzian broadening from sample strain effects', units='deg', value_spec=AttributeSpec( default=0.0, @@ -62,7 +62,7 @@ def __init__(self) -> None: ) self._broad_lorentz_y: Parameter = Parameter( name='broad_lorentz_y', - description='Lorentzian broadening (microstructural defects and strain)', + description='Lorentzian broadening from microstructural defects', units='deg', value_spec=AttributeSpec( default=0.0, @@ -78,8 +78,7 @@ def __init__(self) -> None: @property def broad_gauss_u(self) -> Parameter: """ - Gaussian broadening (sample size and instrument resolution) - (deg²). + Gaussian broadening from sample size and resolution (deg²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -93,8 +92,7 @@ def broad_gauss_u(self, value: float) -> None: @property def broad_gauss_v(self) -> Parameter: """ - Gaussian broadening (instrumental broadening contribution) - (deg²). + Gaussian broadening instrumental contribution (deg²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -108,8 +106,7 @@ def broad_gauss_v(self, value: float) -> None: @property def broad_gauss_w(self) -> Parameter: """ - Gaussian broadening (instrumental broadening contribution) - (deg²). + Gaussian broadening instrumental contribution (deg²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -137,8 +134,7 @@ def broad_lorentz_x(self, value: float) -> None: @property def broad_lorentz_y(self) -> Parameter: """ - Lorentzian broadening (microstructural defects and strain) - (deg). + Lorentzian broadening from microstructural defects (deg). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 0fd6b14c..7933e174 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -1,8 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -""" -Total scattering / pair distribution function (PDF) peak-profile -component classes. +"""Total scattering / PDF peak-profile component classes. This module provides classes that add broadening and asymmetry parameters. They are composed into concrete peak classes elsewhere via @@ -23,7 +21,7 @@ def __init__(self) -> None: self._damp_q = Parameter( name='damp_q', - description='Instrumental Q-resolution damping (affects high-r PDF peak amplitude)', + description='Q-resolution damping for high-r PDF peak amplitude', units='Å⁻¹', value_spec=AttributeSpec( default=0.05, @@ -33,7 +31,7 @@ def __init__(self) -> None: ) self._broad_q = Parameter( name='broad_q', - description='Quadratic peak broadening (thermal and model uncertainty contribution)', + description='Quadratic peak broadening from thermal uncertainty', units='Å⁻²', value_spec=AttributeSpec( default=0.0, @@ -43,7 +41,7 @@ def __init__(self) -> None: ) self._cutoff_q = Parameter( name='cutoff_q', - description='Q-value cutoff for Fourier transform (controls real-space resolution)', + description='Q-value cutoff for Fourier transform', units='Å⁻¹', value_spec=AttributeSpec( default=25.0, @@ -53,7 +51,7 @@ def __init__(self) -> None: ) self._sharp_delta_1 = Parameter( name='sharp_delta_1', - description='PDF peak sharpening coefficient (1/r dependence)', + description='Peak sharpening coefficient (1/r dependence)', units='Å', value_spec=AttributeSpec( default=0.0, @@ -63,7 +61,7 @@ def __init__(self) -> None: ) self._sharp_delta_2 = Parameter( name='sharp_delta_2', - description='PDF peak sharpening coefficient (1/r² dependence)', + description='Peak sharpening coefficient (1/r² dependence)', units='Ų', value_spec=AttributeSpec( default=0.0, @@ -89,8 +87,7 @@ def __init__(self) -> None: @property def damp_q(self) -> Parameter: """ - Instrumental Q-resolution damping (affects high-r PDF peak - amplitude) (Å⁻¹). + Q-resolution damping for high-r PDF peak amplitude (Å⁻¹). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -104,8 +101,7 @@ def damp_q(self, value: float) -> None: @property def broad_q(self) -> Parameter: """ - Quadratic peak broadening (thermal and model uncertainty - contribution) (Å⁻²). + Quadratic peak broadening from thermal uncertainty (Å⁻²). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -119,8 +115,7 @@ def broad_q(self, value: float) -> None: @property def cutoff_q(self) -> Parameter: """ - Q-value cutoff for Fourier transform (controls real-space - resolution) (Å⁻¹). + Q-value cutoff for Fourier transform (Å⁻¹). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 3eb5e447..7f902985 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -34,10 +34,7 @@ class ExperimentBase(DatablockItem): - """ - Base class for all experiment datablock items with only core - attributes. - """ + """Base class for all experiment datablock items.""" def __init__( self, @@ -71,9 +68,7 @@ def name(self, new: str) -> None: @property def type(self) -> object: # TODO: Consider another name - """ - Experiment type descriptor (sample form, probe, beam mode). - """ + """Experiment type descriptor (sample form, probe, beam mode).""" return self._type @property @@ -156,10 +151,7 @@ def calculator_type(self, tag: str) -> None: console.print(tag) def show_supported_calculator_types(self) -> None: - """ - Print a table of calculator backends supported by this - experiment. - """ + """Print a table of supported calculator backends.""" from easydiffraction.analysis.calculators.factory import CalculatorFactory supported_tags = self._supported_calculator_tags() @@ -186,10 +178,7 @@ def show_current_calculator_type(self) -> None: console.print(self.calculator_type) def _resolve_calculator(self) -> None: - """ - Auto-resolve the default calculator from the data category's - ``calculator_support`` and ``CalculatorFactory._default_rules``. - """ + """Auto-resolve the default calculator from data category.""" from easydiffraction.analysis.calculators.factory import CalculatorFactory tag = CalculatorFactory.default_tag( @@ -612,9 +601,7 @@ def excluded_regions_type(self, new_type: str) -> None: console.print(new_type) def show_supported_excluded_regions_types(self) -> None: - """ - Print a table of supported excluded-regions collection types. - """ + """Print a table of supported excluded-regions collection types.""" ExcludedRegionsFactory.show_supported() def show_current_excluded_regions_type(self) -> None: diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index 6f744e78..d5b469cf 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -25,10 +25,7 @@ @ExperimentFactory.register class BraggPdExperiment(PdExperimentBase): - """ - Standard (Bragg) Powder Diffraction experiment class with specific - attributes. - """ + """Standard Bragg powder diffraction experiment.""" type_info = TypeInfo( tag='bragg-pd', diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py index bf1ebece..3cb3f8a1 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py @@ -23,10 +23,7 @@ @ExperimentFactory.register class CwlScExperiment(ScExperimentBase): - """ - Standard (Bragg) constant wavelength single srystal experiment class - with specific attributes. - """ + """Bragg constant-wavelength single-crystal experiment.""" type_info = TypeInfo( tag='bragg-sc-cwl', @@ -89,10 +86,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: @ExperimentFactory.register class TofScExperiment(ScExperimentBase): - """ - Standard (Bragg) time-of-flight single srystal experiment class with - specific attributes. - """ + """Bragg time-of-flight single-crystal experiment.""" type_info = TypeInfo( tag='bragg-sc-tof', diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 99222161..0b021171 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -75,9 +75,7 @@ def _create_experiment_type( radiation_probe: str | None = None, scattering_type: str | None = None, ) -> ExperimentType: - """ - Construct an ExperimentType, using defaults for omitted values. - """ + """Construct an ExperimentType, using defaults for omitted values.""" # Note: validation of input values is done via Descriptor setter # methods diff --git a/src/easydiffraction/datablocks/experiment/item/total_pd.py b/src/easydiffraction/datablocks/experiment/item/total_pd.py index 27acebcd..ade11e6f 100644 --- a/src/easydiffraction/datablocks/experiment/item/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/total_pd.py @@ -43,8 +43,7 @@ def __init__( def _load_ascii_data_to_experiment(self, data_path: str) -> None: """ - Loads x, y, sy values from an ASCII data file into the - experiment. + Load x, y, sy values from an ASCII file into the experiment. The file must be structured as: x y sy """ diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index b94a3b8e..265c3c62 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -216,8 +216,7 @@ def type_symbol(self, value: str) -> None: @property def adp_type(self) -> StringDescriptor: """ - Type of atomic displacement parameter (ADP) used (e.g., Biso, - Uiso, Uani, Bani). + ADP type used (e.g., Biso, Uiso, Uani, Bani). Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -232,8 +231,7 @@ def adp_type(self, value: str) -> None: @property def wyckoff_letter(self) -> StringDescriptor: """ - Wyckoff letter indicating the symmetry of the atom site within - the space group. + Wyckoff letter for the atom site symmetry position. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -290,8 +288,7 @@ def fract_z(self, value: float) -> None: @property def occupancy(self) -> Parameter: """ - Occupancy of the atom site, representing the fraction of the - site occupied by the atom type. + Occupancy fraction of the atom type at this site. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -305,8 +302,7 @@ def occupancy(self, value: float) -> None: @property def b_iso(self) -> Parameter: """ - Isotropic atomic displacement parameter (ADP) for the atom site - (Ų). + Isotropic ADP for the atom site (Ų). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 9c62baa6..8d793b50 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -17,8 +17,7 @@ @CellFactory.register class Cell(CategoryItem): """ - Unit cell with lengths *a*, *b*, *c* and angles *alpha*, *beta*, - *gamma*. + Unit cell with lengths a, b, c and angles alpha, beta, gamma. All six lattice parameters are exposed as :class:`Parameter` descriptors supporting validation, fitting and CIF serialization. diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index 623fbe39..a91cc554 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -22,8 +22,7 @@ @SpaceGroupFactory.register class SpaceGroup(CategoryItem): """ - Space group with Hermann–Mauguin symbol and IT coordinate system - code. + Space group with H-M symbol and IT coordinate system code. Holds the space-group symbol (``name_h_m``) and the International Tables coordinate-system qualifier (``it_coordinate_system_code``). @@ -85,10 +84,7 @@ def __init__(self) -> None: # ------------------------------------------------------------------ def _reset_it_coordinate_system_code(self) -> None: - """ - Reset the IT coordinate system code to the default for the - current group. - """ + """Reset IT coordinate system code to default for this group.""" self._it_coordinate_system_code.value = self._it_coordinate_system_code_default_value @property diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index 743a3589..3f799980 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -245,9 +245,7 @@ def show_current_atom_sites_type(self) -> None: # ------------------------------------------------------------------ def show(self) -> None: - """ - Display an ASCII projection of the structure on a 2D plane. - """ + """Display an ASCII projection of the structure on a 2D plane.""" console.paragraph(f"Structure 🧩 '{self.name}'") console.print('Not implemented yet.') diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index cc9d0cdd..bf97c68a 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -141,9 +141,7 @@ def supported_engines(cls) -> List[str]: @classmethod def descriptions(cls) -> List[Tuple[str, str]]: - """ - Return pairs of engine name and human-friendly description. - """ + """Return pairs of engine name and human-friendly description.""" items = cls._registry().items() return [(name, config.get('description')) for name, config in items] diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 91584150..6cd8dd94 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -159,10 +159,7 @@ def _get_axes_labels( scattering_type: object, x_axis: object, ) -> list: - """ - Look up axis labels for the given experiment / x-axis - combination. - """ + """Look up axis labels for the experiment / x-axis.""" return DEFAULT_AXES_LABELS[(sample_form, scattering_type, x_axis)] def _prepare_powder_data( diff --git a/src/easydiffraction/display/utils.py b/src/easydiffraction/display/utils.py index 087f6378..b4e3dc9d 100644 --- a/src/easydiffraction/display/utils.py +++ b/src/easydiffraction/display/utils.py @@ -18,9 +18,7 @@ class JupyterScrollManager: - """ - Ensures that Jupyter output cells are not scrollable (applied once). - """ + """Ensures that Jupyter output cells are not scrollable (applied once).""" _applied: ClassVar[bool] = False diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index af10e413..af83f4ee 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -24,8 +24,7 @@ def format_value(value: object) -> str: """ - Format a single CIF value, quoting strings with whitespace, and - format floats with global precision. + Format a single CIF value for output. .. note:: The precision must be high enough so that the minimizer's finite-difference Jacobian probes (typically ~1e-8 @@ -160,9 +159,7 @@ def datablock_collection_to_cif(collection: object) -> str: def project_info_to_cif(info: object) -> str: - """ - Render ProjectInfo to CIF text (id, title, description, dates). - """ + """Render ProjectInfo to CIF text (id, title, description, dates).""" name = f'{info.name}' title = f'{info.title}' diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 6e738c81..7f757a2a 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -75,9 +75,7 @@ def info(self) -> ProjectInfo: @property def name(self) -> str: - """ - Convenience property to access the project's name directly. - """ + """Convenience property to access the project's name directly.""" return self._info.name @property diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py index 1afa4cbc..f79d73de 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_info.py @@ -12,10 +12,7 @@ class ProjectInfo(GuardedBase): - """ - Stores metadata about the project, such as name, title, description, - and file paths. - """ + """Store project metadata: name, title, description, paths.""" def __init__( self, diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index 619b5f32..f8ebc15c 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -57,10 +57,7 @@ def show_project_info(self) -> None: print('\n'.join(desc_lines)) def show_crystallographic_data(self) -> None: - """ - Print crystallographic data including phase datablocks, space - groups, cell parameters, and atom sites. - """ + """Print crystallographic data for all phases.""" console.section('Crystallographic data') for model in self.project.structures.values(): @@ -120,10 +117,7 @@ def show_crystallographic_data(self) -> None: ) def show_experimental_data(self) -> None: - """ - Print experimental data including experiment datablocks, types, - instrument settings, and peak profile information. - """ + """Print experimental data for all experiments.""" console.section('Experiments') for expt in self.project.experiments.values(): @@ -181,10 +175,7 @@ def show_experimental_data(self) -> None: ) def show_fitting_details(self) -> None: - """ - Print fitting details including calculation and minimization - engines, and fit quality metrics. - """ + """Print fitting details including engines and metrics.""" console.section('Fitting') console.paragraph('Calculation engine') @@ -214,9 +205,7 @@ def show_fitting_details(self) -> None: # ------------------------------------------ def as_cif(self) -> str: - """ - Export the final fitted data and analysis results as CIF format. - """ + """Export the final fitted data and analysis results as CIF format.""" from easydiffraction.io.cif.serialize import summary_to_cif return summary_to_cif(self) diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index 61937bf5..bb4c3eb9 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -44,10 +44,7 @@ class IconifiedRichHandler(RichHandler): - """ - RichHandler that uses icons for log levels in compact mode, Rich - default in verbose mode. - """ + """RichHandler using icons (compact) or names (verbose).""" _icons = { logging.CRITICAL: '💀', @@ -311,8 +308,7 @@ def restore_original_hook() -> None: @staticmethod def _suppress_traceback(logger: object) -> object: """ - Build a Jupyter custom exception callback that logs only the - message. + Build a Jupyter exception callback that logs the message only. Parameters ---------- @@ -328,9 +324,7 @@ def _suppress_traceback(logger: object) -> object: """ def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: - """ - Log only the exception message, suppressing the traceback. - """ + """Log only the exception message, suppressing the traceback.""" try: _evalue = ( args[2] if len(args) > 2 else kwargs.get('_evalue') or kwargs.get('evalue') @@ -345,8 +339,7 @@ def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: @staticmethod def install_jupyter_traceback_suppressor(logger: logging.Logger) -> None: """ - Install a Jupyter/IPython custom exception handler that - suppresses tracebacks. + Install a Jupyter/IPython exception handler for tracebacks. Parameters ---------- @@ -482,9 +475,7 @@ def configure( @classmethod def _install_jupyter_traceback_suppressor(cls) -> None: - """ - Install traceback suppressor in Jupyter, safely and lint- clean. - """ + """Install traceback suppressor in Jupyter, safely and lint- clean.""" ExceptionHookManager.install_jupyter_traceback_suppressor(cls._logger) # ===== Helpers ===== @@ -637,10 +628,7 @@ def critical(cls, *messages: str, exc_type: type[BaseException] = RuntimeError) class ConsolePrinter: - """ - Printer utility that prints objects to the shared console with left - padding. - """ + """Printer utility for the shared console with left padding.""" _console = ConsoleManager.get() @@ -706,10 +694,7 @@ def section(cls, title: str) -> None: @classmethod def chapter(cls, title: str) -> None: - """ - Formats a chapter header with bold magenta text, uppercase, and - padding. - """ + """Format a chapter header in bold magenta, uppercase.""" width = ConsoleManager._detect_width() symbol = '—' full_title = f' {title.upper()} ' diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 1f357333..6e81a467 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -49,9 +49,7 @@ def _validate_url(url: str) -> None: def _filename_for_id_from_url(data_id: int | str, url: str) -> str: - """ - Return local filename like 'ed-12.xye' using extension from the URL. - """ + """Return local filename like 'ed-12.xye' using extension from the URL.""" suffix = pathlib.Path(urlparse(url).path).suffix # includes leading dot ('.cif', '.xye', ...) # If URL has no suffix, fall back to no extension. return f'ed-{data_id}{suffix}' @@ -72,9 +70,7 @@ def _normalize_known_hash(value: str | None) -> str | None: def _fetch_data_index() -> dict: - """ - Fetch & cache the diffraction data index.json and return it as dict. - """ + """Fetch & cache the diffraction data index.json and return it as dict.""" index_url = 'https://raw.githubusercontent.com/easyscience/data/refs/heads/master/diffraction/index.json' _validate_url(index_url) @@ -99,8 +95,7 @@ def _fetch_data_index() -> dict: @functools.lru_cache(maxsize=1) def _fetch_tutorials_index() -> dict: """ - Fetch & cache the tutorials index.json from gh-pages and return it - as dict. + Fetch and cache the tutorials index.json from gh-pages. The index is fetched from: https://easyscience.github.io/diffraction-lib/{version}/tutorials/index.json @@ -231,8 +226,7 @@ def package_version(package_name: str) -> str | None: def stripped_package_version(package_name: str) -> str | None: """ - Get the installed version of the specified package, stripped of any - local version part. + Get installed package version, stripped of local version parts. Returns only the public version segment (e.g., '1.2.3' or '1.2.3.post4'), omitting any local segment (e.g., '+d136'). @@ -260,8 +254,7 @@ def stripped_package_version(package_name: str) -> str | None: def _is_dev_version(package_name: str) -> bool: """ - Check if the installed package version is a development/local - version. + Check if the installed package version is a dev version. A version is considered "dev" if: - The raw version contains '+dev', '+dirty', or '+devdirty' (local suffixes from versioningit) - The @@ -543,8 +536,7 @@ def render_table( def render_cif(cif_text: str) -> None: """ - Display the CIF text as a formatted table in Jupyter Notebook or - terminal. + Display CIF text as a formatted table in Jupyter or terminal. Parameters ---------- @@ -573,8 +565,7 @@ def tof_to_d( quad_eps: float = 1e-20, ) -> np.ndarray: """ - Convert time-of-flight (TOF) to d-spacing using a quadratic - calibration. + Convert time-of-flight to d-spacing using quadratic calibration. Model: TOF = offset + linear * d + quad * d² @@ -709,8 +700,7 @@ def sin_theta_over_lambda_to_d_spacing(sin_theta_over_lambda: object) -> object: def get_value_from_xye_header(file_path: str, key: str) -> float: """ - Extracts a floating point value from the first line of the file, - corresponding to the given key. + Extract a float from the first line of the file by key. Parameters ---------- @@ -743,8 +733,7 @@ def get_value_from_xye_header(file_path: str, key: str) -> float: def str_to_ufloat(s: Optional[str], default: Optional[float] = None) -> UFloat: """ - Parse a CIF-style numeric string into a ``ufloat`` with an optional - uncertainty. + Parse a CIF-style numeric string into a ufloat. Examples of supported input: - "3.566" → ufloat(3.566, nan) - "3.566(2)" → ufloat(3.566, 0.002) - None → ufloat(default, nan) From ed09e558b56a9adf77fdb57726fc2bcc7ccab032 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 21:33:59 +0100 Subject: [PATCH 36/48] More docstring fixes --- src/easydiffraction/analysis/analysis.py | 10 +++---- .../analysis/calculators/base.py | 4 +-- .../joint_fit_experiments/factory.py | 2 +- .../analysis/fit_helpers/metrics.py | 2 +- .../analysis/minimizers/base.py | 2 +- src/easydiffraction/core/category.py | 4 +-- src/easydiffraction/core/datablock.py | 6 ++--- src/easydiffraction/core/diagnostic.py | 2 +- src/easydiffraction/core/guard.py | 2 +- .../categories/background/chebyshev.py | 4 ++- .../experiment/categories/data/bragg_pd.py | 10 +++---- .../experiment/categories/data/bragg_sc.py | 6 ++--- .../experiment/categories/data/total_pd.py | 2 +- .../categories/experiment_type/default.py | 2 +- .../experiment/categories/extinction/shelx.py | 2 +- .../datablocks/experiment/item/base.py | 4 +-- .../datablocks/experiment/item/factory.py | 2 +- .../datablocks/structure/item/base.py | 2 +- src/easydiffraction/display/base.py | 2 +- src/easydiffraction/display/utils.py | 2 +- src/easydiffraction/io/cif/serialize.py | 2 +- src/easydiffraction/project/project.py | 2 +- src/easydiffraction/summary/summary.py | 2 +- src/easydiffraction/utils/logging.py | 4 +-- src/easydiffraction/utils/utils.py | 4 +-- tmp/show_w505.py | 27 +++++++++++++++++++ 26 files changed, 71 insertions(+), 42 deletions(-) create mode 100644 tmp/show_w505.py diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 5abe5a87..874017cb 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -283,7 +283,7 @@ def show_all_params(self) -> None: tabler.render(filtered_df) def show_fittable_params(self) -> None: - """Print a table with parameters that can be included in fitting.""" + """Print all fittable parameters.""" structures_params = self.project.structures.fittable_parameters experiments_params = self.project.experiments.fittable_parameters @@ -315,7 +315,7 @@ def show_fittable_params(self) -> None: tabler.render(filtered_df) def show_free_params(self) -> None: - """Print a table with only currently-free (varying) parameters.""" + """Print only currently free (varying) parameters.""" structures_params = self.project.structures.free_parameters experiments_params = self.project.experiments.free_parameters free_params = structures_params + experiments_params @@ -475,7 +475,7 @@ def show_current_minimizer(self) -> None: @staticmethod def show_available_minimizers() -> None: - """Print a table of available minimizer drivers on this system.""" + """Print available minimizer drivers on this system.""" MinimizerFactory.show_supported() @property @@ -581,7 +581,7 @@ def show_constraints(self) -> None: ) def apply_constraints(self) -> None: - """Apply the currently defined constraints to the active project.""" + """Apply currently defined constraints to the project.""" if not self.constraints._items: log.warning('No constraints defined.') return @@ -724,7 +724,7 @@ def as_cif(self) -> str: return analysis_to_cif(self) def show_as_cif(self) -> None: - """Render the analysis section as CIF in a formatted console view.""" + """Render the analysis section as CIF in console.""" cif_text: str = self.as_cif() paragraph_title: str = 'Analysis 🧮 info as cif' console.paragraph(paragraph_title) diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index 78f78a30..bd667ac8 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -23,7 +23,7 @@ def name(self) -> str: @property @abstractmethod def engine_imported(self) -> bool: - """Whether the underlying calculation library could be imported.""" + """True if the underlying calculation library is available.""" pass @abstractmethod @@ -33,7 +33,7 @@ def calculate_structure_factors( experiment: ExperimentBase, called_by_minimizer: bool, ) -> None: - """Calculate structure factors for one structure-experiment pair.""" + """Calculate structure factors for one experiment.""" pass @abstractmethod diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py index 64e6bbc7..57666098 100644 --- a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py +++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Joint-fit-experiments factory — delegates entirely to ``FactoryBase``.""" +"""Joint-fit-experiments factory — delegates to ``FactoryBase``.""" from __future__ import annotations diff --git a/src/easydiffraction/analysis/fit_helpers/metrics.py b/src/easydiffraction/analysis/fit_helpers/metrics.py index f6b3b3aa..dee08f86 100644 --- a/src/easydiffraction/analysis/fit_helpers/metrics.py +++ b/src/easydiffraction/analysis/fit_helpers/metrics.py @@ -42,7 +42,7 @@ def calculate_weighted_r_factor( weights: np.ndarray, ) -> float: """ - Calculate the weighted R-factor between observed and calculated data. + Calculate weighted R-factor between observed and calculated data. Parameters ---------- diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py index 77a21b63..f8499dad 100644 --- a/src/easydiffraction/analysis/minimizers/base.py +++ b/src/easydiffraction/analysis/minimizers/base.py @@ -92,7 +92,7 @@ def _sync_result_to_parameters( raw_result: object, parameters: List[object], ) -> None: - """Copy values from ``raw_result`` back to ``parameters`` in-place.""" + """Copy raw_result values back to parameters in-place.""" pass def _finalize_fit( diff --git a/src/easydiffraction/core/category.py b/src/easydiffraction/core/category.py index 010cba2a..25e81894 100644 --- a/src/easydiffraction/core/category.py +++ b/src/easydiffraction/core/category.py @@ -36,7 +36,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def unique_name(self) -> str: - """Fully qualified name combining datablock, category, and entry.""" + """Fully qualified name: datablock, category, entry.""" parts = [ self._identity.datablock_entry_name, self._identity.category_code, @@ -200,7 +200,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def unique_name(self) -> str | None: - """Return None; collections do not carry their own unique name.""" + """Return None; collections have no unique name.""" return None @property diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 16e4c92e..6a1ef289 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -77,7 +77,7 @@ def categories(self) -> list: @property def parameters(self) -> list: - """All parameters from all categories contained in this datablock.""" + """All parameters from all categories in this datablock.""" params = [] for v in self.categories: params.extend(v.parameters) @@ -152,7 +152,7 @@ def __str__(self) -> str: @property def unique_name(self) -> str | None: - """Return None; collections do not carry their own unique name.""" + """Return None; collections have no unique name.""" return None @property @@ -165,7 +165,7 @@ def parameters(self) -> list: @property def fittable_parameters(self) -> list: - """All non-constrained Parameter instances in this collection.""" + """All non-constrained Parameters in this collection.""" return [p for p in self.parameters if isinstance(p, Parameter) and not p.constrained] @property diff --git a/src/easydiffraction/core/diagnostic.py b/src/easydiffraction/core/diagnostic.py index f6248e8f..dc5ea4c4 100644 --- a/src/easydiffraction/core/diagnostic.py +++ b/src/easydiffraction/core/diagnostic.py @@ -57,7 +57,7 @@ def attr_error( allowed: set[str], label: str = 'Allowed', ) -> None: - """Log access to an unknown attribute and suggest closest key.""" + """Log unknown attribute access and suggest closest key.""" suggestion = Diagnostics._build_suggestion(key, allowed) # Use consistent (label) logic for allowed hint = suggestion or Diagnostics._build_allowed(allowed, label=label) diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index 586a8e72..75e45b5e 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -12,7 +12,7 @@ class GuardedBase(ABC): - """Base class enforcing controlled attribute access and parent linkage.""" + """Base class enforcing controlled attribute access and linkage.""" _diagnoser = Diagnostics() diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 3ed8e332..c2da0642 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -99,6 +99,9 @@ def id(self) -> StringDescriptor: @id.setter def id(self, value: str) -> None: self._id.value = value + + @property + def order(self) -> NumericDescriptor: """ Order used in a Chebyshev polynomial background term. @@ -110,7 +113,6 @@ def id(self, value: str) -> None: @order.setter def order(self, value: float) -> None: - """Set the polynomial order.""" self._order.value = value @property diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index dbeac7c7..c2ea345f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -193,7 +193,7 @@ def calc_status(self) -> StringDescriptor: class PdCwlDataPointMixin: - """Mixin for powder diffraction data points with constant wavelength.""" + """Mixin for CWL powder diffraction data points.""" def __init__(self) -> None: super().__init__() @@ -274,7 +274,7 @@ class PdCwlDataPoint( # But also says, that in fact, it is just for consistency. And both # orders work. ): - """Powder diffraction data point for constant-wavelength experiments.""" + """Powder diffraction data point for CWL experiments.""" def __init__(self) -> None: super().__init__() @@ -321,7 +321,7 @@ def _set_intensity_meas(self, values: object) -> None: p.intensity_meas._value = v def _set_intensity_meas_su(self, values: object) -> None: - """Helper method to set standard uncertainty of measured intensity.""" + """Set standard uncertainty of measured intensity values.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas_su._value = v @@ -524,7 +524,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def two_theta(self) -> np.ndarray: - """Get the 2θ values for data points included in calculations.""" + """Get 2θ values for data points included in calculations.""" return np.fromiter( (p.two_theta.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? @@ -599,7 +599,7 @@ def _update(self, called_by_minimizer: bool = False) -> None: @property def time_of_flight(self) -> np.ndarray: - """Get the TOF values for data points included in calculations.""" + """Get TOF values for data points included in calculations.""" return np.fromiter( (p.time_of_flight.value for p in self._calc_items), dtype=float, # TODO: needed? DataTypes.NUMERIC? diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 58d68808..25dd6d5d 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -26,7 +26,7 @@ class Refln(CategoryItem): - """Single reflection for single crystal diffraction data category.""" + """Single reflection for single-crystal diffraction data.""" def __init__(self) -> None: super().__init__() @@ -295,7 +295,7 @@ def _set_intensity_meas(self, values: object) -> None: p.intensity_meas._value = v def _set_intensity_meas_su(self, values: object) -> None: - """Helper method to set standard uncertainty of measured intensity.""" + """Set standard uncertainty of measured intensity values.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas_su._value = v @@ -419,7 +419,7 @@ def intensity_meas_su(self) -> np.ndarray: @property def intensity_calc(self) -> np.ndarray: - """Calculated structure-factor intensities for all reflections.""" + """Calculated intensities for all reflections.""" return np.fromiter( (p.intensity_calc.value for p in self._items), dtype=float, diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index f21ca237..a351f196 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -204,7 +204,7 @@ def _set_g_r_meas(self, values: object) -> None: p.g_r_meas._value = v def _set_g_r_meas_su(self, values: object) -> None: - """Helper method to set standard uncertainty of measured G(r).""" + """Set standard uncertainty of measured G(r) values.""" for p, v in zip(self._items, values, strict=True): p.g_r_meas_su._value = v diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 9340ed51..1b9d3811 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -27,7 +27,7 @@ @ExperimentTypeFactory.register class ExperimentType(CategoryItem): - """Container of categorical attributes defining experiment flavor.""" + """Container of attributes defining the experiment type.""" type_info = TypeInfo( tag='default', diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py index 1eed5efb..dd736a1a 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py @@ -17,7 +17,7 @@ @ExtinctionFactory.register class ShelxExtinction(CategoryItem): - """Shelx-style isotropic extinction correction for single crystals.""" + """Shelx-style extinction correction for single crystals.""" type_info = TypeInfo( tag='shelx', diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 7f902985..2dddee44 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -68,7 +68,7 @@ def name(self, new: str) -> None: @property def type(self) -> object: # TODO: Consider another name - """Experiment type descriptor (sample form, probe, beam mode).""" + """Experiment type: sample form, probe, beam mode.""" return self._type @property @@ -601,7 +601,7 @@ def excluded_regions_type(self, new_type: str) -> None: console.print(new_type) def show_supported_excluded_regions_types(self) -> None: - """Print a table of supported excluded-regions collection types.""" + """Print a table of supported excluded-regions types.""" ExcludedRegionsFactory.show_supported() def show_current_excluded_regions_type(self) -> None: diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index 0b021171..6406ed30 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -75,7 +75,7 @@ def _create_experiment_type( radiation_probe: str | None = None, scattering_type: str | None = None, ) -> ExperimentType: - """Construct an ExperimentType, using defaults for omitted values.""" + """Construct ExperimentType with defaults for omitted values.""" # Note: validation of input values is done via Descriptor setter # methods diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index 3f799980..80d8f76a 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -245,7 +245,7 @@ def show_current_atom_sites_type(self) -> None: # ------------------------------------------------------------------ def show(self) -> None: - """Display an ASCII projection of the structure on a 2D plane.""" + """Display an ASCII projection of the structure in 2D.""" console.paragraph(f"Structure 🧩 '{self.name}'") console.print('Not implemented yet.') diff --git a/src/easydiffraction/display/base.py b/src/easydiffraction/display/base.py index bf97c68a..6ffc0698 100644 --- a/src/easydiffraction/display/base.py +++ b/src/easydiffraction/display/base.py @@ -141,7 +141,7 @@ def supported_engines(cls) -> List[str]: @classmethod def descriptions(cls) -> List[Tuple[str, str]]: - """Return pairs of engine name and human-friendly description.""" + """Return (name, description) pairs for each engine.""" items = cls._registry().items() return [(name, config.get('description')) for name, config in items] diff --git a/src/easydiffraction/display/utils.py b/src/easydiffraction/display/utils.py index b4e3dc9d..17c6fa94 100644 --- a/src/easydiffraction/display/utils.py +++ b/src/easydiffraction/display/utils.py @@ -18,7 +18,7 @@ class JupyterScrollManager: - """Ensures that Jupyter output cells are not scrollable (applied once).""" + """Ensures Jupyter output cells are not scrollable (once).""" _applied: ClassVar[bool] = False diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index af83f4ee..f885aed7 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -159,7 +159,7 @@ def datablock_collection_to_cif(collection: object) -> str: def project_info_to_cif(info: object) -> str: - """Render ProjectInfo to CIF text (id, title, description, dates).""" + """Render ProjectInfo to CIF text (id, title, description).""" name = f'{info.name}' title = f'{info.title}' diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 7f757a2a..af5a5922 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -75,7 +75,7 @@ def info(self) -> ProjectInfo: @property def name(self) -> str: - """Convenience property to access the project's name directly.""" + """Convenience property for the project name.""" return self._info.name @property diff --git a/src/easydiffraction/summary/summary.py b/src/easydiffraction/summary/summary.py index f8ebc15c..9d715bac 100644 --- a/src/easydiffraction/summary/summary.py +++ b/src/easydiffraction/summary/summary.py @@ -205,7 +205,7 @@ def show_fitting_details(self) -> None: # ------------------------------------------ def as_cif(self) -> str: - """Export the final fitted data and analysis results as CIF format.""" + """Export fitted data and analysis results as CIF.""" from easydiffraction.io.cif.serialize import summary_to_cif return summary_to_cif(self) diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index bb4c3eb9..2c9f013f 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -324,7 +324,7 @@ def _suppress_traceback(logger: object) -> object: """ def suppress_jupyter_traceback(*args: object, **kwargs: object) -> None: - """Log only the exception message, suppressing the traceback.""" + """Log only the exception message.""" try: _evalue = ( args[2] if len(args) > 2 else kwargs.get('_evalue') or kwargs.get('evalue') @@ -475,7 +475,7 @@ def configure( @classmethod def _install_jupyter_traceback_suppressor(cls) -> None: - """Install traceback suppressor in Jupyter, safely and lint- clean.""" + """Install the Jupyter traceback suppressor safely.""" ExceptionHookManager.install_jupyter_traceback_suppressor(cls._logger) # ===== Helpers ===== diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 6e81a467..fdba2e74 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -49,7 +49,7 @@ def _validate_url(url: str) -> None: def _filename_for_id_from_url(data_id: int | str, url: str) -> str: - """Return local filename like 'ed-12.xye' using extension from the URL.""" + """Return local filename using the extension from the URL.""" suffix = pathlib.Path(urlparse(url).path).suffix # includes leading dot ('.cif', '.xye', ...) # If URL has no suffix, fall back to no extension. return f'ed-{data_id}{suffix}' @@ -70,7 +70,7 @@ def _normalize_known_hash(value: str | None) -> str | None: def _fetch_data_index() -> dict: - """Fetch & cache the diffraction data index.json and return it as dict.""" + """Fetch and cache the diffraction data index.json.""" index_url = 'https://raw.githubusercontent.com/easyscience/data/refs/heads/master/diffraction/index.json' _validate_url(index_url) diff --git a/tmp/show_w505.py b/tmp/show_w505.py new file mode 100644 index 00000000..6e606803 --- /dev/null +++ b/tmp/show_w505.py @@ -0,0 +1,27 @@ +files_lines = [ + ('src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py', [196,277,324,527,602]), + ('src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py', [29,298,422]), + ('src/easydiffraction/datablocks/experiment/categories/data/total_pd.py', [207]), + ('src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py', [30]), + ('src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py', [20]), + ('src/easydiffraction/datablocks/experiment/item/base.py', [71,604]), + ('src/easydiffraction/datablocks/experiment/item/factory.py', [78]), + ('src/easydiffraction/datablocks/structure/item/base.py', [248]), + ('src/easydiffraction/display/base.py', [144]), + ('src/easydiffraction/display/utils.py', [21]), + ('src/easydiffraction/io/cif/serialize.py', [162]), + ('src/easydiffraction/project/project.py', [78]), + ('src/easydiffraction/summary/summary.py', [208]), + ('src/easydiffraction/utils/logging.py', [327,478]), + ('src/easydiffraction/utils/utils.py', [52,73]), +] +for fname, lines in files_lines: + with open(fname) as f: + content = f.readlines() + for ln in lines: + lo = max(0, ln-2) + hi = min(len(content), ln+1) + for i, l in enumerate(content[lo:hi]): + print(f'{fname}:{lo+i+1}: {l}', end='') + print('---') + From 198d82fdf423f0d733eec72474d42d36c499d50f Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 21:38:17 +0100 Subject: [PATCH 37/48] Remove unused script --- tools/find_missing_docstrings.py | 44 -------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 tools/find_missing_docstrings.py diff --git a/tools/find_missing_docstrings.py b/tools/find_missing_docstrings.py deleted file mode 100644 index cb04709f..00000000 --- a/tools/find_missing_docstrings.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -"""Find all public methods/functions missing docstrings in src/.""" -import ast -import os -import sys - - -def has_docstring(node): - return ( - isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) - and node.body - and isinstance(node.body[0], ast.Expr) - and isinstance(node.body[0].value, ast.Constant) - and isinstance(node.body[0].value.value, str) - ) - - -def is_public(name): - return not name.startswith('_') - - -results = [] -src_root = sys.argv[1] if len(sys.argv) > 1 else 'src' -for root, dirs, files in os.walk(src_root): - dirs[:] = sorted([d for d in dirs if d != '__pycache__' and d != '_vendored']) - for f in sorted(files): - if not f.endswith('.py'): - continue - path = os.path.join(root, f) - try: - with open(path) as fh: - tree = ast.parse(fh.read()) - except Exception as e: - print(f'Error parsing {path}: {e}') - continue - for node in ast.walk(tree): - if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): - if is_public(node.name) and not has_docstring(node): - results.append(f'{path}:{node.lineno}: {node.name}') - -for r in results: - print(r) -print(f'Total: {len(results)} methods missing docstrings') - From bb3650ac549a9729559507a5ace13a1dda8a543d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 21:58:42 +0100 Subject: [PATCH 38/48] Fix D401: use imperative mood in all docstring summaries --- pixi.lock | 4 +-- pyproject.toml | 5 +++- .../analysis/calculators/crysfml.py | 6 ++--- .../analysis/calculators/cryspy.py | 4 +-- .../analysis/calculators/pdffit.py | 4 +-- .../analysis/minimizers/dfols.py | 4 +-- .../analysis/minimizers/lmfit.py | 10 +++---- src/easydiffraction/core/singleton.py | 14 +++++----- src/easydiffraction/core/variable.py | 2 +- .../experiment/categories/data/bragg_pd.py | 16 ++++++------ .../experiment/categories/data/bragg_sc.py | 14 +++++----- .../experiment/categories/data/total_pd.py | 10 +++---- src/easydiffraction/project/project_info.py | 2 +- src/easydiffraction/utils/environment.py | 4 +-- src/easydiffraction/utils/logging.py | 2 +- src/easydiffraction/utils/utils.py | 2 +- tmp/show_d401.py | 26 +++++++++++++++++++ 17 files changed, 79 insertions(+), 50 deletions(-) create mode 100644 tmp/show_d401.py diff --git a/pixi.lock b/pixi.lock index 9bca01f3..8c406175 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty37 - sha256: 720a9d523687c2effefefbc159a74223d0746814d2fb351030677345ef64bdce + version: 0.10.2+devdirty40 + sha256: 13a174f3b1163dada6f0a52896bd23b16d7f04fd8029605672bb5c3fa3ef6643 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index d31ae21f..72b2560a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -279,7 +279,10 @@ ignore = [ # The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint] 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc # Temporary: - #'D205', + 'D100', + 'D101', + 'D104', + 'D105', 'DTZ005', ] diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 29a46ab9..34624e2b 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -108,7 +108,7 @@ def _adjust_pattern_length( target_length: int, ) -> List[float]: """ - Adjusts the length of the pattern to match the target length. + Adjust the pattern length to match the target length. Parameters ---------- @@ -160,7 +160,7 @@ def _convert_structure_to_dict( structure: Structure, ) -> Dict[str, Any]: """ - Converts a structure into a dictionary format. + Convert a structure into a dictionary format. Parameters ---------- @@ -205,7 +205,7 @@ def _convert_experiment_to_dict( experiment: ExperimentBase, ) -> Dict[str, Any]: """ - Converts an experiment into a dictionary format. + Convert an experiment into a dictionary format. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 6f1e4ec0..b6a0e4d6 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -357,7 +357,7 @@ def _convert_structure_to_cryspy_cif( structure: Structure, ) -> str: """ - Converts a structure to a Cryspy CIF string. + Convert a structure to a Cryspy CIF string. Parameters ---------- @@ -377,7 +377,7 @@ def _convert_experiment_to_cryspy_cif( linked_structure: object, ) -> str: """ - Converts an experiment to a Cryspy CIF string. + Convert an experiment to a Cryspy CIF string. Parameters ---------- diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py index 5602d949..47b434e8 100644 --- a/src/easydiffraction/analysis/calculators/pdffit.py +++ b/src/easydiffraction/analysis/calculators/pdffit.py @@ -59,9 +59,9 @@ def name(self) -> str: """Short identifier of this calculator engine.""" return 'pdffit' - def calculate_structure_factors(self, structures: object, experiments: object) -> list: + def calculate_structure_factors(self, structures: object, experiments: object,) -> list: """ - Placeholder — PDF does not compute HKL structure factors. + Return an empty list; PDF does not compute structure factors. Parameters ---------- diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py index 1f47a5e8..fad9ad06 100644 --- a/src/easydiffraction/analysis/minimizers/dfols.py +++ b/src/easydiffraction/analysis/minimizers/dfols.py @@ -55,7 +55,7 @@ def _sync_result_to_parameters( raw_result: object, ) -> None: """ - Synchronizes the result from the solver to the parameters. + Synchronize the solver result back to the parameters. Parameters ---------- @@ -77,7 +77,7 @@ def _sync_result_to_parameters( def _check_success(self, raw_result: object) -> bool: """ - Determines success from DFO-LS result dictionary. + Determine success from DFO-LS result dictionary. Parameters ---------- diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py index 18484c59..771bacad 100644 --- a/src/easydiffraction/analysis/minimizers/lmfit.py +++ b/src/easydiffraction/analysis/minimizers/lmfit.py @@ -40,7 +40,7 @@ def _prepare_solver_args( parameters: List[object], ) -> Dict[str, object]: """ - Prepares the solver arguments for the lmfit minimizer. + Prepare the solver arguments for the lmfit minimizer. Parameters ---------- @@ -66,7 +66,7 @@ def _prepare_solver_args( def _run_solver(self, objective_function: object, **kwargs: object) -> object: """ - Runs the lmfit solver. + Run the lmfit solver. Parameters ---------- @@ -96,7 +96,7 @@ def _sync_result_to_parameters( raw_result: object, ) -> None: """ - Synchronizes the result from the solver to the parameters. + Synchronize the result from the solver to the parameters. Parameters ---------- @@ -117,7 +117,7 @@ def _sync_result_to_parameters( def _check_success(self, raw_result: object) -> bool: """ - Determines success from lmfit MinimizerResult. + Determine success from lmfit MinimizerResult. Parameters ---------- @@ -140,7 +140,7 @@ def _iteration_callback( **kwargs: object, ) -> None: """ - Callback function for each iteration of the minimizer. + Handle each iteration callback of the minimizer. Parameters ---------- diff --git a/src/easydiffraction/core/singleton.py b/src/easydiffraction/core/singleton.py index a4b75648..9033822a 100644 --- a/src/easydiffraction/core/singleton.py +++ b/src/easydiffraction/core/singleton.py @@ -27,7 +27,7 @@ class SingletonBase: @classmethod def get(cls: Type[T]) -> T: - """Returns the shared instance, creating it if needed.""" + """Return the shared instance, creating it if needed.""" if cls._instance is None: cls._instance = cls() return cls._instance @@ -44,12 +44,12 @@ def __init__(self) -> None: self._uid_map: Dict[str, Any] = {} def get_uid_map(self) -> Dict[str, Any]: - """Returns the current UID-to-Parameter map.""" + """Return the current UID-to-Parameter map.""" return self._uid_map def add_to_uid_map(self, parameter: object) -> None: """ - Adds a single Parameter or Descriptor object to the UID map. + Add a single Parameter or Descriptor object to the UID map. Only Descriptor or Parameter instances are allowed (not Components or others). @@ -65,7 +65,7 @@ def add_to_uid_map(self, parameter: object) -> None: def replace_uid(self, old_uid: str, new_uid: str) -> None: """ - Replaces an existing UID key in the UID map with a new UID. + Replace an existing UID key in the UID map with a new UID. Moves the associated parameter from old_uid to new_uid. Raises a KeyError if the old_uid doesn't exist. @@ -107,7 +107,7 @@ def __init__(self) -> None: def set_aliases(self, aliases: object) -> None: """ - Sets the alias map (name → parameter wrapper). + Set the alias map (name → parameter wrapper). Called when user registers parameter aliases like: alias='biso_La', param=model.atom_sites['La'].b_iso @@ -116,7 +116,7 @@ def set_aliases(self, aliases: object) -> None: def set_constraints(self, constraints: object) -> None: """ - Sets the constraints and triggers parsing into internal format. + Set the constraints and triggers parsing into internal format. Called when user registers expressions like: lhs_alias='occ_Ba', rhs_expr='1 - occ_La' @@ -138,7 +138,7 @@ def _parse_constraints(self) -> None: def apply(self) -> None: """ - Evaluates constraints and applies them to dependent parameters. + Evaluate constraints and applies them to dependent parameters. For each constraint: - Evaluate RHS using current values of aliases - Locate the dependent parameter by alias → uid → param diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index d4733747..19465a1e 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -386,7 +386,7 @@ def __init__( **kwargs: object, ) -> None: """ - String descriptor bound to a CIF handler. + Initialize a string descriptor bound to a CIF handler. Parameters ---------- diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index c2ea345f..0e826d2a 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -311,12 +311,12 @@ class PdDataBase(CategoryCollection): # Should be set only once def _set_point_id(self, values: object) -> None: - """Helper method to set point IDs.""" + """Set point IDs.""" for p, v in zip(self._items, values, strict=True): p.point_id._value = v def _set_intensity_meas(self, values: object) -> None: - """Helper method to set measured intensity.""" + """Set measured intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas._value = v @@ -328,22 +328,22 @@ def _set_intensity_meas_su(self, values: object) -> None: # Can be set multiple times def _set_d_spacing(self, values: object) -> None: - """Helper method to set d-spacing values.""" + """Set d-spacing values.""" for p, v in zip(self._calc_items, values, strict=True): p.d_spacing._value = v def _set_intensity_calc(self, values: object) -> None: - """Helper method to set calculated intensity.""" + """Set calculated intensity.""" for p, v in zip(self._calc_items, values, strict=True): p.intensity_calc._value = v def _set_intensity_bkg(self, values: object) -> None: - """Helper method to set background intensity.""" + """Set background intensity.""" for p, v in zip(self._calc_items, values, strict=True): p.intensity_bkg._value = v def _set_calc_status(self, values: object) -> None: - """Helper method to set refinement status.""" + """Set refinement status.""" for p, v in zip(self._items, values, strict=True): if v: p.calc_status._value = 'incl' @@ -493,7 +493,7 @@ def __init__(self) -> None: # Should be set only once def _create_items_set_xcoord_and_id(self, values: object) -> None: - """Helper method to set 2θ values.""" + """Set 2θ values.""" # TODO: split into multiple methods # Create items @@ -566,7 +566,7 @@ def __init__(self) -> None: # Should be set only once def _create_items_set_xcoord_and_id(self, values: object) -> None: - """Helper method to set time-of-flight values.""" + """Set time-of-flight values.""" # TODO: split into multiple methods # Create items diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py index 25dd6d5d..d19eb75f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py @@ -267,7 +267,7 @@ def _create_items_set_hkl_and_id( indices_k: object, indices_l: object, ) -> None: - """Helper method to set Miller indices.""" + """Set Miller indices.""" # TODO: split into multiple methods # Create items @@ -285,12 +285,12 @@ def _create_items_set_hkl_and_id( self._set_id([str(i + 1) for i in range(indices_h.size)]) def _set_id(self, values: object) -> None: - """Helper method to set reflection IDs.""" + """Set reflection IDs.""" for p, v in zip(self._items, values, strict=True): p.id._value = v def _set_intensity_meas(self, values: object) -> None: - """Helper method to set measured intensity.""" + """Set measured intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_meas._value = v @@ -300,24 +300,24 @@ def _set_intensity_meas_su(self, values: object) -> None: p.intensity_meas_su._value = v def _set_wavelength(self, values: object) -> None: - """Helper method to set wavelength.""" + """Set wavelength.""" for p, v in zip(self._items, values, strict=True): p.wavelength._value = v # Can be set multiple times def _set_d_spacing(self, values: object) -> None: - """Helper method to set d-spacing values.""" + """Set d-spacing values.""" for p, v in zip(self._items, values, strict=True): p.d_spacing._value = v def _set_sin_theta_over_lambda(self, values: object) -> None: - """Helper method to set sin(theta)/lambda values.""" + """Set sin(theta)/lambda values.""" for p, v in zip(self._items, values, strict=True): p.sin_theta_over_lambda._value = v def _set_intensity_calc(self, values: object) -> None: - """Helper method to set calculated intensity.""" + """Set calculated intensity.""" for p, v in zip(self._items, values, strict=True): p.intensity_calc._value = v diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index a351f196..e6c94aeb 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -194,12 +194,12 @@ class TotalDataBase(CategoryCollection): # Should be set only once def _set_point_id(self, values: object) -> None: - """Helper method to set point IDs.""" + """Set point IDs.""" for p, v in zip(self._items, values, strict=True): p.point_id._value = v def _set_g_r_meas(self, values: object) -> None: - """Helper method to set measured G(r).""" + """Set measured G(r).""" for p, v in zip(self._items, values, strict=True): p.g_r_meas._value = v @@ -211,12 +211,12 @@ def _set_g_r_meas_su(self, values: object) -> None: # Can be set multiple times def _set_g_r_calc(self, values: object) -> None: - """Helper method to set calculated G(r).""" + """Set calculated G(r).""" for p, v in zip(self._calc_items, values, strict=True): p.g_r_calc._value = v def _set_calc_status(self, values: object) -> None: - """Helper method to set calculation status.""" + """Set calculation status.""" for p, v in zip(self._items, values, strict=True): if v: p.calc_status._value = 'incl' @@ -339,7 +339,7 @@ def __init__(self) -> None: # Should be set only once def _create_items_set_xcoord_and_id(self, values: object) -> None: - """Helper method to set r values.""" + """Set r values.""" # TODO: split into multiple methods # Create items diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py index f79d73de..d062d522 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_info.py @@ -118,7 +118,7 @@ def update_last_modified(self) -> None: self._last_modified = datetime.datetime.now() def parameters(self) -> None: - """Placeholder for parameter listing.""" + """List parameters (not implemented).""" pass # TODO: Consider moving to io.cif.serialize diff --git a/src/easydiffraction/utils/environment.py b/src/easydiffraction/utils/environment.py index c0deba61..2c518092 100644 --- a/src/easydiffraction/utils/environment.py +++ b/src/easydiffraction/utils/environment.py @@ -35,7 +35,7 @@ def in_warp() -> bool: def in_pycharm() -> bool: """ - Determines if the current environment is PyCharm. + Check whether the current environment is PyCharm. Returns ------- @@ -47,7 +47,7 @@ def in_pycharm() -> bool: def in_colab() -> bool: """ - Determines if the current environment is Google Colab. + Check whether the current environment is Google Colab. Returns ------- diff --git a/src/easydiffraction/utils/logging.py b/src/easydiffraction/utils/logging.py index 2c9f013f..876d7956 100644 --- a/src/easydiffraction/utils/logging.py +++ b/src/easydiffraction/utils/logging.py @@ -684,7 +684,7 @@ def paragraph(cls, title: str) -> None: @classmethod def section(cls, title: str) -> None: - """Formats a section header with bold green text.""" + """Format a section header with bold green text.""" full_title = f'{title.upper()}' line = '—' * len(full_title) formatted = f'[bold green]{line}\n{full_title}\n{line}[/bold green]' diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index fdba2e74..486b9c7a 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -308,7 +308,7 @@ def _get_version_for_url(package_name: str = 'easydiffraction') -> str: def _safe_urlopen(request_or_url: object) -> object: # type: ignore[no-untyped-def] """ - Wrapper for urlopen with prior validation. + Open a URL with prior validation. Centralises lint suppression for validated HTTPS requests. """ diff --git a/tmp/show_d401.py b/tmp/show_d401.py new file mode 100644 index 00000000..8ac806fc --- /dev/null +++ b/tmp/show_d401.py @@ -0,0 +1,26 @@ +files_lines = [ + ('src/easydiffraction/analysis/calculators/crysfml.py', [108,160,205]), + ('src/easydiffraction/analysis/calculators/cryspy.py', [357,377]), + ('src/easydiffraction/analysis/calculators/pdffit.py', [61]), + ('src/easydiffraction/analysis/minimizers/dfols.py', [55,77]), + ('src/easydiffraction/analysis/minimizers/lmfit.py', [40,66,96,117,140]), + ('src/easydiffraction/core/singleton.py', [28,45,49,65,107,116,138]), + ('src/easydiffraction/core/variable.py', [386]), + ('src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py', [312,317,329,334,339,344,494,567]), + ('src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py', [268,286,291,301,308,313,318]), + ('src/easydiffraction/datablocks/experiment/categories/data/total_pd.py', [195,200,212,217,340]), + ('src/easydiffraction/project/project_info.py', [119]), + ('src/easydiffraction/utils/environment.py', [35,47]), + ('src/easydiffraction/utils/logging.py', [685]), + ('src/easydiffraction/utils/utils.py', [308]), +] +for fname, lines in files_lines: + with open(fname) as f: + content = f.readlines() + for ln in lines: + lo = max(0, ln-2) + hi = min(len(content), ln+2) + for i, l in enumerate(content[lo:hi]): + print(f'{fname}:{lo+i+1}: {l}', end='') + print('---') + From 9a2fe5b097230c5add5c76b2db7f8c0e0174cd5a Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 22:03:24 +0100 Subject: [PATCH 39/48] More formatting and linting --- src/easydiffraction/analysis/calculators/pdffit.py | 6 +++++- src/easydiffraction/analysis/fitting.py | 4 ++-- .../datablocks/experiment/categories/data/total_pd.py | 5 ++++- .../experiment/categories/excluded_regions/factory.py | 4 +++- .../datablocks/experiment/categories/instrument/cwl.py | 10 ++++++++-- .../datablocks/experiment/categories/instrument/tof.py | 10 ++++++++-- .../datablocks/experiment/categories/peak/cwl.py | 5 ++++- .../datablocks/experiment/categories/peak/tof.py | 5 ++++- .../experiment/categories/peak/total_mixins.py | 3 ++- 9 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py index 47b434e8..4ce20276 100644 --- a/src/easydiffraction/analysis/calculators/pdffit.py +++ b/src/easydiffraction/analysis/calculators/pdffit.py @@ -59,7 +59,11 @@ def name(self) -> str: """Short identifier of this calculator engine.""" return 'pdffit' - def calculate_structure_factors(self, structures: object, experiments: object,) -> list: + def calculate_structure_factors( + self, + structures: object, + experiments: object, + ) -> list: """ Return an empty list; PDF does not compute structure factors. diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index b7076204..fa6c0368 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -161,8 +161,8 @@ def _residual_function( """ Compute residuals between measured and calculated patterns. - It updates the parameter values according - to the optimizer-provided engine_params. + It updates the parameter values according to the + optimizer-provided engine_params. Parameters ---------- diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index e6c94aeb..c8d0aa9d 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -319,7 +319,10 @@ class TotalData(TotalDataBase): transformed to r-space. """ - type_info = TypeInfo(tag='total-pd', description='Total scattering (PDF) data',) + type_info = TypeInfo( + tag='total-pd', + description='Total scattering (PDF) data', + ) compatibility = Compatibility( sample_form=frozenset({SampleFormEnum.POWDER}), scattering_type=frozenset({ScatteringTypeEnum.TOTAL}), diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py index 7fa9bf08..e12fb0c0 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Excluded-regions factory — delegates entirely to ``FactoryBase``.""" +""" +Excluded-regions factory — delegates entirely to ``FactoryBase``. +""" from __future__ import annotations diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 7b2f799e..8955c5d6 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -52,7 +52,10 @@ def setup_wavelength(self, value: float) -> None: @InstrumentFactory.register class CwlScInstrument(CwlInstrumentBase): - type_info = TypeInfo(tag='cwl-sc', description='CW single-crystal diffractometer',) + type_info = TypeInfo( + tag='cwl-sc', + description='CW single-crystal diffractometer', + ) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}), @@ -68,7 +71,10 @@ def __init__(self) -> None: @InstrumentFactory.register class CwlPdInstrument(CwlInstrumentBase): - type_info = TypeInfo(tag='cwl-pd', description='CW powder diffractometer',) + type_info = TypeInfo( + tag='cwl-pd', + description='CW powder diffractometer', + ) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG, ScatteringTypeEnum.TOTAL}), beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}), diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index faa791c5..683eedb5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -18,7 +18,10 @@ @InstrumentFactory.register class TofScInstrument(InstrumentBase): - type_info = TypeInfo(tag='tof-sc', description='TOF single-crystal diffractometer',) + type_info = TypeInfo( + tag='tof-sc', + description='TOF single-crystal diffractometer', + ) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}), @@ -34,7 +37,10 @@ def __init__(self) -> None: @InstrumentFactory.register class TofPdInstrument(InstrumentBase): - type_info = TypeInfo(tag='tof-pd', description='TOF powder diffractometer',) + type_info = TypeInfo( + tag='tof-pd', + description='TOF powder diffractometer', + ) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}), diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py index dc3dbbd6..a2b4f63b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py @@ -24,7 +24,10 @@ class CwlPseudoVoigt( ): """Constant-wavelength pseudo-Voigt peak shape.""" - type_info = TypeInfo(tag='pseudo-voigt', description='Pseudo-Voigt profile',) + type_info = TypeInfo( + tag='pseudo-voigt', + description='Pseudo-Voigt profile', + ) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}), diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py index 39ddc556..59c0b9e3 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py @@ -23,7 +23,10 @@ class TofPseudoVoigt( ): """Time-of-flight pseudo-Voigt peak shape.""" - type_info = TypeInfo(tag='tof-pseudo-voigt', description='TOF pseudo-Voigt profile',) + type_info = TypeInfo( + tag='tof-pseudo-voigt', + description='TOF pseudo-Voigt profile', + ) compatibility = Compatibility( scattering_type=frozenset({ScatteringTypeEnum.BRAGG}), beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}), diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 7933e174..bae2c974 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Total scattering / PDF peak-profile component classes. +""" +Total scattering / PDF peak-profile component classes. This module provides classes that add broadening and asymmetry parameters. They are composed into concrete peak classes elsewhere via From d359f2aebdd0f329c92cbd8f7247f2c881ae6976 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 22:34:34 +0100 Subject: [PATCH 40/48] More fromatting --- .prettierignore | 17 ++++++++++++++++- pixi.lock | 4 ++-- pyproject.toml | 4 +++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.prettierignore b/.prettierignore index 788ec8fa..a08c3c48 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,6 @@ +# Git +.git/ + # Copier .copier-answers*.yml @@ -14,4 +17,16 @@ docs/docs/assets/ .pytest_cache/ # MyPy -.mypy_cache +.mypy_cache/ + +# Ruff +.ruff_cache/ + +# Node +node_modules + +# Misc +.benchmarks +.cache +deps/ +tmp/ diff --git a/pixi.lock b/pixi.lock index 8c406175..4a216280 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty40 - sha256: 13a174f3b1163dada6f0a52896bd23b16d7f04fd8029605672bb5c3fa3ef6643 + version: 0.10.2+devdirty42 + sha256: e4f098b9399fccee9a6dd2fb5acd25a820f8900e2c10092287bf95917ce90ab7 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index 72b2560a..8142ebaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -278,6 +278,8 @@ ignore = [ 'COM812', # https://docs.astral.sh/ruff/rules/missing-trailing-comma/ # The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint] 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc + # Disable, as [tool.format_docstring] split one-line docstrings into the canonical multi-line layout + 'D200', # https://docs.astral.sh/ruff/rules/unnecessary-multiline-docstring/ # Temporary: 'D100', 'D101', @@ -293,7 +295,7 @@ ignore = [ ] 'tests/**' = [ 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann - 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d + 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'S101', # https://docs.astral.sh/ruff/rules/assert/ From 6374c5b1fd68bf495aa802e56a777529e0178c59 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 22:44:21 +0100 Subject: [PATCH 41/48] Add missing D101 class docstrings across 9 files --- pixi.lock | 4 ++-- pyproject.toml | 4 ++-- src/easydiffraction/core/validation.py | 4 ++++ src/easydiffraction/core/variable.py | 10 ++++++++++ .../experiment/categories/background/chebyshev.py | 2 ++ .../experiment/categories/background/line_segment.py | 2 ++ .../datablocks/experiment/categories/data/bragg_pd.py | 6 ++++++ .../datablocks/experiment/categories/instrument/cwl.py | 6 ++++++ .../datablocks/experiment/categories/instrument/tof.py | 4 ++++ src/easydiffraction/display/plotting.py | 2 ++ src/easydiffraction/display/tables.py | 2 ++ 11 files changed, 42 insertions(+), 4 deletions(-) diff --git a/pixi.lock b/pixi.lock index 4a216280..b5cd2d52 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty42 - sha256: e4f098b9399fccee9a6dd2fb5acd25a820f8900e2c10092287bf95917ce90ab7 + version: 0.10.2+devdirty43 + sha256: 31638fca1c921f59959257a944d5570433fdc26698400588d3d4c0f524d38850 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index 8142ebaf..ae3834e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -282,7 +282,7 @@ ignore = [ 'D200', # https://docs.astral.sh/ruff/rules/unnecessary-multiline-docstring/ # Temporary: 'D100', - 'D101', + #'D101', 'D104', 'D105', 'DTZ005', @@ -386,4 +386,4 @@ exclude = 'src/easydiffraction/utils/_vendored/jupyter_dark_detect' # ED only docstring_style = 'numpy' line_length = 72 fix_rst_backticks = true -verbose = 'default' # or 'diff' to show changes made to docstrings +verbose = 'default' diff --git a/src/easydiffraction/core/validation.py b/src/easydiffraction/core/validation.py index 5103cbfe..26d59951 100644 --- a/src/easydiffraction/core/validation.py +++ b/src/easydiffraction/core/validation.py @@ -24,6 +24,8 @@ # TODO: MkDocs doesn't unpack types class DataTypeHints: + """Type hint aliases for numeric, string, and boolean types.""" + Numeric = int | float | np.integer | np.floating String = str Bool = bool @@ -33,6 +35,8 @@ class DataTypeHints: class DataTypes(Enum): + """Enumeration of supported data types for descriptors.""" + NUMERIC = (int, float, np.integer, np.floating) STRING = (str,) BOOL = (bool,) diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 19465a1e..b317da28 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -208,6 +208,8 @@ def from_cif(self, block: object, idx: int = 0) -> None: class GenericStringDescriptor(GenericDescriptorBase): + """Base descriptor that constrains values to strings.""" + _value_type = DataTypes.STRING def __init__( @@ -221,6 +223,8 @@ def __init__( class GenericNumericDescriptor(GenericDescriptorBase): + """Base descriptor that constrains values to numbers.""" + _value_type = DataTypes.NUMERIC def __init__( @@ -379,6 +383,8 @@ def fit_max(self, v: float) -> None: class StringDescriptor(GenericStringDescriptor): + """String descriptor bound to a CIF handler.""" + def __init__( self, *, @@ -404,6 +410,8 @@ def __init__( class NumericDescriptor(GenericNumericDescriptor): + """Numeric descriptor bound to a CIF handler.""" + def __init__( self, *, @@ -429,6 +437,8 @@ def __init__( class Parameter(GenericParameter): + """Fittable parameter bound to a CIF handler.""" + def __init__( self, *, diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index c2da0642..098c4268 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -132,6 +132,8 @@ def coef(self, value: float) -> None: @BackgroundFactory.register class ChebyshevPolynomialBackground(BackgroundBase): + """Chebyshev polynomial background model.""" + type_info = TypeInfo( tag='chebyshev', description='Chebyshev polynomial background', diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index 4573272f..2f0a5496 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -134,6 +134,8 @@ def y(self, value: float) -> None: @BackgroundFactory.register class LineSegmentBackground(BackgroundBase): + """Linear-interpolation background between user-defined points.""" + type_info = TypeInfo( tag='line-segment', description='Linear interpolation between points', diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 0e826d2a..883f7f80 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -296,6 +296,8 @@ def __init__(self) -> None: class PdDataBase(CategoryCollection): + """Base class for powder diffraction data collections.""" + # TODO: ??? # Redefine update priority to ensure data updated after other @@ -470,6 +472,8 @@ def intensity_bkg(self) -> np.ndarray: @DataFactory.register class PdCwlData(PdDataBase): + """Bragg powder CWL data collection.""" + # TODO: ??? # _description: str = 'Powder diffraction data points for # constant-wavelength experiments.' @@ -546,6 +550,8 @@ def unfiltered_x(self) -> np.ndarray: @DataFactory.register class PdTofData(PdDataBase): + """Bragg powder TOF data collection.""" + type_info = TypeInfo(tag='bragg-pd-tof', description='Bragg powder TOF data') compatibility = Compatibility( sample_form=frozenset({SampleFormEnum.POWDER}), diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 8955c5d6..3a3628bd 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -17,6 +17,8 @@ class CwlInstrumentBase(InstrumentBase): + """Base class for constant-wavelength instruments.""" + def __init__(self) -> None: super().__init__() @@ -52,6 +54,8 @@ def setup_wavelength(self, value: float) -> None: @InstrumentFactory.register class CwlScInstrument(CwlInstrumentBase): + """CW single-crystal diffractometer.""" + type_info = TypeInfo( tag='cwl-sc', description='CW single-crystal diffractometer', @@ -71,6 +75,8 @@ def __init__(self) -> None: @InstrumentFactory.register class CwlPdInstrument(CwlInstrumentBase): + """CW powder diffractometer.""" + type_info = TypeInfo( tag='cwl-pd', description='CW powder diffractometer', diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index 683eedb5..7e1db98e 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -18,6 +18,8 @@ @InstrumentFactory.register class TofScInstrument(InstrumentBase): + """TOF single-crystal diffractometer.""" + type_info = TypeInfo( tag='tof-sc', description='TOF single-crystal diffractometer', @@ -37,6 +39,8 @@ def __init__(self) -> None: @InstrumentFactory.register class TofPdInstrument(InstrumentBase): + """TOF powder diffractometer.""" + type_info = TypeInfo( tag='tof-pd', description='TOF powder diffractometer', diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 6cd8dd94..ec8e1d5c 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -29,6 +29,8 @@ class PlotterEngineEnum(str, Enum): + """Available plotting engine backends.""" + ASCII = 'asciichartpy' PLOTLY = 'plotly' diff --git a/src/easydiffraction/display/tables.py b/src/easydiffraction/display/tables.py index e192e131..1ae39594 100644 --- a/src/easydiffraction/display/tables.py +++ b/src/easydiffraction/display/tables.py @@ -18,6 +18,8 @@ class TableEngineEnum(str, Enum): + """Available table rendering backends.""" + RICH = 'rich' PANDAS = 'pandas' From 41423184682448a4b0bc7890ed2935c10a8b52ab Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 22:47:04 +0100 Subject: [PATCH 42/48] Add missing D105 magic method docstrings across 3 files --- pixi.lock | 4 ++-- pyproject.toml | 3 +-- src/easydiffraction/core/guard.py | 4 ++++ src/easydiffraction/core/validation.py | 2 ++ src/easydiffraction/core/variable.py | 3 +++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pixi.lock b/pixi.lock index b5cd2d52..a831a9cd 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty43 - sha256: 31638fca1c921f59959257a944d5570433fdc26698400588d3d4c0f524d38850 + version: 0.10.2+devdirty44 + sha256: 02a9404f20c52f83f03761673a0ab1be197549e02150bc5f4fc9ac23a9de9448 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index ae3834e4..05a4c0b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -282,9 +282,8 @@ ignore = [ 'D200', # https://docs.astral.sh/ruff/rules/unnecessary-multiline-docstring/ # Temporary: 'D100', - #'D101', 'D104', - 'D105', + #'D105', 'DTZ005', ] diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py index 75e45b5e..17b60fb5 100644 --- a/src/easydiffraction/core/guard.py +++ b/src/easydiffraction/core/guard.py @@ -21,12 +21,15 @@ def __init__(self) -> None: self._identity = Identity(owner=self) def __str__(self) -> str: + """Return the string representation of this object.""" return f'<{self.unique_name}>' def __repr__(self) -> str: + """Return the developer representation of this object.""" return self.__str__() def __getattr__(self, key: str) -> None: + """Raise a descriptive error for unknown attribute access.""" cls = type(self) allowed = cls._public_attrs() if key not in allowed: @@ -38,6 +41,7 @@ def __getattr__(self, key: str) -> None: ) def __setattr__(self, key: str, value: object) -> None: + """Set an attribute with access-control diagnostics.""" # Always allow private or special attributes without diagnostics if key.startswith('_'): object.__setattr__(self, key, value) diff --git a/src/easydiffraction/core/validation.py b/src/easydiffraction/core/validation.py index 26d59951..d3c411af 100644 --- a/src/easydiffraction/core/validation.py +++ b/src/easydiffraction/core/validation.py @@ -43,6 +43,7 @@ class DataTypes(Enum): ANY = (object,) # fallback for unconstrained def __str__(self) -> str: + """Return the lowercase name of the data type.""" return self.name.lower() @property @@ -65,6 +66,7 @@ class ValidationStage(Enum): REGEX = auto() def __str__(self) -> str: + """Return the lowercase name of the validation stage.""" return self.name.lower() diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index b317da28..8e06b2a9 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -98,6 +98,7 @@ def __init__( self._value = default() if callable(default) else default def __str__(self) -> str: + """Return the string representation of this descriptor.""" return f'<{self.unique_name} = {self.value!r}>' @property @@ -237,6 +238,7 @@ def __init__( self._units: str = units def __str__(self) -> str: + """Return the string representation including units.""" s: str = super().__str__() s = s[1:-1] # strip <> if self.units: @@ -289,6 +291,7 @@ def __init__( UidMapHandler.get().add_to_uid_map(self) def __str__(self) -> str: + """Return string representation with uncertainty and free.""" s = GenericDescriptorBase.__str__(self) s = s[1:-1] # strip <> if self.uncertainty is not None: From 56b13132faca5d1165e4be7e931334770d2057b6 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 22:50:19 +0100 Subject: [PATCH 43/48] Minor changes --- pixi.lock | 4 ++-- pyproject.toml | 7 +++---- .../datablocks/structure/categories/cell/default.py | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pixi.lock b/pixi.lock index a831a9cd..e7ad8660 100644 --- a/pixi.lock +++ b/pixi.lock @@ -4874,8 +4874,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydiffraction - version: 0.10.2+devdirty44 - sha256: 02a9404f20c52f83f03761673a0ab1be197549e02150bc5f4fc9ac23a9de9448 + version: 0.10.2+dev46 + sha256: 9ada6af993ed7303fd58593648683db9afb40a7c191b5f5f7384f37934362c42 requires_dist: - asciichartpy - asteval diff --git a/pyproject.toml b/pyproject.toml index 05a4c0b9..16d18621 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -281,10 +281,9 @@ ignore = [ # Disable, as [tool.format_docstring] split one-line docstrings into the canonical multi-line layout 'D200', # https://docs.astral.sh/ruff/rules/unnecessary-multiline-docstring/ # Temporary: - 'D100', - 'D104', - #'D105', - 'DTZ005', + 'D100', # https://docs.astral.sh/ruff/rules/undocumented-public-module/#undocumented-publi-module-d100 + 'D104', # https://docs.astral.sh/ruff/rules/undocumented-public-package/#undocumented-public-package-d104 + 'DTZ005', # https://docs.astral.sh/ruff/rules/call-datetime-now-without-tzinfo/#call-datetime-now-without-tzinfo-dtz005 ] # Ignore specific rules in certain files or directories diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 8d793b50..53cc98ee 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -64,7 +64,7 @@ def __init__(self) -> None: ) self._angle_alpha = Parameter( name='angle_alpha', - description='Angle between edges b and c.', + description='Angle between edges b and c', units='deg', value_spec=AttributeSpec( default=90.0, @@ -74,7 +74,7 @@ def __init__(self) -> None: ) self._angle_beta = Parameter( name='angle_beta', - description='Angle between edges a and c.', + description='Angle between edges a and c', units='deg', value_spec=AttributeSpec( default=90.0, @@ -84,7 +84,7 @@ def __init__(self) -> None: ) self._angle_gamma = Parameter( name='angle_gamma', - description='Angle between edges a and b.', + description='Angle between edges a and b', units='deg', value_spec=AttributeSpec( default=90.0, From 8a0fd20a355e3e88bb25335a6cc702dd113ca23f Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 26 Mar 2026 23:10:15 +0100 Subject: [PATCH 44/48] Fix broken nav entry, tutorial links, and anchor in docs --- docs/docs/user-guide/analysis-workflow/experiment.md | 2 +- docs/docs/user-guide/first-steps.md | 4 ++-- docs/mkdocs.yml | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/docs/user-guide/analysis-workflow/experiment.md b/docs/docs/user-guide/analysis-workflow/experiment.md index 120653af..c121d6a9 100644 --- a/docs/docs/user-guide/analysis-workflow/experiment.md +++ b/docs/docs/user-guide/analysis-workflow/experiment.md @@ -86,7 +86,7 @@ manually, you can use the `add_from_data_path` method of the need to specify the **name** of the experiment, which will be used to reference it later, as well as **data_path** to the measured data file (e.g., `.xye`, `.xy`). Supported formats are described in the -[Measured Data Category](#5-measured-data-category) section. +[Measured Data Category](#measured-data-category) section. Optionally, you can also specify the additional parameters that define the **type of experiment** you want to create. If you do not specify any diff --git a/docs/docs/user-guide/first-steps.md b/docs/docs/user-guide/first-steps.md index 2de8fb5c..b4366684 100644 --- a/docs/docs/user-guide/first-steps.md +++ b/docs/docs/user-guide/first-steps.md @@ -32,7 +32,7 @@ project = ed.Project() ``` A complete tutorial using the `import` syntax can be found -[here](../../tutorials/ed-3/). +[here](../tutorials/ed-3.ipynb). ### Importing specific parts @@ -57,7 +57,7 @@ project = Project() ``` A complete tutorial using the `from` syntax can be found -[here](../../tutorials/ed-4/). +[here](../tutorials/ed-4.ipynb). ## Utility functions diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index cc71ad38..55ca5f22 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -222,7 +222,6 @@ nav: - experiment: api-reference/datablocks/experiment.md - structure: api-reference/datablocks/structure.md - display: api-reference/display.md - - experiments: api-reference/experiment.md - io: api-reference/io.md - project: api-reference/project.md - summary: api-reference/summary.md From 090e16533ceeccd0289534fdf0cacf5729dc0761 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 27 Mar 2026 08:20:03 +0100 Subject: [PATCH 45/48] Bump dependencies --- pixi.lock | 149 ++++++++++++++++++++++++++---------------------------- 1 file changed, 72 insertions(+), 77 deletions(-) diff --git a/pixi.lock b/pixi.lock index e7ad8660..1b3db850 100644 --- a/pixi.lock +++ b/pixi.lock @@ -75,7 +75,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d0/c1/8f69e69303554fb3a5ada668683690d68623dcfb1eb52067cce85c91012a/chardet-7.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d1/3b/6103194ea934f1c3a4ea080905c8849f71e83de455c16cb625d25f49b779/chardet-7.4.0.post1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -244,7 +244,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -262,7 +262,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2e/75/5604f4d17ab607510d4702f156329194d8edfff7e29644ca9200b085e9a2/scipp-26.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -379,7 +379,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/54/2f/ca2f6d868e450eb37c8b91903f6741024caa2c884418e6a26c96df34751d/chardet-7.3.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e9/32/83a15c6077e7f240834ffd9ed78ef12f20f6e1924d7d7986d33f3d2af905/chardet-7.4.0.post1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -548,7 +548,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -566,7 +566,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/69/1dcb8e967f62759578938db5b29792b82ea8939a2d712e79491fa3e1cf0a/scipp-26.3.1-cp313-cp313-macosx_14_0_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -683,7 +683,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/ca/df314f70a6ed83394ac31eb835d024c04c9b46f6c2fe5d260fc7287058c3/chardet-7.3.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/83/d3/80554c1cc15631446c9b90aec6fe63b7310aa0b82d3004f7ba38bd8a8270/chardet-7.4.0.post1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -851,7 +851,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -869,7 +869,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/fe/b14d806894cf05178f1e77d0d619f071db50cf698bc654c54f9241223bcf/scipp-26.3.1-cp313-cp313-macosx_14_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -978,7 +978,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a2/6b/2ac79f482dc59ea6ca2205830396ee23efd767db9a757394b7cf486ff00a/chardet-7.3.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/91/d7/47988d40231b41376f5a66346ef3b322c81091dfd4c0f84df5a1e3bb06b5/chardet-7.4.0.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -1145,7 +1145,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -1165,7 +1165,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/37/fd/22621d3ee9e3ee87ef4c89b63bba55b265ab85039b3c1ba88ed2380a24c1/scipp-26.3.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -1292,7 +1292,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/96/91/9cb4259a08689fd0d7f0552e8f78bead89a1778429a478e9857cc0ccfb43/chardet-7.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e3/a2/dab58511fbeef06dd88866568ea1a11b2f15654223cafc2681e2da84b1f2/chardet-7.4.0.post1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -1463,7 +1463,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -1481,7 +1481,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d4/06/19ff1efd58b85906149ce83dfddce23252cea5bec7e0fa5f834336cfe836/scipp-26.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -1597,7 +1597,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/ef/97/eaf0d2b7c62d4de86e0c95adb30d9d56febe1bf78e57d62f198ece85b236/chardet-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3e/38/fe380893cbba72febb24d5dc0c2f9ac99f437153c36a409a8e254ed77bb6/chardet-7.4.0.post1-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -1768,7 +1768,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -1786,7 +1786,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/21/4962b1daddf0422e56c5ed4c41bea1ccb6d2a9ab72b795196835a20969c7/scipp-26.3.1-cp311-cp311-macosx_14_0_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -1902,7 +1902,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/6b/02/4b814ab51e5b3981ea1ded8bd97dee878e64458f7e954597692dc37c00ca/chardet-7.3.0-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/5e/24/3c1522d777b66e2e3615ee33d1d4291c47b0ec258a9471b559339b01fac5/chardet-7.4.0.post1-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -2072,7 +2072,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -2090,7 +2090,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/60/54/5011adb56853caabfd90686c2e543d1e3c76a8ef2755809b7e12e3f3583b/scipp-26.3.1-cp311-cp311-macosx_14_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -2198,7 +2198,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c8/a4/067808ec52282aa4c4a5e2a87b5afb3ed5270b7ebb81c969c75169bc8947/chardet-7.3.0-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/91/d7/47988d40231b41376f5a66346ef3b322c81091dfd4c0f84df5a1e3bb06b5/chardet-7.4.0.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -2367,7 +2367,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -2387,7 +2387,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/0d/8882a4c7a5ebe59a46b709e82411d9c730d67250d41a2e11bc4bcd4d431d/scipp-26.3.1-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -2515,7 +2515,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d0/c1/8f69e69303554fb3a5ada668683690d68623dcfb1eb52067cce85c91012a/chardet-7.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d1/3b/6103194ea934f1c3a4ea080905c8849f71e83de455c16cb625d25f49b779/chardet-7.4.0.post1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -2684,7 +2684,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -2702,7 +2702,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2e/75/5604f4d17ab607510d4702f156329194d8edfff7e29644ca9200b085e9a2/scipp-26.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -2819,7 +2819,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/54/2f/ca2f6d868e450eb37c8b91903f6741024caa2c884418e6a26c96df34751d/chardet-7.3.0-cp313-cp313-macosx_10_13_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e9/32/83a15c6077e7f240834ffd9ed78ef12f20f6e1924d7d7986d33f3d2af905/chardet-7.4.0.post1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -2988,7 +2988,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -3006,7 +3006,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e2/69/1dcb8e967f62759578938db5b29792b82ea8939a2d712e79491fa3e1cf0a/scipp-26.3.1-cp313-cp313-macosx_14_0_x86_64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -3123,7 +3123,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/dc/ca/df314f70a6ed83394ac31eb835d024c04c9b46f6c2fe5d260fc7287058c3/chardet-7.3.0-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/83/d3/80554c1cc15631446c9b90aec6fe63b7310aa0b82d3004f7ba38bd8a8270/chardet-7.4.0.post1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -3291,7 +3291,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -3309,7 +3309,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl - - pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/fe/b14d806894cf05178f1e77d0d619f071db50cf698bc654c54f9241223bcf/scipp-26.3.1-cp313-cp313-macosx_14_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -3418,7 +3418,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/a2/6b/2ac79f482dc59ea6ca2205830396ee23efd767db9a757394b7cf486ff00a/chardet-7.3.0-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/91/d7/47988d40231b41376f5a66346ef3b322c81091dfd4c0f84df5a1e3bb06b5/chardet-7.4.0.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl @@ -3585,7 +3585,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl @@ -3605,7 +3605,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/37/fd/22621d3ee9e3ee87ef4c89b63bba55b265ab85039b3c1ba88ed2380a24c1/scipp-26.3.1-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl @@ -4271,45 +4271,40 @@ packages: version: 3.5.0 sha256: a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/54/2f/ca2f6d868e450eb37c8b91903f6741024caa2c884418e6a26c96df34751d/chardet-7.3.0-cp313-cp313-macosx_10_13_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/3e/38/fe380893cbba72febb24d5dc0c2f9ac99f437153c36a409a8e254ed77bb6/chardet-7.4.0.post1-cp311-cp311-macosx_10_9_x86_64.whl name: chardet - version: 7.3.0 - sha256: 51a0ad2031bd10d24357d7e52de68abe29efb62b7aedf1bd9d1c593fd438cec5 + version: 7.4.0.post1 + sha256: 2769be12361a6c7873392e435c708eca88c9f0fb6a647af75fa1386db64032d6 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/6b/02/4b814ab51e5b3981ea1ded8bd97dee878e64458f7e954597692dc37c00ca/chardet-7.3.0-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/5e/24/3c1522d777b66e2e3615ee33d1d4291c47b0ec258a9471b559339b01fac5/chardet-7.4.0.post1-cp311-cp311-macosx_11_0_arm64.whl name: chardet - version: 7.3.0 - sha256: 811029a7cf397ed32441aa134475f7047826fba496047a6afd7bba1da7fc4ab7 + version: 7.4.0.post1 + sha256: 8e1eaa942ae81d43d535092ff3ba660c967344178cc3876b54834a56c1207f3a requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/96/91/9cb4259a08689fd0d7f0552e8f78bead89a1778429a478e9857cc0ccfb43/chardet-7.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/83/d3/80554c1cc15631446c9b90aec6fe63b7310aa0b82d3004f7ba38bd8a8270/chardet-7.4.0.post1-cp313-cp313-macosx_11_0_arm64.whl name: chardet - version: 7.3.0 - sha256: 90a6249a455b8ecdb207a9f5a5eb46e1c21f2173eacb5e1603def61ed4784303 + version: 7.4.0.post1 + sha256: e6285d35f79d0cdc8838d3cb01876f979c8419a74662e8de39444e40639e0b2b requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/a2/6b/2ac79f482dc59ea6ca2205830396ee23efd767db9a757394b7cf486ff00a/chardet-7.3.0-cp313-cp313-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/91/d7/47988d40231b41376f5a66346ef3b322c81091dfd4c0f84df5a1e3bb06b5/chardet-7.4.0.post1-py3-none-any.whl name: chardet - version: 7.3.0 - sha256: 340f4eccafa17cfc5a90af1fb8558dc791156f815f8d800d8ac2b1b9fb2b8103 + version: 7.4.0.post1 + sha256: 57a62ef50f69bc2fb3a3ea1ffffec6d10f3d2112d3b05d6e3cb15c2c9b55f6cc requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/c8/a4/067808ec52282aa4c4a5e2a87b5afb3ed5270b7ebb81c969c75169bc8947/chardet-7.3.0-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/d1/3b/6103194ea934f1c3a4ea080905c8849f71e83de455c16cb625d25f49b779/chardet-7.4.0.post1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: chardet - version: 7.3.0 - sha256: ff4d81f5561f2382343dc922e533c017d52bc55125e50551c18034fffcdbd0cb + version: 7.4.0.post1 + sha256: 329aa8766c4917d3acc1b1d0462f0b2e820e24e9f341d0f858aee85396ae3002 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/d0/c1/8f69e69303554fb3a5ada668683690d68623dcfb1eb52067cce85c91012a/chardet-7.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/e3/a2/dab58511fbeef06dd88866568ea1a11b2f15654223cafc2681e2da84b1f2/chardet-7.4.0.post1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: chardet - version: 7.3.0 - sha256: 622206fdb0d274766559fa172e2d680d58cc5fc6a67015c21c36fec500e7bd50 + version: 7.4.0.post1 + sha256: ad98a6c2e61624b1120919353d222121b8f5848b9d33c885d949fe0235575682 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/dc/ca/df314f70a6ed83394ac31eb835d024c04c9b46f6c2fe5d260fc7287058c3/chardet-7.3.0-cp313-cp313-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/e9/32/83a15c6077e7f240834ffd9ed78ef12f20f6e1924d7d7986d33f3d2af905/chardet-7.4.0.post1-cp313-cp313-macosx_10_13_x86_64.whl name: chardet - version: 7.3.0 - sha256: 23d264962f680deec691308cc753685b4c5a69798a26e00ce4f4cf96d2e43fcb - requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/ef/97/eaf0d2b7c62d4de86e0c95adb30d9d56febe1bf78e57d62f198ece85b236/chardet-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl - name: chardet - version: 7.3.0 - sha256: 5b58eb6d6dea67fc3b578dd956dd6e92aafea383aa559ccea55265554ff062a3 + version: 7.4.0.post1 + sha256: efdb3785c8700b3d0b354553827a166480a439f9754f7366f795bbe8b42d6daf requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl name: charset-normalizer @@ -10658,10 +10653,10 @@ packages: requires_dist: - six>=1.5 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' -- pypi: https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl +- pypi: https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl name: python-discovery - version: 1.2.0 - sha256: 1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a + version: 1.2.1 + sha256: b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502 requires_dist: - filelock>=3.15.4 - platformdirs>=4.3.6,<5 @@ -11023,25 +11018,25 @@ packages: version: 0.30.0 sha256: e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/2b/e9/346d4d3fffc6871125e877dae8d9a1966b254fbd92a50f8561078b88b099/ruff-0.15.7-py3-none-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl name: ruff - version: 0.15.7 - sha256: 4d53d712ddebcd7dace1bc395367aec12c057aacfe9adbb6d832302575f4d3a1 + version: 0.15.8 + sha256: 04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl name: ruff - version: 0.15.7 - sha256: 722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477 + version: 0.15.8 + sha256: 6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl name: ruff - version: 0.15.7 - sha256: 7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e + version: 0.15.8 + sha256: d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570 requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: ruff - version: 0.15.7 - sha256: dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5 + version: 0.15.8 + sha256: 0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1 requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl name: sciline From 362ffbbb7b1305a7195df7c521a90a640b59adf6 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 27 Mar 2026 08:32:28 +0100 Subject: [PATCH 46/48] Refactor tutorial display to remove description and update title in index --- docs/docs/tutorials/index.json | 2 +- src/easydiffraction/utils/utils.py | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/docs/tutorials/index.json b/docs/docs/tutorials/index.json index 138b0e51..d40d1896 100644 --- a/docs/docs/tutorials/index.json +++ b/docs/docs/tutorials/index.json @@ -86,7 +86,7 @@ "13": { "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-13/ed-13.ipynb", "original_name": "dmsc-summer-school-2025_analysis-powder-diffraction", - "title": "DMSC Summer School 2025: Powder Diffraction Analysis", + "title": "DMSC Summer School: Powder Diffraction Analysis", "description": "Comprehensive workshop tutorial covering Rietveld refinement of Si and La0.5Ba0.5CoO3 using simulated powder diffraction data", "level": "workshop" }, diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 486b9c7a..e548188b 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -346,8 +346,8 @@ def list_tutorials() -> None: """ Display a table of available tutorial notebooks. - Shows tutorial ID, filename, title, and description for all - tutorials available for the current version of easydiffraction. + Shows tutorial ID, filename and title for all tutorials available + for the current version of easydiffraction. """ index = _fetch_tutorials_index() if not index: @@ -355,19 +355,17 @@ def list_tutorials() -> None: return version = _get_version_for_url() - console.print(f'Tutorials available for easydiffraction v{version}:') - console.print('') + console.paragraph(f'Tutorials available for easydiffraction v{version}:') - columns_headers = ['id', 'file', 'title', 'description'] - columns_alignment = ['right', 'left', 'left', 'left'] + columns_headers = ['id', 'file', 'title'] + columns_alignment = ['right', 'left', 'left'] columns_data = [] - for tutorial_id in sorted(index.keys(), key=lambda x: int(x) if x.isdigit() else x): + for tutorial_id in index.keys(): record = index[tutorial_id] filename = f'ed-{tutorial_id}.ipynb' title = record.get('title', '') - description = record.get('description', '') - columns_data.append([tutorial_id, filename, title, description]) + columns_data.append([tutorial_id, filename, title]) render_table( columns_headers=columns_headers, From 93b05988a89ed5d948e8fb71ea7c8bd75f53477d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 27 Mar 2026 09:13:51 +0100 Subject: [PATCH 47/48] Add platform-specific dependencies for GNU Scientific Library and libc++ on macOS --- .github/workflows/test.yml | 15 +- pixi.lock | 293 ----------------------------- pixi.toml | 7 +- src/easydiffraction/utils/utils.py | 2 +- 4 files changed, 20 insertions(+), 297 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f287ebf2..801dd9b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -205,8 +205,19 @@ jobs: echo "Adding Python $py_ver" pixi add "python=$py_ver" - echo "Adding GNU Scientific Library (GSL)" - pixi add gsl + # diffpy.pdffit2 wheel needs libgsl on macOS. Added as + # platform-specific deps so this is a no-op on Linux/Windows. + echo "Adding GNU Scientific Library (required by diffpy.pdffit2)" + pixi add --platform osx-arm64 gsl + pixi add --platform osx-64 gsl + + # diffpy.pdffit2 wheel links @rpath/libc++.1.dylib, which must be + # present in the conda env lib/ dir on macOS (Python propagates its + # own @loader_path/../lib/ rpath to loaded extensions). Added as + # platform-specific deps so this is a no-op on Linux/Windows. + echo "Adding libc++ for macOS (required by diffpy.pdffit2)" + pixi add --platform osx-arm64 libcxx + pixi add --platform osx-64 libcxx echo "Looking for wheel in ../dist/py$py_ver/" ls -l "../dist/py$py_ver/" diff --git a/pixi.lock b/pixi.lock index 1b3db850..929b2f14 100644 --- a/pixi.lock +++ b/pixi.lock @@ -13,27 +13,21 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda @@ -924,27 +918,16 @@ environments: win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.2-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.52.0-hf5d6505_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -1230,27 +1213,21 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda @@ -2146,25 +2123,14 @@ environments: win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.2-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.52.0-hf5d6505_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -2453,27 +2419,21 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda @@ -3364,27 +3324,16 @@ environments: win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.2-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.52.0-hf5d6505_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -5649,19 +5598,6 @@ packages: - platformdirs>=4.2 ; extra == 'pypi' - wheel>=0.42 ; extra == 'pypi' requires_python: '>=3.10' -- conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - sha256: f923af07c3a3db746d3be8efebdaa9c819a6007ee3cc12445cee059641611e05 - md5: 04e128d2adafe3c844cde58f103c481b - depends: - - __glibc >=2.17,<3.0.a0 - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - libgcc >=13 - license: GPL-3.0-or-later - license_family: GPL - purls: [] - size: 2486744 - timestamp: 1737621160295 - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda sha256: 1d729f940f28dd5476b847123883abce119dff7af1abc236452d54ad4682b702 md5: 382c8abc7d56f9236090a76fc6e51a97 @@ -5686,20 +5622,6 @@ packages: purls: [] size: 1862134 timestamp: 1737621413640 -- conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda - sha256: 87a3468e09cc1ee0268e8639debad6a5b440090ef8cb1d2ee5eed66c86085528 - md5: a47cf810b7c03955139a150b228b93ca - depends: - - libblas >=3.9.0,<4.0a0 - - libcblas >=3.9.0,<4.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - license: GPL-3.0-or-later - license_family: GPL - purls: [] - size: 1528970 - timestamp: 1737622367981 - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl name: h11 version: 0.16.0 @@ -6645,23 +6567,6 @@ packages: purls: [] size: 1229639 timestamp: 1770863511331 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - build_number: 6 - sha256: 7bfe936dbb5db04820cf300a9cc1f5ee8d5302fc896c2d66e30f1ee2f20fbfd6 - md5: 6d6d225559bfa6e2f3c90ee9c03d4e2e - depends: - - libopenblas >=0.3.32,<0.3.33.0a0 - - libopenblas >=0.3.32,<1.0a0 - constrains: - - blas 2.306 openblas - - liblapack 3.11.0 6*_openblas - - liblapacke 3.11.0 6*_openblas - - libcblas 3.11.0 6*_openblas - - mkl <2026 - license: BSD-3-Clause - purls: [] - size: 18621 - timestamp: 1774503034895 - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-6_he492b99_openblas.conda build_number: 6 sha256: 6865098475f3804208038d0c424edf926f4dc9eacaa568d14e29f59df53731fd @@ -6696,21 +6601,6 @@ packages: purls: [] size: 18859 timestamp: 1774504387211 -- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda - build_number: 6 - sha256: 10c8054f007adca8c780cd8bb9335fa5d990f0494b825158d3157983a25b1ea2 - md5: 95543eec964b4a4a7ca3c4c9be481aa1 - depends: - - mkl >=2025.3.1,<2026.0a0 - constrains: - - blas 2.306 mkl - - liblapacke 3.11.0 6*_mkl - - liblapack 3.11.0 6*_mkl - - libcblas 3.11.0 6*_mkl - license: BSD-3-Clause - purls: [] - size: 68082 - timestamp: 1774503684284 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e md5: 72c8fd1af66bd67bf580645b426513ed @@ -6810,20 +6700,6 @@ packages: purls: [] size: 290754 timestamp: 1764018009077 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - build_number: 6 - sha256: 57edafa7796f6fa3ebbd5367692dd4c7f552be42109c2dd1a7c89b55089bf374 - md5: 36ae340a916635b97ac8a0655ace2a35 - depends: - - libblas 3.11.0 6_h4a7cf45_openblas - constrains: - - blas 2.306 openblas - - liblapack 3.11.0 6*_openblas - - liblapacke 3.11.0 6*_openblas - license: BSD-3-Clause - purls: [] - size: 18622 - timestamp: 1774503050205 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-6_h9b27e0a_openblas.conda build_number: 6 sha256: 8422e1ce083e015bdb44addd25c9a8fe99aa9b0edbd9b7f1239b7ac1e3d04f77 @@ -6852,20 +6728,6 @@ packages: purls: [] size: 18863 timestamp: 1774504433388 -- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - build_number: 6 - sha256: 02b2a2225f4899c6aaa1dc723e06b3f7a4903d2129988f91fc1527409b07b0a5 - md5: 9e4bf521c07f4d423cba9296b7927e3c - depends: - - libblas 3.11.0 6_hf2e6a31_mkl - constrains: - - blas 2.306 mkl - - liblapacke 3.11.0 6*_mkl - - liblapack 3.11.0 6*_mkl - license: BSD-3-Clause - purls: [] - size: 68221 - timestamp: 1774503722413 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda sha256: 46561199545890e050a8a90edcfce984e5f881da86b09388926e3a6c6b759dec md5: ed6f7b7a35f942a0301e581d72616f7d @@ -7056,18 +6918,6 @@ packages: purls: [] size: 27526 timestamp: 1771378224552 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda - sha256: d2c9fad338fd85e4487424865da8e74006ab2e2475bd788f624d7a39b2a72aee - md5: 9063115da5bc35fdc3e1002e69b9ef6e - depends: - - libgfortran5 15.2.0 h68bc16d_18 - constrains: - - libgfortran-ng ==15.2.0=*_18 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - size: 27523 - timestamp: 1771378269450 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_18.conda sha256: fb06c2a2ef06716a0f2a6550f5d13cdd1d89365993068512b7ae3c34e6e665d9 md5: 34a9f67498721abcfef00178bcf4b190 @@ -7092,19 +6942,6 @@ packages: purls: [] size: 138973 timestamp: 1771379054939 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - sha256: 539b57cf50ec85509a94ba9949b7e30717839e4d694bc94f30d41c9d34de2d12 - md5: 646855f357199a12f02a87382d429b75 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=15.2.0 - constrains: - - libgfortran 15.2.0 - license: GPL-3.0-only WITH GCC-exception-3.1 - license_family: GPL - purls: [] - size: 2482475 - timestamp: 1771378241063 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_18.conda sha256: ddaf9dcf008c031b10987991aa78643e03c24a534ad420925cbd5851b31faa11 md5: ca52daf58cea766656266c8771d8be81 @@ -7139,32 +6976,6 @@ packages: purls: [] size: 603262 timestamp: 1771378117851 -- conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda - sha256: 8cdf11333a81085468d9aa536ebb155abd74adc293576f6013fc0c85a7a90da3 - md5: 3b576f6860f838f950c570f4433b086e - depends: - - libwinpthread >=12.0.0.r4.gg4f2fc60ca - - libxml2 - - libxml2-16 >=2.14.6 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: BSD-3-Clause - license_family: BSD - purls: [] - size: 2411241 - timestamp: 1765104337762 -- conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7 - md5: 64571d1dd6cdcfa25d0664a5950fdaa2 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: LGPL-2.1-only - purls: [] - size: 696926 - timestamp: 1754909290005 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda sha256: 755c55ebab181d678c12e49cced893598f2bab22d582fbbf4d8b83c18be207eb md5: c7c83eecbb72d88b940c249af56c8b17 @@ -7315,20 +7126,6 @@ packages: purls: [] size: 33731 timestamp: 1750274110928 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - sha256: 6dc30b28f32737a1c52dada10c8f3a41bc9e021854215efca04a7f00487d09d9 - md5: 89d61bc91d3f39fda0ca10fcd3c68594 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - - libgfortran - - libgfortran5 >=14.3.0 - constrains: - - openblas >=0.3.32,<0.3.33.0a0 - license: BSD-3-Clause - purls: [] - size: 5928890 - timestamp: 1774471724897 - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.32-openmp_h9e49c7b_0.conda sha256: 6764229359cd927c9efc036930ba28f83436b8d6759c5ac4ea9242fc29a7184e md5: 4058c5f8dbef6d28cb069f96b95ae6df @@ -7456,18 +7253,6 @@ packages: purls: [] size: 421195 timestamp: 1753948426421 -- conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - sha256: 0fccf2d17026255b6e10ace1f191d0a2a18f2d65088fd02430be17c701f8ffe0 - md5: 8a86073cf3b343b87d03f41790d8b4e5 - depends: - - ucrt - constrains: - - pthreads-win32 <0.0a0 - - msys2-conda-epoch <0.0a0 - license: MIT AND BSD-3-Clause-Clear - purls: [] - size: 36621 - timestamp: 1759768399557 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c md5: 5aa797f8787fe7a17d1b0821485b5adc @@ -7477,42 +7262,6 @@ packages: purls: [] size: 100393 timestamp: 1702724383534 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - sha256: f905eb7046987c336122121759e7f09144729f6898f48cd06df2a945b86998d8 - md5: 1007e1bfe181a2aee214779ee7f13d30 - depends: - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.2,<6.0a0 - - libxml2-16 2.15.2 h692994f_0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - icu <0.0a0 - license: MIT - license_family: MIT - purls: [] - size: 43681 - timestamp: 1772704748950 -- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda - sha256: b8c71b3b609c7cfe17f3f2a47c75394d7b30acfb8b34ad7a049ea8757b4d33df - md5: e365238134188e42ed36ee996159d482 - depends: - - libiconv >=1.18,<2.0a0 - - liblzma >=5.8.2,<6.0a0 - - libzlib >=1.3.1,<2.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - libxml2 2.15.2 - - icu <0.0a0 - license: MIT - license_family: MIT - purls: [] - size: 520078 - timestamp: 1772704728534 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda sha256: 55044c403570f0dc26e6364de4dc5368e5f3fc7ff103e867c487e2b5ab2bcda9 md5: d87ff7921124eccd67248aa483c23fec @@ -7589,21 +7338,6 @@ packages: purls: [] size: 285912 timestamp: 1774349644882 -- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda - sha256: 64c7fe6490583f3c49c36c2f413e681072102db8abea13a3e1832f44eaf55518 - md5: d9f479404fe316e575f4a4575f3df406 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - constrains: - - openmp 22.1.1|22.1.1.* - - intel-openmp <0.0a0 - license: Apache-2.0 WITH LLVM-exception - license_family: APACHE - purls: [] - size: 347138 - timestamp: 1774349485844 - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl name: lmfit version: 1.3.4 @@ -8093,20 +7827,6 @@ packages: - griffelib>=2.0 - typing-extensions>=4.0 ; python_full_version < '3.11' requires_python: '>=3.10' -- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - sha256: f2c2b2a3c2e7d08d78c10bef7c135a4262c80d1d48c85fb5902ca30d61d645f4 - md5: 3fd3009cef89c36e9898a6feeb0f5530 - depends: - - llvm-openmp >=22.1.1 - - tbb >=2022.3.0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: LicenseRef-IntelSimplifiedSoftwareOct2022 - license_family: Proprietary - purls: [] - size: 99997309 - timestamp: 1774449747739 - pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl name: mpld3 version: 0.5.12 @@ -12276,19 +11996,6 @@ packages: requires_dist: - wcwidth ; extra == 'widechars' requires_python: '>=3.10' -- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda - sha256: abd9a489f059fba85c8ffa1abdaa4d515d6de6a3325238b8e81203b913cf65a9 - md5: 0f9817ffbe25f9e69ceba5ea70c52606 - depends: - - libhwloc >=2.12.2,<2.12.3.0a0 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: Apache-2.0 - license_family: APACHE - purls: [] - size: 155869 - timestamp: 1767886839029 - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl name: terminado version: 0.18.1 diff --git a/pixi.toml b/pixi.toml index 629589f7..95994105 100644 --- a/pixi.toml +++ b/pixi.toml @@ -52,9 +52,14 @@ macos = '14.0' # Default feature configuration +[target.osx-arm64.dependencies] +gsl = '*' # GNU Scientific Library; required for pdffit2 + +[target.osx.dependencies] +gsl = '*' # GNU Scientific Library; required for pdffit2 + [dependencies] nodejs = '*' # Required for Prettier (non-Python formatting) -gsl = '*' # GNU Scientific Library; required for pdffit2 [pypi-dependencies] # == [feature.default.pypi-dependencies] #pip = '*' # Native package installer diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index e548188b..3e8c5f1e 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -361,7 +361,7 @@ def list_tutorials() -> None: columns_alignment = ['right', 'left', 'left'] columns_data = [] - for tutorial_id in index.keys(): + for tutorial_id in index: record = index[tutorial_id] filename = f'ed-{tutorial_id}.ipynb' title = record.get('title', '') From 943b71b9ce8eab780587c252e635d054cf1b1384 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 27 Mar 2026 09:16:44 +0100 Subject: [PATCH 48/48] Consolidate GNU Scientific Library dependency for macOS in pixi.toml and test.yml --- .github/workflows/test.yml | 5 +- pixi.lock | 293 +++++++++++++++++++++++++++++++++++++ pixi.toml | 7 +- 3 files changed, 295 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 801dd9b0..745c0b03 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -205,11 +205,8 @@ jobs: echo "Adding Python $py_ver" pixi add "python=$py_ver" - # diffpy.pdffit2 wheel needs libgsl on macOS. Added as - # platform-specific deps so this is a no-op on Linux/Windows. echo "Adding GNU Scientific Library (required by diffpy.pdffit2)" - pixi add --platform osx-arm64 gsl - pixi add --platform osx-64 gsl + pixi add gsl # diffpy.pdffit2 wheel links @rpath/libc++.1.dylib, which must be # present in the conda env lib/ dir on macOS (Python propagates its diff --git a/pixi.lock b/pixi.lock index 929b2f14..1b3db850 100644 --- a/pixi.lock +++ b/pixi.lock @@ -13,21 +13,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda @@ -918,16 +924,27 @@ environments: win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.2-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.52.0-hf5d6505_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -1213,21 +1230,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda @@ -2123,14 +2146,25 @@ environments: win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.2-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.52.0-hf5d6505_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -2419,21 +2453,27 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda @@ -3324,16 +3364,27 @@ environments: win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.2-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.52.0-hf5d6505_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.8.2-h80d1838_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda @@ -5598,6 +5649,19 @@ packages: - platformdirs>=4.2 ; extra == 'pypi' - wheel>=0.42 ; extra == 'pypi' requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/gsl-2.8-hbf7d49c_1.conda + sha256: f923af07c3a3db746d3be8efebdaa9c819a6007ee3cc12445cee059641611e05 + md5: 04e128d2adafe3c844cde58f103c481b + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=13 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 2486744 + timestamp: 1737621160295 - conda: https://conda.anaconda.org/conda-forge/osx-64/gsl-2.8-hc707ee6_1.conda sha256: 1d729f940f28dd5476b847123883abce119dff7af1abc236452d54ad4682b702 md5: 382c8abc7d56f9236090a76fc6e51a97 @@ -5622,6 +5686,20 @@ packages: purls: [] size: 1862134 timestamp: 1737621413640 +- conda: https://conda.anaconda.org/conda-forge/win-64/gsl-2.8-h5b8d9c4_1.conda + sha256: 87a3468e09cc1ee0268e8639debad6a5b440090ef8cb1d2ee5eed66c86085528 + md5: a47cf810b7c03955139a150b228b93ca + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: GPL-3.0-or-later + license_family: GPL + purls: [] + size: 1528970 + timestamp: 1737622367981 - pypi: https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl name: h11 version: 0.16.0 @@ -6567,6 +6645,23 @@ packages: purls: [] size: 1229639 timestamp: 1770863511331 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda + build_number: 6 + sha256: 7bfe936dbb5db04820cf300a9cc1f5ee8d5302fc896c2d66e30f1ee2f20fbfd6 + md5: 6d6d225559bfa6e2f3c90ee9c03d4e2e + depends: + - libopenblas >=0.3.32,<0.3.33.0a0 + - libopenblas >=0.3.32,<1.0a0 + constrains: + - blas 2.306 openblas + - liblapack 3.11.0 6*_openblas + - liblapacke 3.11.0 6*_openblas + - libcblas 3.11.0 6*_openblas + - mkl <2026 + license: BSD-3-Clause + purls: [] + size: 18621 + timestamp: 1774503034895 - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-6_he492b99_openblas.conda build_number: 6 sha256: 6865098475f3804208038d0c424edf926f4dc9eacaa568d14e29f59df53731fd @@ -6601,6 +6696,21 @@ packages: purls: [] size: 18859 timestamp: 1774504387211 +- conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-6_hf2e6a31_mkl.conda + build_number: 6 + sha256: 10c8054f007adca8c780cd8bb9335fa5d990f0494b825158d3157983a25b1ea2 + md5: 95543eec964b4a4a7ca3c4c9be481aa1 + depends: + - mkl >=2025.3.1,<2026.0a0 + constrains: + - blas 2.306 mkl + - liblapacke 3.11.0 6*_mkl + - liblapack 3.11.0 6*_mkl + - libcblas 3.11.0 6*_mkl + license: BSD-3-Clause + purls: [] + size: 68082 + timestamp: 1774503684284 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e md5: 72c8fd1af66bd67bf580645b426513ed @@ -6700,6 +6810,20 @@ packages: purls: [] size: 290754 timestamp: 1764018009077 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda + build_number: 6 + sha256: 57edafa7796f6fa3ebbd5367692dd4c7f552be42109c2dd1a7c89b55089bf374 + md5: 36ae340a916635b97ac8a0655ace2a35 + depends: + - libblas 3.11.0 6_h4a7cf45_openblas + constrains: + - blas 2.306 openblas + - liblapack 3.11.0 6*_openblas + - liblapacke 3.11.0 6*_openblas + license: BSD-3-Clause + purls: [] + size: 18622 + timestamp: 1774503050205 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-6_h9b27e0a_openblas.conda build_number: 6 sha256: 8422e1ce083e015bdb44addd25c9a8fe99aa9b0edbd9b7f1239b7ac1e3d04f77 @@ -6728,6 +6852,20 @@ packages: purls: [] size: 18863 timestamp: 1774504433388 +- conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-6_h2a3cdd5_mkl.conda + build_number: 6 + sha256: 02b2a2225f4899c6aaa1dc723e06b3f7a4903d2129988f91fc1527409b07b0a5 + md5: 9e4bf521c07f4d423cba9296b7927e3c + depends: + - libblas 3.11.0 6_hf2e6a31_mkl + constrains: + - blas 2.306 mkl + - liblapacke 3.11.0 6*_mkl + - liblapack 3.11.0 6*_mkl + license: BSD-3-Clause + purls: [] + size: 68221 + timestamp: 1774503722413 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda sha256: 46561199545890e050a8a90edcfce984e5f881da86b09388926e3a6c6b759dec md5: ed6f7b7a35f942a0301e581d72616f7d @@ -6918,6 +7056,18 @@ packages: purls: [] size: 27526 timestamp: 1771378224552 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda + sha256: d2c9fad338fd85e4487424865da8e74006ab2e2475bd788f624d7a39b2a72aee + md5: 9063115da5bc35fdc3e1002e69b9ef6e + depends: + - libgfortran5 15.2.0 h68bc16d_18 + constrains: + - libgfortran-ng ==15.2.0=*_18 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27523 + timestamp: 1771378269450 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_18.conda sha256: fb06c2a2ef06716a0f2a6550f5d13cdd1d89365993068512b7ae3c34e6e665d9 md5: 34a9f67498721abcfef00178bcf4b190 @@ -6942,6 +7092,19 @@ packages: purls: [] size: 138973 timestamp: 1771379054939 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda + sha256: 539b57cf50ec85509a94ba9949b7e30717839e4d694bc94f30d41c9d34de2d12 + md5: 646855f357199a12f02a87382d429b75 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 2482475 + timestamp: 1771378241063 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_18.conda sha256: ddaf9dcf008c031b10987991aa78643e03c24a534ad420925cbd5851b31faa11 md5: ca52daf58cea766656266c8771d8be81 @@ -6976,6 +7139,32 @@ packages: purls: [] size: 603262 timestamp: 1771378117851 +- conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.2-default_h4379cf1_1000.conda + sha256: 8cdf11333a81085468d9aa536ebb155abd74adc293576f6013fc0c85a7a90da3 + md5: 3b576f6860f838f950c570f4433b086e + depends: + - libwinpthread >=12.0.0.r4.gg4f2fc60ca + - libxml2 + - libxml2-16 >=2.14.6 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 2411241 + timestamp: 1765104337762 +- conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda + sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7 + md5: 64571d1dd6cdcfa25d0664a5950fdaa2 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: LGPL-2.1-only + purls: [] + size: 696926 + timestamp: 1754909290005 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda sha256: 755c55ebab181d678c12e49cced893598f2bab22d582fbbf4d8b83c18be207eb md5: c7c83eecbb72d88b940c249af56c8b17 @@ -7126,6 +7315,20 @@ packages: purls: [] size: 33731 timestamp: 1750274110928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda + sha256: 6dc30b28f32737a1c52dada10c8f3a41bc9e021854215efca04a7f00487d09d9 + md5: 89d61bc91d3f39fda0ca10fcd3c68594 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + constrains: + - openblas >=0.3.32,<0.3.33.0a0 + license: BSD-3-Clause + purls: [] + size: 5928890 + timestamp: 1774471724897 - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.32-openmp_h9e49c7b_0.conda sha256: 6764229359cd927c9efc036930ba28f83436b8d6759c5ac4ea9242fc29a7184e md5: 4058c5f8dbef6d28cb069f96b95ae6df @@ -7253,6 +7456,18 @@ packages: purls: [] size: 421195 timestamp: 1753948426421 +- conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda + sha256: 0fccf2d17026255b6e10ace1f191d0a2a18f2d65088fd02430be17c701f8ffe0 + md5: 8a86073cf3b343b87d03f41790d8b4e5 + depends: + - ucrt + constrains: + - pthreads-win32 <0.0a0 + - msys2-conda-epoch <0.0a0 + license: MIT AND BSD-3-Clause-Clear + purls: [] + size: 36621 + timestamp: 1759768399557 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c md5: 5aa797f8787fe7a17d1b0821485b5adc @@ -7262,6 +7477,42 @@ packages: purls: [] size: 100393 timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda + sha256: f905eb7046987c336122121759e7f09144729f6898f48cd06df2a945b86998d8 + md5: 1007e1bfe181a2aee214779ee7f13d30 + depends: + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.2,<6.0a0 + - libxml2-16 2.15.2 h692994f_0 + - libzlib >=1.3.1,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - icu <0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 43681 + timestamp: 1772704748950 +- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.2-h692994f_0.conda + sha256: b8c71b3b609c7cfe17f3f2a47c75394d7b30acfb8b34ad7a049ea8757b4d33df + md5: e365238134188e42ed36ee996159d482 + depends: + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.2,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - libxml2 2.15.2 + - icu <0.0a0 + license: MIT + license_family: MIT + purls: [] + size: 520078 + timestamp: 1772704728534 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda sha256: 55044c403570f0dc26e6364de4dc5368e5f3fc7ff103e867c487e2b5ab2bcda9 md5: d87ff7921124eccd67248aa483c23fec @@ -7338,6 +7589,21 @@ packages: purls: [] size: 285912 timestamp: 1774349644882 +- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.1-h4fa8253_0.conda + sha256: 64c7fe6490583f3c49c36c2f413e681072102db8abea13a3e1832f44eaf55518 + md5: d9f479404fe316e575f4a4575f3df406 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - openmp 22.1.1|22.1.1.* + - intel-openmp <0.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + purls: [] + size: 347138 + timestamp: 1774349485844 - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl name: lmfit version: 1.3.4 @@ -7827,6 +8093,20 @@ packages: - griffelib>=2.0 - typing-extensions>=4.0 ; python_full_version < '3.11' requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.1-hac47afa_11.conda + sha256: f2c2b2a3c2e7d08d78c10bef7c135a4262c80d1d48c85fb5902ca30d61d645f4 + md5: 3fd3009cef89c36e9898a6feeb0f5530 + depends: + - llvm-openmp >=22.1.1 + - tbb >=2022.3.0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: LicenseRef-IntelSimplifiedSoftwareOct2022 + license_family: Proprietary + purls: [] + size: 99997309 + timestamp: 1774449747739 - pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl name: mpld3 version: 0.5.12 @@ -11996,6 +12276,19 @@ packages: requires_dist: - wcwidth ; extra == 'widechars' requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda + sha256: abd9a489f059fba85c8ffa1abdaa4d515d6de6a3325238b8e81203b913cf65a9 + md5: 0f9817ffbe25f9e69ceba5ea70c52606 + depends: + - libhwloc >=2.12.2,<2.12.3.0a0 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: APACHE + purls: [] + size: 155869 + timestamp: 1767886839029 - pypi: https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl name: terminado version: 0.18.1 diff --git a/pixi.toml b/pixi.toml index 95994105..2cbf09c5 100644 --- a/pixi.toml +++ b/pixi.toml @@ -52,14 +52,9 @@ macos = '14.0' # Default feature configuration -[target.osx-arm64.dependencies] -gsl = '*' # GNU Scientific Library; required for pdffit2 - -[target.osx.dependencies] -gsl = '*' # GNU Scientific Library; required for pdffit2 - [dependencies] nodejs = '*' # Required for Prettier (non-Python formatting) +gsl = '*' # GNU Scientific Library; required for diffpy.pdffit2 [pypi-dependencies] # == [feature.default.pypi-dependencies] #pip = '*' # Native package installer