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/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 00000000..6c342493
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,159 @@
+# Copilot Instructions for EasyDiffraction
+
+## Project Context
+
+- 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
+ - radiation_probe: neutron and x-ray
+ - scattering_type: bragg and total scattering
+- Calculations are done using external calculation libraries:
+ - `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.
+- 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).
+- 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.
+
+## Code Style
+
+- 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 (numpy style).
+- Prefer flat over nested, explicit over clever.
+- 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.
+
+## 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()`).
+- 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.
+- 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 add dependencies without asking.
+
+## Tutorials
+
+- Jupyter notebooks (`docs/docs/tutorials/*.ipynb`) are **generated
+ artifacts** — never edit them by hand. Edit only the corresponding
+ `*.py` script, then run `pixi run notebook-convert` followed by
+ `pixi run notebook-prepare` to regenerate the notebook.
+
+## 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.
+- 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.
+- 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.
+- 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
+ `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 unit tests with `pixi run unit-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.:
+ - Add ChebyshevPolynomialBackground class
+ - Implement background_type setter on Experiment
+ - Standardize switchable-category naming convention
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..f1135fa5
--- /dev/null
+++ b/.github/workflows/lint-format.yml
@@ -0,0 +1,124 @@
+# 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 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 "| 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.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..745c0b03
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,279 @@
+# 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 "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 "Adding GNU Scientific Library (required by diffpy.pdffit2)"
+ 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
+ # 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/"
+
+ 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-colab.yaml b/.github/workflows/tutorial-tests-colab.yaml
index 08e2e2b6..30966aaa 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=1200 --color=yes
+ -n=auto
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..6dc595c7 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,8 @@ CMakeLists.txt.user*
.DS_Store
*.app
*.dmg
+
+# Misc
+.cache/
+*.log
+*.zip
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index c3d471cd..9a3855f4 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,57 +1,61 @@
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]
- # ----------------
- # 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..a08c3c48 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,6 +1,32 @@
+# Git
+.git/
+
+# Copier
+.copier-answers*.yml
+
# Pixi
.pixi
pixi.lock
+# MkDocs
+docs/overrides/
+docs/site/
+docs/docs/assets/
+
+# Python
+.pytest_cache/
+
# MyPy
-.mypy_cache
+.mypy_cache/
+
+# Ruff
+.ruff_cache/
+
+# Node
+node_modules
+
+# Misc
+.benchmarks
+.cache
+deps/
+tmp/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f05c041f..e8dc420f 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 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/DEVELOPMENT.md b/DEVELOPMENT.md
deleted file mode 100644
index 2acffeb3..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
- ```
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..a9fccc67 100644
--- a/README.md
+++ b/README.md
@@ -9,45 +9,45 @@
-**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.
+
-## 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
+**EasyDiffraction** is developed both as a Python library and as a
+cross-platform desktop application.
-We welcome contributions! Our vision is for **EasyDiffraction** to be a
-community-driven, open-source project supported by a diverse group of
-contributors.
+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).
-The project is currently maintained by the [European Spallation Source (ESS)].
+License:
+[BSD 3-Clause](https://github.com/easyscience/diffraction-lib/blob/master/LICENSE)
-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.
-
-## License
-
-**EasyDiffraction** is licensed under the [BSD 3-Clause License].
+## Useful Links
-
-[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
-
+### 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)
+
+### 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)
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/experiments.md b/docs/api-reference/experiments.md
deleted file mode 100644
index 2eb1bd91..00000000
--- a/docs/api-reference/experiments.md
+++ /dev/null
@@ -1 +0,0 @@
-::: easydiffraction.experiments
diff --git a/docs/api-reference/sample_models.md b/docs/api-reference/sample_models.md
deleted file mode 100644
index 43c54901..00000000
--- a/docs/api-reference/sample_models.md
+++ /dev/null
@@ -1 +0,0 @@
-::: easydiffraction.sample_models
diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md
new file mode 100644
index 00000000..3067e6a6
--- /dev/null
+++ b/docs/architecture/architecture.md
@@ -0,0 +1,1178 @@
+# EasyDiffraction Architecture
+
+**Version:** 1.0
+**Date:** 2026-03-24
+**Status:** Living document — updated as the project evolves
+
+---
+
+## 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.
+
+### 1.1 Supported Experiment Dimensions
+
+Every experiment is fully described by four orthogonal axes:
+
+| Axis | Options | Enum |
+| --------------- | ----------------------------------- | -------------------- |
+| Sample form | powder, single crystal | `SampleFormEnum` |
+| Scattering type | Bragg, total (PDF) | `ScatteringTypeEnum` |
+| Beam mode | constant wavelength, time-of-flight | `BeamModeEnum` |
+| Radiation probe | neutron, X-ray | `RadiationProbeEnum` |
+
+> **Planned extensions:** 1D / 2D data dimensionality, polarised /
+> unpolarised neutron beam.
+
+### 1.2 Calculation Engines
+
+External libraries perform the heavy computation:
+
+| Engine | Scope |
+| --------- | ----------------- |
+| `cryspy` | Bragg diffraction |
+| `crysfml` | Bragg diffraction |
+| `pdffit2` | Total scattering |
+
+---
+
+## 2. Core Abstractions
+
+All core types live in `core/` which contains **only** base classes and
+utilities — no domain logic.
+
+### 2.1 Object Hierarchy
+
+```shell
+GuardedBase # Controlled attribute access, parent linkage, identity
+├── CategoryItem # Single CIF category row (e.g. Cell, Peak, Instrument)
+├── CollectionBase # Ordered name→item container
+│ ├── CategoryCollection # CIF loop (e.g. AtomSites, Background, Data)
+│ └── DatablockCollection # Top-level container (e.g. Structures, Experiments)
+└── 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
+`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:
+
+- **`__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.
+
+#### 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:
+
+| 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:
+
+```python
+@property
+def name(self) -> str:
+ """Human-readable name of the experiment."""
+ return self._name
+
+
+@name.setter
+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:
+
+```python
+@property
+def sample_form(self) -> StringDescriptor:
+ """Sample form descriptor (read-only for the user)."""
+ return self._sample_form
+
+
+def _set_sample_form(self, value: str) -> None:
+ """Internal setter used by factory/CIF code during construction."""
+ self._sample_form.value = value
+```
+
+**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.
+- 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.
+
+### 2.3 CategoryItem and CategoryCollection
+
+| Aspect | `CategoryItem` | `CategoryCollection` |
+| --------------- | ---------------------------------- | ----------------------------------------- |
+| CIF analogy | Single category row | Loop (table) of rows |
+| Examples | Cell, SpaceGroup, Instrument, Peak | AtomSites, Background, Data, LinkedPhases |
+| Parameters | All `GenericDescriptorBase` attrs | Aggregated from all child items |
+| Serialisation | `as_cif` / `from_cif` | `as_cif` / `from_cif` |
+| Update hook | `_update(called_by_minimizer=)` | `_update(called_by_minimizer=)` |
+| Update priority | `_update_priority` (default 10) | `_update_priority` (default 10) |
+| 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).
+
+### 2.4 DatablockItem and DatablockCollection
+
+| Aspect | `DatablockItem` | `DatablockCollection` |
+| ------------------ | ------------------------------------------- | ------------------------------ |
+| CIF analogy | A single `data_` block | Collection of data blocks |
+| Examples | Structure, BraggPdExperiment | Structures, Experiments |
+| Category discovery | Scans `vars(self)` for categories | N/A |
+| Update cascade | `_update_categories()` — sorted by priority | N/A |
+| Parameters | Aggregated from all categories | Aggregated from all datablocks |
+| Fittable params | N/A | Non-constrained `Parameter`s |
+| Free params | N/A | Fittable + `free == True` |
+| 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.
+
+### 2.5 Variable System — Parameters and Descriptors
+
+```shell
+GuardedBase
+└── GenericDescriptorBase # name, value (validated via AttributeSpec), description
+ ├── GenericStringDescriptor # _value_type = DataTypes.STRING
+ └── GenericNumericDescriptor # _value_type = DataTypes.NUMERIC, + units
+ └── GenericParameter # + free, uncertainty, fit_min, fit_max, constrained, uid
+```
+
+CIF-bound concrete classes add a `CifHandler` for serialisation:
+
+| Class | Base | Use case |
+| ------------------- | -------------------------- | ---------------------------- |
+| `StringDescriptor` | `GenericStringDescriptor` | Read-only or writable text |
+| `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.
+
+**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:
+
+| Validator | Purpose |
+| --------------------- | -------------------------------------- |
+| `TypeValidator` | Checks Python type against `DataTypes` |
+| `RangeValidator` | `ge`, `le`, `gt`, `lt` bounds checking |
+| `MembershipValidator` | Value must be in an allowed set |
+| `RegexValidator` | Value must match a pattern |
+
+---
+
+## 3. Experiment System
+
+### 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.
+
+### 3.2 Experiment Hierarchy
+
+```shell
+DatablockItem
+└── ExperimentBase # name, type: ExperimentType, as_cif
+ ├── PdExperimentBase # + linked_phases, excluded_regions, peak, data
+ │ ├── BraggPdExperiment # + instrument, background (both via factories)
+ │ └── TotalPdExperiment # (no extra categories yet)
+ └── ScExperimentBase # + linked_crystal, extinction, instrument, data
+ ├── CwlScExperiment
+ └── TofScExperiment
+```
+
+Each concrete experiment class carries:
+
+- `type_info: TypeInfo` — tag and description for factory lookup
+- `compatibility: Compatibility` — which enum axis values it supports
+
+### 3.3 Category Ownership
+
+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
+experiment.linked_phases # CategoryCollection
+experiment.excluded_regions # CategoryCollection
+experiment.instrument # CategoryItem
+experiment.peak # CategoryItem
+experiment.data # CategoryCollection
+
+# Type-switchable — recreates the underlying object
+experiment.background_type = 'chebyshev' # triggers BackgroundFactory.create(...)
+experiment.peak_profile_type = 'thompson-cox-hastings' # triggers PeakFactory.create(...)
+experiment.extinction_type = 'shelx' # triggers ExtinctionFactory.create(...)
+experiment.linked_crystal_type = 'default' # triggers LinkedCrystalFactory.create(...)
+experiment.excluded_regions_type = 'default' # triggers ExcludedRegionsFactory.create(...)
+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.
+
+---
+
+## 4. Structure System
+
+### 4.1 Structure Hierarchy
+
+```shell
+DatablockItem
+└── Structure # name, cell, space_group, atom_sites
+```
+
+A `Structure` contains three categories:
+
+- `Cell` — unit cell parameters (`CategoryItem`)
+- `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()`.
+
+---
+
+## 5. Factory System
+
+### 5.1 FactoryBase
+
+All factories inherit from `FactoryBase`, which provides:
+
+| Feature | Method / Attribute | Description |
+| ------------------ | ---------------------------- | ------------------------------------------------- |
+| Registration | `@Factory.register` | Class decorator, appends to `_registry` |
+| Supported map | `_supported_map()` | `{tag: class}` from all registered classes |
+| Creation | `create(tag)` | Instantiate by tag string |
+| Default resolution | `default_tag(**conditions)` | Largest-subset matching on `_default_rules` |
+| Context creation | `create_default_for(**cond)` | Resolve tag → create |
+| Filtered query | `supported_for(**filters)` | Filter by `Compatibility` and `CalculatorSupport` |
+| 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`.
+
+### 5.2 Default Rules
+
+`_default_rules` maps frozensets of `(axis_name, enum_value)` tuples to
+tag strings (preferably enum values for type safety):
+
+```python
+class PeakFactory(FactoryBase):
+ _default_rules = {
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('beam_mode', BeamModeEnum.CONSTANT_WAVELENGTH),
+ }): PeakProfileTypeEnum.PSEUDO_VOIGT,
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('beam_mode', BeamModeEnum.TIME_OF_FLIGHT),
+ }): PeakProfileTypeEnum.PSEUDO_VOIGT_IKEDA_CARPENTER,
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.TOTAL),
+ }): PeakProfileTypeEnum.GAUSSIAN_DAMPED_SINC,
+ }
+```
+
+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:
+
+```python
+@PeakFactory.register
+class CwlPseudoVoigt(PeakBase, CwlBroadeningMixin):
+ type_info = TypeInfo(
+ tag='pseudo-voigt',
+ description='Pseudo-Voigt profile',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+```
+
+| Metadata | Purpose |
+| ------------------- | ------------------------------------------------------- |
+| `TypeInfo` | Stable tag for lookup/serialisation + human description |
+| `Compatibility` | Which enum axis values this class works with |
+| `CalculatorSupport` | Which calculation engines support this class |
+
+### 5.4 Registration Trigger
+
+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
+from .chebyshev import ChebyshevPolynomialBackground
+from .line_segment import LineSegmentBackground
+```
+
+### 5.5 All Factories
+
+| Factory | Domain | Tags resolve to |
+| ---------------------------- | ---------------------- | ----------------------------------------------------------- |
+| `BackgroundFactory` | Background categories | `LineSegmentBackground`, `ChebyshevPolynomialBackground` |
+| `PeakFactory` | Peak profiles | `CwlPseudoVoigt`, `TofPseudoVoigtIkedaCarpenter`, … |
+| `InstrumentFactory` | Instruments | `CwlPdInstrument`, `TofPdInstrument`, … |
+| `DataFactory` | Data collections | `PdCwlData`, `PdTofData`, `ReflnData`, `TotalData` |
+| `ExtinctionFactory` | Extinction models | `ShelxExtinction` |
+| `LinkedCrystalFactory` | Linked-crystal refs | `LinkedCrystal` |
+| `ExcludedRegionsFactory` | Excluded regions | `ExcludedRegions` |
+| `LinkedPhasesFactory` | Linked phases | `LinkedPhases` |
+| `ExperimentTypeFactory` | Experiment descriptors | `ExperimentType` |
+| `CellFactory` | Unit cells | `Cell` |
+| `SpaceGroupFactory` | Space groups | `SpaceGroup` |
+| `AtomSitesFactory` | Atom sites | `AtomSites` |
+| `AliasesFactory` | Parameter aliases | `Aliases` |
+| `ConstraintsFactory` | Parameter constraints | `Constraints` |
+| `FitModeFactory` | Fit-mode category | `FitMode` |
+| `JointFitExperimentsFactory` | Joint-fit weights | `JointFitExperiments` |
+| `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
+> exists today).
+
+### 5.6 Tag Naming Convention
+
+Tags are the user-facing identifiers for selecting types. They must be:
+
+- **Consistent** — use the same abbreviations everywhere.
+- **Hyphen-separated** — all lowercase, words joined by hyphens.
+- **Semantically ordered** — from general to specific.
+- **Unique within a factory** — but may overlap across factories.
+
+#### Standard Abbreviations
+
+| Concept | Abbreviation | Never use |
+| ------------------- | ------------ | --------------------------- |
+| Powder | `pd` | `powder` |
+| Single crystal | `sc` | `single-crystal` |
+| Constant wavelength | `cwl` | `cw`, `constant-wavelength` |
+| Time-of-flight | `tof` | `time-of-flight` |
+| Bragg (scattering) | `bragg` | |
+| Total (scattering) | `total` | |
+
+#### Complete Tag Registry
+
+**Background tags**
+
+| Tag | Class |
+| -------------- | ------------------------------- |
+| `line-segment` | `LineSegmentBackground` |
+| `chebyshev` | `ChebyshevPolynomialBackground` |
+
+**Peak tags**
+
+| Tag | Class |
+| ---------------------------------- | ------------------------------ |
+| `pseudo-voigt` | `CwlPseudoVoigt` |
+| `split-pseudo-voigt` | `CwlSplitPseudoVoigt` |
+| `thompson-cox-hastings` | `CwlThompsonCoxHastings` |
+| `tof-pseudo-voigt` | `TofPseudoVoigt` |
+| `tof-pseudo-voigt-ikeda-carpenter` | `TofPseudoVoigtIkedaCarpenter` |
+| `tof-pseudo-voigt-back-to-back` | `TofPseudoVoigtBackToBack` |
+| `gaussian-damped-sinc` | `TotalGaussianDampedSinc` |
+
+**Instrument tags**
+
+| Tag | Class |
+| -------- | ----------------- |
+| `cwl-pd` | `CwlPdInstrument` |
+| `cwl-sc` | `CwlScInstrument` |
+| `tof-pd` | `TofPdInstrument` |
+| `tof-sc` | `TofScInstrument` |
+
+**Data tags**
+
+| Tag | Class |
+| -------------- | ----------- |
+| `bragg-pd-cwl` | `PdCwlData` |
+| `bragg-pd-tof` | `PdTofData` |
+| `bragg-sc` | `ReflnData` |
+| `total-pd` | `TotalData` |
+
+**Extinction tags**
+
+| Tag | Class |
+| ------- | ----------------- |
+| `shelx` | `ShelxExtinction` |
+
+**Linked-crystal tags**
+
+| Tag | Class |
+| --------- | --------------- |
+| `default` | `LinkedCrystal` |
+
+**Experiment tags**
+
+| Tag | Class |
+| -------------- | ------------------- |
+| `bragg-pd` | `BraggPdExperiment` |
+| `total-pd` | `TotalPdExperiment` |
+| `bragg-sc-cwl` | `CwlScExperiment` |
+| `bragg-sc-tof` | `TofScExperiment` |
+
+**Calculator tags**
+
+| Tag | Class |
+| --------- | ------------------- |
+| `cryspy` | `CryspyCalculator` |
+| `crysfml` | `CrysfmlCalculator` |
+| `pdffit` | `PdffitCalculator` |
+
+**Minimizer tags**
+
+| Tag | Class |
+| ----------------------- | ----------------------------------------- |
+| `lmfit` | `LmfitMinimizer` |
+| `lmfit (leastsq)` | `LmfitMinimizer` (method=`leastsq`) |
+| `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.
+
+### 5.7 Metadata Classification — Which Classes Get What
+
+#### The Rule
+
+> **If a concrete class is created by a factory, it gets `type_info`,
+> `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.**
+
+#### 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.
+
+#### Singleton CategoryItems — factory-created (get all three)
+
+| Class | Factory |
+| ------------------------------ | ----------------------- |
+| `CwlPdInstrument` | `InstrumentFactory` |
+| `CwlScInstrument` | `InstrumentFactory` |
+| `TofPdInstrument` | `InstrumentFactory` |
+| `TofScInstrument` | `InstrumentFactory` |
+| `CwlPseudoVoigt` | `PeakFactory` |
+| `CwlSplitPseudoVoigt` | `PeakFactory` |
+| `CwlThompsonCoxHastings` | `PeakFactory` |
+| `TofPseudoVoigt` | `PeakFactory` |
+| `TofPseudoVoigtIkedaCarpenter` | `PeakFactory` |
+| `TofPseudoVoigtBackToBack` | `PeakFactory` |
+| `TotalGaussianDampedSinc` | `PeakFactory` |
+| `ShelxExtinction` | `ExtinctionFactory` |
+| `LinkedCrystal` | `LinkedCrystalFactory` |
+| `Cell` | `CellFactory` |
+| `SpaceGroup` | `SpaceGroupFactory` |
+| `ExperimentType` | `ExperimentTypeFactory` |
+| `FitMode` | `FitModeFactory` |
+
+#### CategoryCollections — factory-created (get all three)
+
+| Class | Factory |
+| ------------------------------- | ---------------------------- |
+| `LineSegmentBackground` | `BackgroundFactory` |
+| `ChebyshevPolynomialBackground` | `BackgroundFactory` |
+| `PdCwlData` | `DataFactory` |
+| `PdTofData` | `DataFactory` |
+| `TotalData` | `DataFactory` |
+| `ReflnData` | `DataFactory` |
+| `ExcludedRegions` | `ExcludedRegionsFactory` |
+| `LinkedPhases` | `LinkedPhasesFactory` |
+| `AtomSites` | `AtomSitesFactory` |
+| `Aliases` | `AliasesFactory` |
+| `Constraints` | `ConstraintsFactory` |
+| `JointFitExperiments` | `JointFitExperimentsFactory` |
+
+#### CategoryItems that are ONLY children of collections (NO metadata)
+
+| Class | Parent collection |
+| -------------------- | ------------------------------- |
+| `LineSegment` | `LineSegmentBackground` |
+| `PolynomialTerm` | `ChebyshevPolynomialBackground` |
+| `AtomSite` | `AtomSites` |
+| `PdCwlDataPoint` | `PdCwlData` |
+| `PdTofDataPoint` | `PdTofData` |
+| `TotalDataPoint` | `TotalData` |
+| `Refln` | `ReflnData` |
+| `LinkedPhase` | `LinkedPhases` |
+| `ExcludedRegion` | `ExcludedRegions` |
+| `Alias` | `Aliases` |
+| `Constraint` | `Constraints` |
+| `JointFitExperiment` | `JointFitExperiments` |
+
+#### Non-category classes — factory-created (get `type_info` only)
+
+| Class | Factory | Notes |
+| ------------------- | ------------------- | -------------------------------------------------------- |
+| `CryspyCalculator` | `CalculatorFactory` | No `compatibility` — limitations expressed on categories |
+| `CrysfmlCalculator` | `CalculatorFactory` | (same) |
+| `PdffitCalculator` | `CalculatorFactory` | (same) |
+| `LmfitMinimizer` | `MinimizerFactory` | `type_info` only |
+| `DfolsMinimizer` | `MinimizerFactory` | (same) |
+| `BraggPdExperiment` | `ExperimentFactory` | `type_info` + `compatibility` (no `calculator_support`) |
+| `TotalPdExperiment` | `ExperimentFactory` | (same) |
+| `CwlScExperiment` | `ExperimentFactory` | (same) |
+| `TofScExperiment` | `ExperimentFactory` | (same) |
+
+---
+
+## 6. Analysis
+
+### 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 experiment exposes the standard switchable-category API:
+
+- `calculator` — read-only property (lazy, auto-resolved on first
+ access)
+- `calculator_type` — getter + setter
+- `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'`).
+
+### 6.3 Fitter
+
+`Fitter` wraps a minimiser instance and orchestrates the fitting
+workflow:
+
+1. Collect `free_parameters` from structures + experiments.
+2. Record start values.
+3. Build an objective function that calls the calculator.
+4. Delegate to `minimizer.fit()`.
+5. Sync results (values + uncertainties) back to parameters.
+
+### 6.4 Analysis Object
+
+`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`.
+- 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()`,
+ `show_free_params()`, `how_to_access_parameters()`
+- Fitting: `fit()`, `show_fit_results()`
+- Aliases and constraints (switchable categories with `aliases_type`,
+ `constraints_type`, `fit_mode_type`, `joint_fit_experiments_type`)
+
+---
+
+## 7. Project — The Top-Level Façade
+
+`Project` is the single entry point for the user:
+
+```python
+import easydiffraction as ed
+
+project = ed.Project(name='my_project')
+```
+
+It owns and coordinates all components:
+
+| Property | Type | Description |
+| --------------------- | ------------- | ---------------------------------------- |
+| `project.info` | `ProjectInfo` | Metadata: name, title, description, path |
+| `project.structures` | `Structures` | Collection of structure datablocks |
+| `project.experiments` | `Experiments` | Collection of experiment datablocks |
+| `project.analysis` | `Analysis` | Calculator, minimiser, fitting |
+| `project.summary` | `Summary` | Report generation |
+| `project.plotter` | `Plotter` | Visualisation |
+| `project.verbosity` | `str` | Console output level (full/short/silent) |
+
+### 7.1 Data Flow
+
+```
+Parameter.value set
+ → AttributeSpec validation (type + value)
+ → _need_categories_update = True (on parent DatablockItem)
+
+Plot / CIF export / fit objective evaluation
+ → _update_categories()
+ → categories sorted by _update_priority
+ → each category._update()
+ → background: interpolate/evaluate → write to data
+ → calculator: compute pattern → write to data
+ → _need_categories_update = False
+```
+
+### 7.2 Persistence
+
+Projects are saved as a directory of CIF files:
+
+```shell
+project_dir/
+├── project.cif # ProjectInfo
+├── analysis.cif # Analysis settings
+├── summary.cif # Summary report
+├── structures/
+│ └── lbco.cif # One file per structure
+└── experiments/
+ └── hrpt.cif # One file per experiment
+```
+
+### 7.3 Verbosity
+
+`Project.verbosity` controls how much console output operations produce.
+It is backed by `VerbosityEnum` (in `utils/enums.py`) and accepts three
+values:
+
+| Level | Enum member | Behaviour |
+| -------- | ---------------------- | -------------------------------------------------- |
+| `full` | `VerbosityEnum.FULL` | Multi-line output with headers, tables, and detail |
+| `short` | `VerbosityEnum.SHORT` | One-line status message per action |
+| `silent` | `VerbosityEnum.SILENT` | No console output |
+
+The default is `'full'`.
+
+```python
+project.verbosity = 'short'
+```
+
+**Resolution order:** methods that produce console output (e.g.
+`analysis.fit()`, `experiments.add_from_data_path()`) accept an optional
+`verbosity` keyword argument. When the argument is `None` (the default),
+the method reads `project.verbosity`. When a string is passed, it
+overrides the project-level setting for that single call.
+
+```python
+# Use project-level default for all operations
+project.verbosity = 'short'
+project.analysis.fit() # → short mode
+
+# Override for a single call
+project.analysis.fit(verbosity='silent') # → silent, project stays short
+```
+
+**Output styles per level:**
+
+- **Data loading** — `full`: paragraph header + detail line; `short`:
+ `✅ Data loaded: Experiment 🔬 'name'. N points.`; `silent`: nothing.
+- **Fitting** — `full`: per-iteration progress table with improvement
+ percentages; `short`: one-row-per-experiment summary table; `silent`:
+ nothing.
+
+---
+
+## 8. User-Facing API Patterns
+
+All examples below are drawn from the actual tutorials (`tutorials/`).
+
+> **Notebook workflow:** Jupyter notebooks (`*.ipynb`) in
+> `docs/docs/tutorials/` are generated artifacts. Edit only the
+> corresponding `*.py` script, then run `pixi run notebook-convert`
+> followed by `pixi run notebook-prepare` to regenerate the notebook.
+> Never edit `*.ipynb` files by hand.
+
+### 8.1 Project Setup
+
+```python
+import easydiffraction as ed
+
+project = ed.Project(name='lbco_hrpt')
+project.info.title = 'La0.5Ba0.5CoO3 at HRPT@PSI'
+project.save_as(dir_path='lbco_hrpt', temporary=True)
+```
+
+### 8.2 Define Structures
+
+```python
+# Create a structure datablock
+project.structures.create(name='lbco')
+
+# Set space group and unit cell
+project.structures['lbco'].space_group.name_h_m = 'P m -3 m'
+project.structures['lbco'].cell.length_a = 3.88
+
+# Add atom sites
+project.structures['lbco'].atom_sites.create(
+ label='La',
+ type_symbol='La',
+ fract_x=0,
+ fract_y=0,
+ fract_z=0,
+ wyckoff_letter='a',
+ b_iso=0.5,
+ occupancy=0.5,
+)
+
+# Show as CIF
+project.structures['lbco'].show_as_cif()
+```
+
+### 8.3 Define Experiments
+
+```python
+# Download data and create experiment from a data file
+data_path = ed.download_data(id=3, destination='data')
+project.experiments.add_from_data_path(
+ name='hrpt',
+ data_path=data_path,
+ sample_form='powder',
+ beam_mode='constant wavelength',
+ radiation_probe='neutron',
+)
+
+# Set instrument parameters
+project.experiments['hrpt'].instrument.setup_wavelength = 1.494
+project.experiments['hrpt'].instrument.calib_twotheta_offset = 0.6
+
+# Browse and select peak profile type
+project.experiments['hrpt'].show_supported_peak_profile_types()
+project.experiments['hrpt'].peak_profile_type = 'pseudo-voigt'
+
+# Set peak profile parameters
+project.experiments['hrpt'].peak.broad_gauss_u = 0.1
+project.experiments['hrpt'].peak.broad_gauss_v = -0.1
+
+# Browse and select background type
+project.experiments['hrpt'].show_supported_background_types()
+project.experiments['hrpt'].background_type = 'line-segment'
+
+# Add background points
+project.experiments['hrpt'].background.create(id='10', x=10, y=170)
+project.experiments['hrpt'].background.create(id='50', x=50, y=170)
+
+# Link structure to experiment
+project.experiments['hrpt'].linked_phases.create(id='lbco', scale=10.0)
+```
+
+### 8.4 Analysis and Fitting
+
+```python
+# Calculator is auto-resolved per experiment; override if needed
+project.experiments['hrpt'].show_supported_calculator_types()
+project.experiments['hrpt'].calculator_type = 'cryspy'
+project.analysis.current_minimizer = 'lmfit'
+
+# Plot before fitting
+project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)
+
+# Select free parameters
+project.structures['lbco'].cell.length_a.free = True
+project.experiments['hrpt'].linked_phases['lbco'].scale.free = True
+project.experiments['hrpt'].instrument.calib_twotheta_offset.free = True
+project.experiments['hrpt'].background['10'].y.free = True
+
+# Inspect free parameters
+project.analysis.show_free_params()
+
+# Fit and show results
+project.analysis.fit()
+project.analysis.show_fit_results()
+
+# Plot after fitting
+project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)
+
+# Save
+project.save()
+```
+
+### 8.5 TOF Experiment (tutorial ed-7)
+
+```python
+expt = ExperimentFactory.from_data_path(
+ name='sepd',
+ data_path=data_path,
+ beam_mode='time-of-flight',
+)
+expt.instrument.calib_d_to_tof_offset = 0.0
+expt.instrument.calib_d_to_tof_linear = 7476.91
+expt.peak_profile_type = 'pseudo-voigt * ikeda-carpenter'
+expt.peak.broad_gauss_sigma_0 = 3.0
+```
+
+### 8.6 Total Scattering / PDF (tutorial ed-12)
+
+```python
+project.experiments.add_from_data_path(
+ name='xray_pdf',
+ data_path=data_path,
+ sample_form='powder',
+ beam_mode='constant wavelength',
+ radiation_probe='xray',
+ scattering_type='total',
+)
+project.experiments['xray_pdf'].peak_profile_type = 'gaussian-damped-sinc'
+# Calculator is auto-resolved to 'pdffit' for total scattering experiments
+```
+
+---
+
+## 9. Design Principles
+
+### 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.
+- Reuse the concept of datablocks and categories from CIF.
+- `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.
+
+### 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:
+
+```python
+# ✅ Correct — type property on the experiment
+expt.background_type = 'chebyshev'
+
+# ❌ Not used — type property on the category
+expt.background.type = 'chebyshev'
+```
+
+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.
+
+| Facet | Naming pattern | Example |
+| --------------- | -------------------------------------------- | ------------------------------------------------ |
+| Current object | `` property (read-only) | `expt.background`, `expt.peak` |
+| Active type tag | `_type` property (getter + setter) | `expt.background_type`, `expt.peak_profile_type` |
+| Show supported | `show_supported__types()` | `expt.show_supported_background_types()` |
+| Show current | `show_current__type()` | `expt.show_current_peak_profile_type()` |
+
+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`.
+- **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`).
+
+### 9.5 Discoverable Supported Options
+
+The user can always discover what is supported for the current
+experiment:
+
+```python
+expt.show_supported_peak_profile_types()
+expt.show_supported_background_types()
+expt.show_supported_calculator_types()
+expt.show_supported_extinction_types()
+expt.show_supported_linked_crystal_types()
+expt.show_supported_excluded_regions_types()
+expt.show_supported_linked_phases_types()
+expt.show_supported_instrument_types()
+expt.show_supported_data_types()
+struct.show_supported_cell_types()
+struct.show_supported_space_group_types()
+struct.show_supported_atom_sites_types()
+project.analysis.show_supported_aliases_types()
+project.analysis.show_supported_constraints_types()
+project.analysis.show_supported_fit_mode_types()
+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.
+
+### 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:
+
+- 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:
+
+- **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:
+
+```python
+# ✅ Correct — compare with enum
+if self._fit_mode.mode.value == FitModeEnum.JOINT:
+
+# ❌ Wrong — compare with raw string
+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:
+
+```
+Owner (DatablockItem / Analysis)
+├── CategoryA (CategoryItem or CategoryCollection)
+├── CategoryB (CategoryItem or CategoryCollection)
+└── CategoryC (CategoryItem or CategoryCollection)
+```
+
+Never:
+
+```
+Owner
+└── CategoryA
+ └── CategoryB ← WRONG: CategoryB is a child of CategoryA
+```
+
+**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:
+
+```python
+# ✅ Correct — sibling categories on Analysis
+project.analysis.fit_mode.mode = 'joint'
+project.analysis.joint_fit_experiments['npd'].weight = 0.7
+
+# ❌ Wrong — joint_fit_experiments as a child of fit_mode
+project.analysis.fit_mode.joint_fit_experiments['npd'].weight = 0.7
+```
+
+In CIF output, sibling categories appear as independent blocks:
+
+```
+_analysis.fit_mode joint
+
+loop_
+_joint_fit_experiment.id
+_joint_fit_experiment.weight
+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 — writable property:**
+
+```python
+@property
+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.
+ """
+ return self._length_a
+
+@length_a.setter
+def length_a(self, value: float) -> None:
+ self._length_a.value = value
+```
+
+**Template — read-only property:**
+
+```python
+@property
+def length_a(self) -> Parameter:
+ """Length of the a axis of the unit cell (Å).
+
+ Reading this property returns the underlying ``Parameter``
+ object.
+ """
+ return self._length_a
+```
+
+**Quick-reference table:**
+
+| 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:**
+
+- 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;
+ `pixi run param-consistency-fix` auto-fixes violations.
+
+---
+
+## 10. Issues
+
+- **Open:** [`issues_open.md`](issues_open.md) — prioritised backlog.
+- **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
new file mode 100644
index 00000000..a67edcbe
--- /dev/null
+++ b/docs/architecture/issues_closed.md
@@ -0,0 +1,67 @@
+# EasyDiffraction — Closed Issues
+
+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`.
+
+---
+
+## 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),
+`show_supported_calculator_types()`, `show_current_calculator_type()`.
+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).
+
+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.
diff --git a/docs/architecture/issues_open.md b/docs/architecture/issues_open.md
new file mode 100644
index 00000000..05ed175f
--- /dev/null
+++ b/docs/architecture/issues_open.md
@@ -0,0 +1,359 @@
+# 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.
+
+**Legend:** 🔴 High · 🟡 Medium · 🟢 Low
+
+---
+
+## 1. 🔴 Implement `Project.load()`
+
+**Type:** Completeness
+
+`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.
+
+**Fix:** implement `load()` that reads CIF files from the project
+directory and reconstructs structures, experiments, and analysis
+settings.
+
+**Depends on:** nothing (standalone).
+
+---
+
+## 2. 🟡 Restore Minimiser Variant Support
+
+**Type:** Feature loss + Design limitation
+
+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.
+
+**Fix:** decide on an approach (thin subclasses, extended registry, or
+two-level selection) and implement. Thin subclasses is the quickest.
+
+**Planned tags:**
+
+| Tag | Description |
+| ----------------------- | ------------------------------------------------------------------------ |
+| `lmfit` | LMFIT library using the default Levenberg-Marquardt least squares method |
+| `lmfit (leastsq)` | LMFIT library with Levenberg-Marquardt least squares method |
+| `lmfit (least_squares)` | LMFIT library with SciPy's trust region reflective algorithm |
+| `dfols` | DFO-LS library for derivative-free least-squares optimization |
+
+**Trade-offs:**
+
+| Approach | Pros | Cons |
+| -------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
+| **A. Thin subclasses** (one per variant) | Works today; each variant gets full metadata; no `FactoryBase` changes | Class proliferation; boilerplate |
+| **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).
+
+---
+
+## 3. 🟡 Rebuild Joint-Fit Weights on Every Fit
+
+**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.
+
+**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.
+
+---
+
+## 4. 🔴 Refresh Constraint State Before Automatic Updates and Fitting
+
+**Type:** Correctness
+
+`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.
+
+**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.
+
+**Depends on:** nothing.
+
+---
+
+## 5. 🟡 Make `Analysis` a `DatablockItem`
+
+**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.
+
+**Fix:** make `Analysis` extend `DatablockItem`, or extract a shared
+`_update_categories()` protocol.
+
+**Depends on:** benefits from issue 1 (load/save) being designed first.
+
+---
+
+## 6. 🔴 Restrict `data_type` Switching to Compatible Types and Preserve Data Safety
+
+**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.
+
+**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.
+
+**Depends on:** nothing.
+
+---
+
+## 7. 🟡 Eliminate Dummy `Experiments` Wrapper in Single-Fit Mode
+
+**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.
+
+**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.
+
+---
+
+## 8. 🟡 Add Explicit `create()` Signatures on Collections
+
+**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.
+
+**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.
+
+---
+
+## 9. 🟢 Add Future Enum Extensions
+
+**Type:** Design improvement
+
+The four current experiment axes will be extended with at least two
+more:
+
+| New axis | Options | Enum (proposed) |
+| ------------------- | ---------------------- | ------------------------ |
+| Data dimensionality | 1D, 2D | `DataDimensionalityEnum` |
+| 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).
+
+**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.
+
+---
+
+## 10. 🟢 Unify Project-Level Update Orchestration
+
+**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.
+
+**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).
+
+---
+
+## 11. 🟢 Document Category `_update` Contract
+
+**Type:** Maintainability
+
+`_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.
+- **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.
+
+**Depends on:** nothing.
+
+---
+
+## 12. 🟢 Add CIF Round-Trip Integration Test
+
+**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.
+
+**Depends on:** issue 1 (`Project.load()` implementation).
+
+---
+
+## 13. 🟢 Suppress Redundant Dirty-Flag Sets in Symmetry Constraints
+
+**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.
+
+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.
+
+**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.
+
+**Depends on:** nothing, but low priority.
+
+---
+
+## 14. 🟢 Finer-Grained Parameter Change Tracking
+
+**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.
+
+**Depends on:** nothing, but low priority.
+
+---
+
+## 15. 🟡 Validate Joint-Fit Weights Before Residual Normalisation
+
+**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.
+
+**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.
+
+---
+
+## 16. 🟡 Persist Per-Experiment `calculator_type`
+
+**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.
+
+**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).
+
+---
+
+## Summary
+
+| # | Issue | Severity | Type |
+| --- | ------------------------------------------ | -------- | ----------------------- |
+| 1 | Implement `Project.load()` | 🔴 High | Completeness |
+| 2 | Restore minimiser variants | 🟡 Med | Feature loss |
+| 3 | Rebuild joint-fit weights | 🟡 Med | Fragility |
+| 4 | Refresh constraint state before auto-apply | 🔴 High | Correctness |
+| 5 | `Analysis` as `DatablockItem` | 🟡 Med | Consistency |
+| 6 | Restrict `data_type` switching | 🔴 High | Correctness/Data safety |
+| 7 | Eliminate dummy `Experiments` | 🟡 Med | Fragility |
+| 8 | Explicit `create()` signatures | 🟡 Med | API safety |
+| 9 | Future enum extensions | 🟢 Low | Design |
+| 10 | Unify update orchestration | 🟢 Low | Maintainability |
+| 11 | Document `_update` contract | 🟢 Low | Maintainability |
+| 12 | CIF round-trip integration test | 🟢 Low | Quality |
+| 13 | Suppress redundant dirty-flag sets | 🟢 Low | Performance |
+| 14 | Finer-grained change tracking | 🟢 Low | Performance |
+| 15 | Validate joint-fit weights | 🟡 Med | Correctness |
+| 16 | Persist per-experiment `calculator_type` | 🟡 Med | Completeness |
diff --git a/docs/architecture/package-structure-full.md b/docs/architecture/package-structure-full.md
index e24eadab..74662449 100644
--- a/docs/architecture/package-structure-full.md
+++ b/docs/architecture/package-structure-full.md
@@ -67,37 +67,176 @@
│ │ └── 🏷️ class GuardedBase
│ ├── 📄 identity.py
│ │ └── 🏷️ class Identity
-│ ├── 📄 parameters.py
-│ │ ├── 🏷️ class GenericDescriptorBase
-│ │ ├── 🏷️ class GenericStringDescriptor
-│ │ ├── 🏷️ class GenericNumericDescriptor
-│ │ ├── 🏷️ class GenericParameter
-│ │ ├── 🏷️ class StringDescriptor
-│ │ ├── 🏷️ class NumericDescriptor
-│ │ └── 🏷️ class Parameter
-│ ├── 📄 singletons.py
+│ ├── 📄 metadata.py
+│ │ ├── 🏷️ class TypeInfo
+│ │ ├── 🏷️ class Compatibility
+│ │ └── 🏷️ class CalculatorSupport
+│ ├── 📄 singleton.py
│ │ ├── 🏷️ class SingletonBase
│ │ ├── 🏷️ class UidMapHandler
│ │ └── 🏷️ class ConstraintsHandler
-│ └── 📄 validation.py
-│ ├── 🏷️ class DataTypes
-│ ├── 🏷️ class ValidationStage
-│ ├── 🏷️ class ValidatorBase
-│ ├── 🏷️ class TypeValidator
-│ ├── 🏷️ class RangeValidator
-│ ├── 🏷️ class MembershipValidator
-│ ├── 🏷️ class RegexValidator
-│ └── 🏷️ class AttributeSpec
+│ ├── 📄 validation.py
+│ │ ├── 🏷️ class DataTypeHints
+│ │ ├── 🏷️ class DataTypes
+│ │ ├── 🏷️ class ValidationStage
+│ │ ├── 🏷️ class ValidatorBase
+│ │ ├── 🏷️ class TypeValidator
+│ │ ├── 🏷️ class RangeValidator
+│ │ ├── 🏷️ class MembershipValidator
+│ │ ├── 🏷️ class RegexValidator
+│ │ └── 🏷️ class AttributeSpec
+│ └── 📄 variable.py
+│ ├── 🏷️ class GenericDescriptorBase
+│ ├── 🏷️ class GenericStringDescriptor
+│ ├── 🏷️ class GenericNumericDescriptor
+│ ├── 🏷️ class GenericParameter
+│ ├── 🏷️ class StringDescriptor
+│ ├── 🏷️ class NumericDescriptor
+│ └── 🏷️ class Parameter
├── 📁 crystallography
│ ├── 📄 __init__.py
│ ├── 📄 crystallography.py
│ └── 📄 space_groups.py
+├── 📁 datablocks
+│ ├── 📁 experiment
+│ │ ├── 📁 categories
+│ │ │ ├── 📁 background
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 base.py
+│ │ │ │ │ └── 🏷️ class BackgroundBase
+│ │ │ │ ├── 📄 chebyshev.py
+│ │ │ │ │ ├── 🏷️ class PolynomialTerm
+│ │ │ │ │ └── 🏷️ class ChebyshevPolynomialBackground
+│ │ │ │ ├── 📄 enums.py
+│ │ │ │ │ └── 🏷️ class BackgroundTypeEnum
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ │ └── 🏷️ class BackgroundFactory
+│ │ │ │ └── 📄 line_segment.py
+│ │ │ │ ├── 🏷️ class LineSegment
+│ │ │ │ └── 🏷️ class LineSegmentBackground
+│ │ │ ├── 📁 data
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 bragg_pd.py
+│ │ │ │ │ ├── 🏷️ class PdDataPointBaseMixin
+│ │ │ │ │ ├── 🏷️ class PdCwlDataPointMixin
+│ │ │ │ │ ├── 🏷️ class PdTofDataPointMixin
+│ │ │ │ │ ├── 🏷️ class PdCwlDataPoint
+│ │ │ │ │ ├── 🏷️ class PdTofDataPoint
+│ │ │ │ │ ├── 🏷️ class PdDataBase
+│ │ │ │ │ ├── 🏷️ class PdCwlData
+│ │ │ │ │ └── 🏷️ class PdTofData
+│ │ │ │ ├── 📄 bragg_sc.py
+│ │ │ │ │ ├── 🏷️ class Refln
+│ │ │ │ │ └── 🏷️ class ReflnData
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ │ └── 🏷️ class DataFactory
+│ │ │ │ └── 📄 total_pd.py
+│ │ │ │ ├── 🏷️ class TotalDataPoint
+│ │ │ │ ├── 🏷️ class TotalDataBase
+│ │ │ │ └── 🏷️ class TotalData
+│ │ │ ├── 📁 instrument
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 base.py
+│ │ │ │ │ └── 🏷️ class InstrumentBase
+│ │ │ │ ├── 📄 cwl.py
+│ │ │ │ │ ├── 🏷️ class CwlInstrumentBase
+│ │ │ │ │ ├── 🏷️ class CwlScInstrument
+│ │ │ │ │ └── 🏷️ class CwlPdInstrument
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ │ └── 🏷️ class InstrumentFactory
+│ │ │ │ └── 📄 tof.py
+│ │ │ │ ├── 🏷️ class TofScInstrument
+│ │ │ │ └── 🏷️ class TofPdInstrument
+│ │ │ ├── 📁 peak
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 base.py
+│ │ │ │ │ └── 🏷️ class PeakBase
+│ │ │ │ ├── 📄 cwl.py
+│ │ │ │ │ ├── 🏷️ class CwlPseudoVoigt
+│ │ │ │ │ ├── 🏷️ class CwlSplitPseudoVoigt
+│ │ │ │ │ └── 🏷️ class CwlThompsonCoxHastings
+│ │ │ │ ├── 📄 cwl_mixins.py
+│ │ │ │ │ ├── 🏷️ class CwlBroadeningMixin
+│ │ │ │ │ ├── 🏷️ class EmpiricalAsymmetryMixin
+│ │ │ │ │ └── 🏷️ class FcjAsymmetryMixin
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ │ └── 🏷️ class PeakFactory
+│ │ │ │ ├── 📄 tof.py
+│ │ │ │ │ ├── 🏷️ class TofPseudoVoigt
+│ │ │ │ │ ├── 🏷️ class TofPseudoVoigtIkedaCarpenter
+│ │ │ │ │ └── 🏷️ class TofPseudoVoigtBackToBack
+│ │ │ │ ├── 📄 tof_mixins.py
+│ │ │ │ │ ├── 🏷️ class TofBroadeningMixin
+│ │ │ │ │ └── 🏷️ class IkedaCarpenterAsymmetryMixin
+│ │ │ │ ├── 📄 total.py
+│ │ │ │ │ └── 🏷️ class TotalGaussianDampedSinc
+│ │ │ │ └── 📄 total_mixins.py
+│ │ │ │ └── 🏷️ class TotalBroadeningMixin
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 excluded_regions.py
+│ │ │ │ ├── 🏷️ class ExcludedRegion
+│ │ │ │ └── 🏷️ class ExcludedRegions
+│ │ │ ├── 📄 experiment_type.py
+│ │ │ │ └── 🏷️ class ExperimentType
+│ │ │ ├── 📄 extinction.py
+│ │ │ │ └── 🏷️ class Extinction
+│ │ │ ├── 📄 linked_crystal.py
+│ │ │ │ └── 🏷️ class LinkedCrystal
+│ │ │ └── 📄 linked_phases.py
+│ │ │ ├── 🏷️ class LinkedPhase
+│ │ │ └── 🏷️ class LinkedPhases
+│ │ ├── 📁 item
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 base.py
+│ │ │ │ ├── 🏷️ class ExperimentBase
+│ │ │ │ ├── 🏷️ class ScExperimentBase
+│ │ │ │ └── 🏷️ class PdExperimentBase
+│ │ │ ├── 📄 bragg_pd.py
+│ │ │ │ └── 🏷️ class BraggPdExperiment
+│ │ │ ├── 📄 bragg_sc.py
+│ │ │ │ ├── 🏷️ class CwlScExperiment
+│ │ │ │ └── 🏷️ class TofScExperiment
+│ │ │ ├── 📄 enums.py
+│ │ │ │ ├── 🏷️ class SampleFormEnum
+│ │ │ │ ├── 🏷️ class ScatteringTypeEnum
+│ │ │ │ ├── 🏷️ class RadiationProbeEnum
+│ │ │ │ ├── 🏷️ class BeamModeEnum
+│ │ │ │ ├── 🏷️ class CalculatorEnum
+│ │ │ │ └── 🏷️ class PeakProfileTypeEnum
+│ │ │ ├── 📄 factory.py
+│ │ │ │ └── 🏷️ class ExperimentFactory
+│ │ │ └── 📄 total_pd.py
+│ │ │ └── 🏷️ class TotalPdExperiment
+│ │ ├── 📄 __init__.py
+│ │ └── 📄 collection.py
+│ │ └── 🏷️ class Experiments
+│ ├── 📁 structure
+│ │ ├── 📁 categories
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 atom_sites.py
+│ │ │ │ ├── 🏷️ class AtomSite
+│ │ │ │ └── 🏷️ class AtomSites
+│ │ │ ├── 📄 cell.py
+│ │ │ │ └── 🏷️ class Cell
+│ │ │ └── 📄 space_group.py
+│ │ │ └── 🏷️ class SpaceGroup
+│ │ ├── 📁 item
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 base.py
+│ │ │ │ └── 🏷️ class Structure
+│ │ │ └── 📄 factory.py
+│ │ │ └── 🏷️ class StructureFactory
+│ │ ├── 📄 __init__.py
+│ │ └── 📄 collection.py
+│ │ └── 🏷️ class Structures
+│ └── 📄 __init__.py
├── 📁 display
│ ├── 📁 plotters
│ │ ├── 📄 __init__.py
│ │ ├── 📄 ascii.py
│ │ │ └── 🏷️ class AsciiPlotter
│ │ ├── 📄 base.py
+│ │ │ ├── 🏷️ class XAxisType
│ │ │ └── 🏷️ class PlotterBase
│ │ └── 📄 plotly.py
│ │ └── 🏷️ class PlotlyPlotter
@@ -123,108 +262,6 @@
│ │ └── 🏷️ class TableRendererFactory
│ └── 📄 utils.py
│ └── 🏷️ class JupyterScrollManager
-├── 📁 experiments
-│ ├── 📁 categories
-│ │ ├── 📁 background
-│ │ │ ├── 📄 __init__.py
-│ │ │ ├── 📄 base.py
-│ │ │ │ └── 🏷️ class BackgroundBase
-│ │ │ ├── 📄 chebyshev.py
-│ │ │ │ ├── 🏷️ class PolynomialTerm
-│ │ │ │ └── 🏷️ class ChebyshevPolynomialBackground
-│ │ │ ├── 📄 enums.py
-│ │ │ │ └── 🏷️ class BackgroundTypeEnum
-│ │ │ ├── 📄 factory.py
-│ │ │ │ └── 🏷️ class BackgroundFactory
-│ │ │ └── 📄 line_segment.py
-│ │ │ ├── 🏷️ class LineSegment
-│ │ │ └── 🏷️ class LineSegmentBackground
-│ │ ├── 📁 data
-│ │ │ ├── 📄 bragg_pd.py
-│ │ │ │ ├── 🏷️ class PdDataPointBaseMixin
-│ │ │ │ ├── 🏷️ class PdCwlDataPointMixin
-│ │ │ │ ├── 🏷️ class PdTofDataPointMixin
-│ │ │ │ ├── 🏷️ class PdCwlDataPoint
-│ │ │ │ ├── 🏷️ class PdTofDataPoint
-│ │ │ │ ├── 🏷️ class PdDataBase
-│ │ │ │ ├── 🏷️ class PdCwlData
-│ │ │ │ └── 🏷️ class PdTofData
-│ │ │ ├── 📄 bragg_sc.py
-│ │ │ │ └── 🏷️ class Refln
-│ │ │ ├── 📄 factory.py
-│ │ │ │ └── 🏷️ class DataFactory
-│ │ │ └── 📄 total.py
-│ │ │ ├── 🏷️ class TotalDataPoint
-│ │ │ ├── 🏷️ class TotalDataBase
-│ │ │ └── 🏷️ class TotalData
-│ │ ├── 📁 instrument
-│ │ │ ├── 📄 __init__.py
-│ │ │ ├── 📄 base.py
-│ │ │ │ └── 🏷️ class InstrumentBase
-│ │ │ ├── 📄 cwl.py
-│ │ │ │ └── 🏷️ class CwlInstrument
-│ │ │ ├── 📄 factory.py
-│ │ │ │ └── 🏷️ class InstrumentFactory
-│ │ │ └── 📄 tof.py
-│ │ │ └── 🏷️ class TofInstrument
-│ │ ├── 📁 peak
-│ │ │ ├── 📄 __init__.py
-│ │ │ ├── 📄 base.py
-│ │ │ │ └── 🏷️ class PeakBase
-│ │ │ ├── 📄 cwl.py
-│ │ │ │ ├── 🏷️ class CwlPseudoVoigt
-│ │ │ │ ├── 🏷️ class CwlSplitPseudoVoigt
-│ │ │ │ └── 🏷️ class CwlThompsonCoxHastings
-│ │ │ ├── 📄 cwl_mixins.py
-│ │ │ │ ├── 🏷️ class CwlBroadeningMixin
-│ │ │ │ ├── 🏷️ class EmpiricalAsymmetryMixin
-│ │ │ │ └── 🏷️ class FcjAsymmetryMixin
-│ │ │ ├── 📄 factory.py
-│ │ │ │ └── 🏷️ class PeakFactory
-│ │ │ ├── 📄 tof.py
-│ │ │ │ ├── 🏷️ class TofPseudoVoigt
-│ │ │ │ ├── 🏷️ class TofPseudoVoigtIkedaCarpenter
-│ │ │ │ └── 🏷️ class TofPseudoVoigtBackToBack
-│ │ │ ├── 📄 tof_mixins.py
-│ │ │ │ ├── 🏷️ class TofBroadeningMixin
-│ │ │ │ └── 🏷️ class IkedaCarpenterAsymmetryMixin
-│ │ │ ├── 📄 total.py
-│ │ │ │ └── 🏷️ class TotalGaussianDampedSinc
-│ │ │ └── 📄 total_mixins.py
-│ │ │ └── 🏷️ class TotalBroadeningMixin
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 excluded_regions.py
-│ │ │ ├── 🏷️ class ExcludedRegion
-│ │ │ └── 🏷️ class ExcludedRegions
-│ │ ├── 📄 experiment_type.py
-│ │ │ └── 🏷️ class ExperimentType
-│ │ └── 📄 linked_phases.py
-│ │ ├── 🏷️ class LinkedPhase
-│ │ └── 🏷️ class LinkedPhases
-│ ├── 📁 experiment
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 base.py
-│ │ │ ├── 🏷️ class ExperimentBase
-│ │ │ └── 🏷️ class PdExperimentBase
-│ │ ├── 📄 bragg_pd.py
-│ │ │ └── 🏷️ class BraggPdExperiment
-│ │ ├── 📄 bragg_sc.py
-│ │ │ └── 🏷️ class BraggScExperiment
-│ │ ├── 📄 enums.py
-│ │ │ ├── 🏷️ class SampleFormEnum
-│ │ │ ├── 🏷️ class ScatteringTypeEnum
-│ │ │ ├── 🏷️ class RadiationProbeEnum
-│ │ │ ├── 🏷️ class BeamModeEnum
-│ │ │ └── 🏷️ class PeakProfileTypeEnum
-│ │ ├── 📄 factory.py
-│ │ │ └── 🏷️ class ExperimentFactory
-│ │ ├── 📄 instrument_mixin.py
-│ │ │ └── 🏷️ class InstrumentMixin
-│ │ └── 📄 total_pd.py
-│ │ └── 🏷️ class TotalPdExperiment
-│ ├── 📄 __init__.py
-│ └── 📄 experiments.py
-│ └── 🏷️ class Experiments
├── 📁 io
│ ├── 📁 cif
│ │ ├── 📄 __init__.py
@@ -239,30 +276,17 @@
│ │ └── 🏷️ class Project
│ └── 📄 project_info.py
│ └── 🏷️ class ProjectInfo
-├── 📁 sample_models
-│ ├── 📁 categories
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 atom_sites.py
-│ │ │ ├── 🏷️ class AtomSite
-│ │ │ └── 🏷️ class AtomSites
-│ │ ├── 📄 cell.py
-│ │ │ └── 🏷️ class Cell
-│ │ └── 📄 space_group.py
-│ │ └── 🏷️ class SpaceGroup
-│ ├── 📁 sample_model
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 base.py
-│ │ │ └── 🏷️ class SampleModelBase
-│ │ └── 📄 factory.py
-│ │ └── 🏷️ class SampleModelFactory
-│ ├── 📄 __init__.py
-│ └── 📄 sample_models.py
-│ └── 🏷️ class SampleModels
├── 📁 summary
│ ├── 📄 __init__.py
│ └── 📄 summary.py
│ └── 🏷️ class Summary
├── 📁 utils
+│ ├── 📁 _vendored
+│ │ ├── 📁 jupyter_dark_detect
+│ │ │ ├── 📄 __init__.py
+│ │ │ └── 📄 detector.py
+│ │ ├── 📄 __init__.py
+│ │ └── 📄 theme_detect.py
│ ├── 📄 __init__.py
│ ├── 📄 environment.py
│ ├── 📄 logging.py
diff --git a/docs/architecture/package-structure-short.md b/docs/architecture/package-structure-short.md
index 7f9d5af0..efe89066 100644
--- a/docs/architecture/package-structure-short.md
+++ b/docs/architecture/package-structure-short.md
@@ -38,13 +38,75 @@
│ ├── 📄 factory.py
│ ├── 📄 guard.py
│ ├── 📄 identity.py
-│ ├── 📄 parameters.py
-│ ├── 📄 singletons.py
-│ └── 📄 validation.py
+│ ├── 📄 metadata.py
+│ ├── 📄 singleton.py
+│ ├── 📄 validation.py
+│ └── 📄 variable.py
├── 📁 crystallography
│ ├── 📄 __init__.py
│ ├── 📄 crystallography.py
│ └── 📄 space_groups.py
+├── 📁 datablocks
+│ ├── 📁 experiment
+│ │ ├── 📁 categories
+│ │ │ ├── 📁 background
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 base.py
+│ │ │ │ ├── 📄 chebyshev.py
+│ │ │ │ ├── 📄 enums.py
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ └── 📄 line_segment.py
+│ │ │ ├── 📁 data
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 bragg_pd.py
+│ │ │ │ ├── 📄 bragg_sc.py
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ └── 📄 total_pd.py
+│ │ │ ├── 📁 instrument
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 base.py
+│ │ │ │ ├── 📄 cwl.py
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ └── 📄 tof.py
+│ │ │ ├── 📁 peak
+│ │ │ │ ├── 📄 __init__.py
+│ │ │ │ ├── 📄 base.py
+│ │ │ │ ├── 📄 cwl.py
+│ │ │ │ ├── 📄 cwl_mixins.py
+│ │ │ │ ├── 📄 factory.py
+│ │ │ │ ├── 📄 tof.py
+│ │ │ │ ├── 📄 tof_mixins.py
+│ │ │ │ ├── 📄 total.py
+│ │ │ │ └── 📄 total_mixins.py
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 excluded_regions.py
+│ │ │ ├── 📄 experiment_type.py
+│ │ │ ├── 📄 extinction.py
+│ │ │ ├── 📄 linked_crystal.py
+│ │ │ └── 📄 linked_phases.py
+│ │ ├── 📁 item
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 base.py
+│ │ │ ├── 📄 bragg_pd.py
+│ │ │ ├── 📄 bragg_sc.py
+│ │ │ ├── 📄 enums.py
+│ │ │ ├── 📄 factory.py
+│ │ │ └── 📄 total_pd.py
+│ │ ├── 📄 __init__.py
+│ │ └── 📄 collection.py
+│ ├── 📁 structure
+│ │ ├── 📁 categories
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 atom_sites.py
+│ │ │ ├── 📄 cell.py
+│ │ │ └── 📄 space_group.py
+│ │ ├── 📁 item
+│ │ │ ├── 📄 __init__.py
+│ │ │ ├── 📄 base.py
+│ │ │ └── 📄 factory.py
+│ │ ├── 📄 __init__.py
+│ │ └── 📄 collection.py
+│ └── 📄 __init__.py
├── 📁 display
│ ├── 📁 plotters
│ │ ├── 📄 __init__.py
@@ -61,51 +123,6 @@
│ ├── 📄 plotting.py
│ ├── 📄 tables.py
│ └── 📄 utils.py
-├── 📁 experiments
-│ ├── 📁 categories
-│ │ ├── 📁 background
-│ │ │ ├── 📄 __init__.py
-│ │ │ ├── 📄 base.py
-│ │ │ ├── 📄 chebyshev.py
-│ │ │ ├── 📄 enums.py
-│ │ │ ├── 📄 factory.py
-│ │ │ └── 📄 line_segment.py
-│ │ ├── 📁 data
-│ │ │ ├── 📄 bragg_pd.py
-│ │ │ ├── 📄 bragg_sc.py
-│ │ │ ├── 📄 factory.py
-│ │ │ └── 📄 total.py
-│ │ ├── 📁 instrument
-│ │ │ ├── 📄 __init__.py
-│ │ │ ├── 📄 base.py
-│ │ │ ├── 📄 cwl.py
-│ │ │ ├── 📄 factory.py
-│ │ │ └── 📄 tof.py
-│ │ ├── 📁 peak
-│ │ │ ├── 📄 __init__.py
-│ │ │ ├── 📄 base.py
-│ │ │ ├── 📄 cwl.py
-│ │ │ ├── 📄 cwl_mixins.py
-│ │ │ ├── 📄 factory.py
-│ │ │ ├── 📄 tof.py
-│ │ │ ├── 📄 tof_mixins.py
-│ │ │ ├── 📄 total.py
-│ │ │ └── 📄 total_mixins.py
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 excluded_regions.py
-│ │ ├── 📄 experiment_type.py
-│ │ └── 📄 linked_phases.py
-│ ├── 📁 experiment
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 base.py
-│ │ ├── 📄 bragg_pd.py
-│ │ ├── 📄 bragg_sc.py
-│ │ ├── 📄 enums.py
-│ │ ├── 📄 factory.py
-│ │ ├── 📄 instrument_mixin.py
-│ │ └── 📄 total_pd.py
-│ ├── 📄 __init__.py
-│ └── 📄 experiments.py
├── 📁 io
│ ├── 📁 cif
│ │ ├── 📄 __init__.py
@@ -117,22 +134,16 @@
│ ├── 📄 __init__.py
│ ├── 📄 project.py
│ └── 📄 project_info.py
-├── 📁 sample_models
-│ ├── 📁 categories
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 atom_sites.py
-│ │ ├── 📄 cell.py
-│ │ └── 📄 space_group.py
-│ ├── 📁 sample_model
-│ │ ├── 📄 __init__.py
-│ │ ├── 📄 base.py
-│ │ └── 📄 factory.py
-│ ├── 📄 __init__.py
-│ └── 📄 sample_models.py
├── 📁 summary
│ ├── 📄 __init__.py
│ └── 📄 summary.py
├── 📁 utils
+│ ├── 📁 _vendored
+│ │ ├── 📁 jupyter_dark_detect
+│ │ │ ├── 📄 __init__.py
+│ │ │ └── 📄 detector.py
+│ │ ├── 📄 __init__.py
+│ │ └── 📄 theme_detect.py
│ ├── 📄 __init__.py
│ ├── 📄 environment.py
│ ├── 📄 logging.py
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/docs/api-reference/datablocks/experiment.md b/docs/docs/api-reference/datablocks/experiment.md
new file mode 100644
index 00000000..b0d19a6a
--- /dev/null
+++ b/docs/docs/api-reference/datablocks/experiment.md
@@ -0,0 +1 @@
+::: easydiffraction.datablocks.experiment
diff --git a/docs/docs/api-reference/datablocks/structure.md b/docs/docs/api-reference/datablocks/structure.md
new file mode 100644
index 00000000..43f752ff
--- /dev/null
+++ b/docs/docs/api-reference/datablocks/structure.md
@@ -0,0 +1 @@
+::: easydiffraction.datablocks.structure
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 60%
rename from docs/api-reference/index.md
rename to docs/docs/api-reference/index.md
index 25418279..351d7f56 100644
--- a/docs/api-reference/index.md
+++ b/docs/docs/api-reference/index.md
@@ -7,18 +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.
+ - [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.
-- [sample_models](sample_models.md) – Defines sample models, such as
- crystallographic structures, and manages their properties.
-- [experiments](experiments.md) – Manages experimental setups and instrument
- parameters, as well as the associated diffraction data.
-- [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/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 00000000..27628988
Binary files /dev/null and b/docs/docs/assets/images/favicon.png differ
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 53%
rename from docs/index.md
rename to docs/docs/index.md
index a34d3023..73c35423 100644
--- a/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/installation-and-setup/index.md b/docs/docs/installation-and-setup/index.md
similarity index 80%
rename from docs/installation-and-setup/index.md
rename to docs/docs/installation-and-setup/index.md
index 80b64235..0891a55f 100644
--- a/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/introduction/index.md b/docs/docs/introduction/index.md
similarity index 90%
rename from docs/introduction/index.md
rename to docs/docs/introduction/index.md
index 2996386b..a2b42b59 100644
--- a/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/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/tutorials/ed-1.py b/docs/docs/tutorials/ed-1.py
similarity index 84%
rename from tutorials/ed-1.py
rename to docs/docs/tutorials/ed-1.py
index 38bacfda..51e4e8e6 100644
--- a/tutorials/ed-1.py
+++ b/docs/docs/tutorials/ed-1.py
@@ -2,8 +2,8 @@
# # Structure Refinement: LBCO, HRPT
#
# This minimalistic example is designed to show how Rietveld refinement
-# of a crystal structure can be performed when both the sample model and
-# experiment are defined using CIF files.
+# can be performed when both the crystal structure and experiment
+# parameters are defined using CIF files.
#
# For this example, constant-wavelength neutron powder diffraction data
# for La0.5Ba0.5CoO3 from HRPT at PSI is used.
@@ -33,14 +33,14 @@
project = ed.Project()
# %% [markdown]
-# ## Step 2: Define Sample Model
+# ## Step 2: Define Crystal Structure
# %%
# Download CIF file from repository
-model_path = ed.download_data(id=1, destination='data')
+structure_path = ed.download_data(id=1, destination='data')
# %%
-project.sample_models.add(cif_path=model_path)
+project.structures.add_from_cif_path(structure_path)
# %% [markdown]
# ## Step 3: Define Experiment
@@ -50,7 +50,7 @@
expt_path = ed.download_data(id=2, destination='data')
# %%
-project.experiments.add(cif_path=expt_path)
+project.experiments.add_from_cif_path(expt_path)
# %% [markdown]
# ## Step 4: Perform Analysis
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/tutorials/ed-10.py b/docs/docs/tutorials/ed-10.py
similarity index 75%
rename from tutorials/ed-10.py
rename to docs/docs/tutorials/ed-10.py
index f3253429..1cad89ab 100644
--- a/tutorials/ed-10.py
+++ b/docs/docs/tutorials/ed-10.py
@@ -21,16 +21,16 @@
project = ed.Project()
# %% [markdown]
-# ## Add Sample Model
+# ## Add Structure
# %%
-project.sample_models.add(name='ni')
+project.structures.create(name='ni')
# %%
-project.sample_models['ni'].space_group.name_h_m = 'F m -3 m'
-project.sample_models['ni'].space_group.it_coordinate_system_code = '1'
-project.sample_models['ni'].cell.length_a = 3.52387
-project.sample_models['ni'].atom_sites.add(
+project.structures['ni'].space_group.name_h_m = 'F m -3 m'
+project.structures['ni'].space_group.it_coordinate_system_code = '1'
+project.structures['ni'].cell.length_a = 3.52387
+project.structures['ni'].atom_sites.create(
label='Ni',
type_symbol='Ni',
fract_x=0.0,
@@ -47,7 +47,7 @@
data_path = ed.download_data(id=6, destination='data')
# %%
-project.experiments.add(
+project.experiments.add_from_data_path(
name='pdf',
data_path=data_path,
sample_form='powder',
@@ -57,7 +57,7 @@
)
# %%
-project.experiments['pdf'].linked_phases.add(id='ni', scale=1.0)
+project.experiments['pdf'].linked_phases.create(id='ni', scale=1.0)
project.experiments['pdf'].peak.damp_q = 0
project.experiments['pdf'].peak.broad_q = 0.03
project.experiments['pdf'].peak.cutoff_q = 27.0
@@ -69,8 +69,8 @@
# ## Select Fitting Parameters
# %%
-project.sample_models['ni'].cell.length_a.free = True
-project.sample_models['ni'].atom_sites['Ni'].b_iso.free = True
+project.structures['ni'].cell.length_a.free = True
+project.structures['ni'].atom_sites['Ni'].b_iso.free = True
# %%
project.experiments['pdf'].linked_phases['ni'].scale.free = True
@@ -81,7 +81,6 @@
# ## Run Fitting
# %%
-project.analysis.current_calculator = 'pdffit'
project.analysis.fit()
project.analysis.show_fit_results()
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/tutorials/ed-11.py b/docs/docs/tutorials/ed-11.py
similarity index 77%
rename from tutorials/ed-11.py
rename to docs/docs/tutorials/ed-11.py
index 4a5b3176..a16dbec7 100644
--- a/tutorials/ed-11.py
+++ b/docs/docs/tutorials/ed-11.py
@@ -30,17 +30,17 @@
project.plotter.x_max = 40
# %% [markdown]
-# ## Add Sample Model
+# ## Add Structure
# %%
-project.sample_models.add(name='si')
+project.structures.create(name='si')
# %%
-sample_model = project.sample_models['si']
-sample_model.space_group.name_h_m.value = 'F d -3 m'
-sample_model.space_group.it_coordinate_system_code = '1'
-sample_model.cell.length_a = 5.43146
-sample_model.atom_sites.add(
+structure = project.structures['si']
+structure.space_group.name_h_m.value = 'F d -3 m'
+structure.space_group.it_coordinate_system_code = '1'
+structure.cell.length_a = 5.43146
+structure.atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0,
@@ -57,7 +57,7 @@
data_path = ed.download_data(id=5, destination='data')
# %%
-project.experiments.add(
+project.experiments.add_from_data_path(
name='nomad',
data_path=data_path,
sample_form='powder',
@@ -68,7 +68,7 @@
# %%
experiment = project.experiments['nomad']
-experiment.linked_phases.add(id='si', scale=1.0)
+experiment.linked_phases.create(id='si', scale=1.0)
experiment.peak.damp_q = 0.02
experiment.peak.broad_q = 0.03
experiment.peak.cutoff_q = 35.0
@@ -80,8 +80,8 @@
# ## Select Fitting Parameters
# %%
-project.sample_models['si'].cell.length_a.free = True
-project.sample_models['si'].atom_sites['Si'].b_iso.free = True
+project.structures['si'].cell.length_a.free = True
+project.structures['si'].atom_sites['Si'].b_iso.free = True
experiment.linked_phases['si'].scale.free = True
# %%
@@ -94,7 +94,6 @@
# ## Run Fitting
# %%
-project.analysis.current_calculator = 'pdffit'
project.analysis.fit()
project.analysis.show_fit_results()
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/tutorials/ed-12.py b/docs/docs/tutorials/ed-12.py
similarity index 77%
rename from tutorials/ed-12.py
rename to docs/docs/tutorials/ed-12.py
index c40cc5ad..b6701709 100644
--- a/tutorials/ed-12.py
+++ b/docs/docs/tutorials/ed-12.py
@@ -34,16 +34,16 @@
project.plotter.x_max = 30.0
# %% [markdown]
-# ## Add Sample Model
+# ## Add Structure
# %%
-project.sample_models.add(name='nacl')
+project.structures.create(name='nacl')
# %%
-project.sample_models['nacl'].space_group.name_h_m = 'F m -3 m'
-project.sample_models['nacl'].space_group.it_coordinate_system_code = '1'
-project.sample_models['nacl'].cell.length_a = 5.62
-project.sample_models['nacl'].atom_sites.add(
+project.structures['nacl'].space_group.name_h_m = 'F m -3 m'
+project.structures['nacl'].space_group.it_coordinate_system_code = '1'
+project.structures['nacl'].cell.length_a = 5.62
+project.structures['nacl'].atom_sites.create(
label='Na',
type_symbol='Na',
fract_x=0,
@@ -52,7 +52,7 @@
wyckoff_letter='a',
b_iso=1.0,
)
-project.sample_models['nacl'].atom_sites.add(
+project.structures['nacl'].atom_sites.create(
label='Cl',
type_symbol='Cl',
fract_x=0.5,
@@ -69,7 +69,7 @@
data_path = ed.download_data(id=4, destination='data')
# %%
-project.experiments.add(
+project.experiments.add_from_data_path(
name='xray_pdf',
data_path=data_path,
sample_form='powder',
@@ -96,15 +96,15 @@
project.experiments['xray_pdf'].peak.damp_particle_diameter = 0
# %%
-project.experiments['xray_pdf'].linked_phases.add(id='nacl', scale=0.5)
+project.experiments['xray_pdf'].linked_phases.create(id='nacl', scale=0.5)
# %% [markdown]
# ## Select Fitting Parameters
# %%
-project.sample_models['nacl'].cell.length_a.free = True
-project.sample_models['nacl'].atom_sites['Na'].b_iso.free = True
-project.sample_models['nacl'].atom_sites['Cl'].b_iso.free = True
+project.structures['nacl'].cell.length_a.free = True
+project.structures['nacl'].atom_sites['Na'].b_iso.free = True
+project.structures['nacl'].atom_sites['Cl'].b_iso.free = True
# %%
project.experiments['xray_pdf'].linked_phases['nacl'].scale.free = True
@@ -115,7 +115,6 @@
# ## Run Fitting
# %%
-project.analysis.current_calculator = 'pdffit'
project.analysis.fit()
project.analysis.show_fit_results()
diff --git a/docs/docs/tutorials/ed-13.ipynb b/docs/docs/tutorials/ed-13.ipynb
new file mode 100644
index 00000000..a31467a1
--- /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",
+ "`extract_metadata` 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.extract_metadata(\n",
+ " si_xye_path, r'two_theta\\s*=\\s*([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)'\n",
+ ")\n",
+ "project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = ed.extract_metadata(\n",
+ " si_xye_path, r'DIFC\\s*=\\s*([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)'\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.extract_metadata(\n",
+ " lbco_xye_path, r'two_theta\\s*=\\s*([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)'\n",
+ ")\n",
+ "project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = ed.extract_metadata(\n",
+ " lbco_xye_path, r'DIFC\\s*=\\s*([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)'\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/tutorials/ed-13.py b/docs/docs/tutorials/ed-13.py
similarity index 88%
rename from tutorials/ed-13.py
rename to docs/docs/tutorials/ed-13.py
index e2bf1110..42996532 100644
--- a/tutorials/ed-13.py
+++ b/docs/docs/tutorials/ed-13.py
@@ -52,11 +52,11 @@
#
# In EasyDiffraction, a project serves as a container for all
# information related to the analysis of a specific experiment or set of
-# experiments. It enables you to organize your data, experiments, sample
-# models, and fitting parameters in a structured manner. You can think
-# of it as a folder containing all the essential details about your
-# analysis. The project also allows us to visualize both the measured
-# and calculated diffraction patterns, among other things.
+# experiments. It enables you to organize your data, experiments,
+# crystal structures, and fitting parameters in an organized manner. You
+# can think of it as a folder containing all the essential details about
+# your analysis. The project also allows us to visualize both the
+# measured and calculated diffraction patterns, among other things.
# %% [markdown] tags=["doc-link"]
# 📖 See
@@ -120,7 +120,7 @@
# for more details about different types of experiments.
# %%
-project_1.experiments.add(
+project_1.experiments.add_from_data_path(
name='sim_si',
data_path=si_xye_path,
sample_form='powder',
@@ -185,8 +185,8 @@
# for more details about excluding regions from the measured data.
# %%
-project_1.experiments['sim_si'].excluded_regions.add(id='1', start=0, end=55000)
-project_1.experiments['sim_si'].excluded_regions.add(id='2', start=105500, end=200000)
+project_1.experiments['sim_si'].excluded_regions.create(id='1', start=0, end=55000)
+project_1.experiments['sim_si'].excluded_regions.create(id='2', start=105500, end=200000)
# %% [markdown]
# To visualize the effect of excluding the high TOF region, we can plot
@@ -212,7 +212,7 @@
# TOF, respectively.
#
# You can set them manually, but it is more convenient to use the
-# `get_value_from_xye_header` function from the EasyDiffraction library.
+# `extract_metadata` function from the EasyDiffraction library.
# %% [markdown] tags=["doc-link"]
# 📖 See
@@ -220,11 +220,11 @@
# for more details about the instrument parameters.
# %%
-project_1.experiments['sim_si'].instrument.setup_twotheta_bank = ed.get_value_from_xye_header(
- si_xye_path, 'two_theta'
+project_1.experiments['sim_si'].instrument.setup_twotheta_bank = ed.extract_metadata(
+ si_xye_path, r'two_theta\s*=\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)'
)
-project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = ed.get_value_from_xye_header(
- si_xye_path, 'DIFC'
+project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = ed.extract_metadata(
+ si_xye_path, r'DIFC\s*=\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)'
)
# %% [markdown]
@@ -355,25 +355,25 @@
# %%
project_1.experiments['sim_si'].background_type = 'line-segment'
-project_1.experiments['sim_si'].background.add(id='1', x=50000, y=0.01)
-project_1.experiments['sim_si'].background.add(id='2', x=60000, y=0.01)
-project_1.experiments['sim_si'].background.add(id='3', x=70000, y=0.01)
-project_1.experiments['sim_si'].background.add(id='4', x=80000, y=0.01)
-project_1.experiments['sim_si'].background.add(id='5', x=90000, y=0.01)
-project_1.experiments['sim_si'].background.add(id='6', x=100000, y=0.01)
-project_1.experiments['sim_si'].background.add(id='7', x=110000, y=0.01)
+project_1.experiments['sim_si'].background.create(id='1', x=50000, y=0.01)
+project_1.experiments['sim_si'].background.create(id='2', x=60000, y=0.01)
+project_1.experiments['sim_si'].background.create(id='3', x=70000, y=0.01)
+project_1.experiments['sim_si'].background.create(id='4', x=80000, y=0.01)
+project_1.experiments['sim_si'].background.create(id='5', x=90000, y=0.01)
+project_1.experiments['sim_si'].background.create(id='6', x=100000, y=0.01)
+project_1.experiments['sim_si'].background.create(id='7', x=110000, y=0.01)
# %% [markdown]
-# ### 🧩 Create a Sample Model – Si
+# ### 🧩 Create a Structure – Si
#
-# After setting up the experiment, we need to create a sample model that
+# After setting up the experiment, we need to create a structure that
# describes the crystal structure of the sample being analyzed.
#
-# In this case, we will create a sample model for silicon (Si) with a
-# cubic crystal structure. The sample model contains information about
+# In this case, we will create a structure for silicon (Si) with a
+# cubic crystal structure. The structure contains information about
# the space group, lattice parameters, atomic positions of the atoms in
# the unit cell, atom types, occupancies and atomic displacement
-# parameters. The sample model is essential for the fitting process, as
+# parameters. The structure is essential for the fitting process, as
# it is used to calculate the expected diffraction pattern.
#
# EasyDiffraction refines the crystal structure of the sample, but does
@@ -435,54 +435,54 @@
# %% [markdown]
# As with adding the experiment in the previous step, we will create a
-# default sample model and then modify its parameters to match the Si
+# default structure and then modify its parameters to match the Si
# structure.
# %% [markdown] tags=["doc-link"]
# 📖 See
-# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/model/)
-# for more details about sample models and their purpose in the data
+# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/)
+# for more details about structures and their purpose in the data
# analysis workflow.
# %% [markdown]
-# #### Add Sample Model
+# #### Add Structure
# %%
-project_1.sample_models.add(name='si')
+project_1.structures.create(name='si')
# %% [markdown]
# #### Set Space Group
# %% [markdown] tags=["doc-link"]
# 📖 See
-# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/model/#space-group-category)
+# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/#space-group-category)
# for more details about the space group.
# %%
-project_1.sample_models['si'].space_group.name_h_m = 'F d -3 m'
-project_1.sample_models['si'].space_group.it_coordinate_system_code = '2'
+project_1.structures['si'].space_group.name_h_m = 'F d -3 m'
+project_1.structures['si'].space_group.it_coordinate_system_code = '2'
# %% [markdown]
# #### Set Lattice Parameters
# %% [markdown] tags=["doc-link"]
# 📖 See
-# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/model/#cell-category)
+# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/#cell-category)
# for more details about the unit cell parameters.
# %%
-project_1.sample_models['si'].cell.length_a = 5.43
+project_1.structures['si'].cell.length_a = 5.43
# %% [markdown]
# #### Set Atom Sites
# %% [markdown] tags=["doc-link"]
# 📖 See
-# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/model/#atom-sites-category)
+# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/structure/#atom-sites-category)
# for more details about the atom sites category.
# %%
-project_1.sample_models['si'].atom_sites.add(
+project_1.structures['si'].atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0,
@@ -493,29 +493,29 @@
)
# %% [markdown]
-# ### 🔗 Assign Sample Model to Experiment
+# ### 🔗 Assign Structure to Experiment
#
-# Now we need to assign, or link, this sample model to the experiment
+# Now we need to assign, or link, this structure to the experiment
# created above. This linked crystallographic phase will be used to
# calculate the expected diffraction pattern based on the crystal
-# structure defined in the sample model.
+# structure defined in the structure.
# %% [markdown] tags=["doc-link"]
# 📖 See
# [documentation](https://docs.easydiffraction.org/lib/user-guide/analysis-workflow/experiment/#linked-phases-category)
-# for more details about linking a sample model to an experiment.
+# for more details about linking a structure to an experiment.
# %%
-project_1.experiments['sim_si'].linked_phases.add(id='si', scale=1.0)
+project_1.experiments['sim_si'].linked_phases.create(id='si', scale=1.0)
# %% [markdown]
# ### 🚀 Analyze and Fit the Data
#
-# After setting up the experiment and sample model, we can now analyze
+# After setting up the experiment and structure, we can now analyze
# the measured diffraction pattern and perform the fit. Building on the
# analogies from the EasyScience library and the previous notebooks, we
# can say that all the parameters we introduced earlier — those defining
-# the sample model (crystal structure parameters) and the experiment
+# the structure (crystal structure parameters) and the experiment
# (instrument, background, and peak profile parameters) — together form
# the complete set of parameters that can be refined during the fitting
# process.
@@ -528,12 +528,12 @@
# %% [markdown] **Reminder:**
#
# The fitting process involves comparing the measured diffraction
-# pattern with the calculated diffraction pattern based on the sample
-# model and instrument parameters. The goal is to adjust the parameters
-# of the sample model and the experiment to minimize the difference
-# between the measured and calculated diffraction patterns. This is done
-# by refining the parameters of the sample model and the instrument
-# settings to achieve a better fit.
+# pattern with the calculated diffraction pattern based on the crystal
+# structure and instrument parameters. The goal is to adjust the
+# parameters of the structure and the experiment to minimize the
+# difference between the measured and calculated diffraction patterns.
+# This is done by refining the parameters of the structure and the
+# instrument settings to achieve a better fit.
# %% [markdown] tags=["doc-link"]
# 📖 See
@@ -593,7 +593,7 @@
#
# Before performing the fit, we can visually compare the measured
# diffraction pattern with the calculated diffraction pattern based on
-# the initial parameters of the sample model and the instrument. This
+# the initial parameters of the structure and the instrument. This
# provides an indication of how well the initial parameters match the
# measured data. The `plot_meas_vs_calc` method of the project allows
# this comparison.
@@ -670,7 +670,7 @@
# setting the `d_spacing` parameter to `True`.
# %%
-project_1.plot_meas_vs_calc(expt_name='sim_si', d_spacing=True)
+project_1.plot_meas_vs_calc(expt_name='sim_si', x='d_spacing')
# %% [markdown]
# As you can see, the calculated diffraction pattern now matches the
@@ -689,7 +689,7 @@
#
# Before moving on, we can save the project to disk for later use. This
# will preserve the entire project structure, including experiments,
-# sample models, and fitting results. The project is saved into a
+# structures, and fitting results. The project is saved into a
# directory specified by the `dir_path` attribute of the project object.
# %%
@@ -753,7 +753,7 @@
# reduced data file is missing.
lbco_xye_path = ed.download_data(id=18, destination=data_dir)
-project_2.experiments.add(
+project_2.experiments.add_from_data_path(
name='sim_lbco',
data_path=lbco_xye_path,
sample_form='powder',
@@ -783,8 +783,8 @@
# %% tags=["solution", "hide-input"]
project_2.plot_meas(expt_name='sim_lbco')
-project_2.experiments['sim_lbco'].excluded_regions.add(id='1', start=0, end=55000)
-project_2.experiments['sim_lbco'].excluded_regions.add(id='2', start=105500, end=200000)
+project_2.experiments['sim_lbco'].excluded_regions.create(id='1', start=0, end=55000)
+project_2.experiments['sim_lbco'].excluded_regions.create(id='2', start=105500, end=200000)
project_2.plot_meas(expt_name='sim_lbco')
@@ -804,11 +804,11 @@
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.experiments['sim_lbco'].instrument.setup_twotheta_bank = ed.get_value_from_xye_header(
- lbco_xye_path, 'two_theta'
+project_2.experiments['sim_lbco'].instrument.setup_twotheta_bank = ed.extract_metadata(
+ lbco_xye_path, r'two_theta\s*=\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)'
)
-project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = ed.get_value_from_xye_header(
- lbco_xye_path, 'DIFC'
+project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = ed.extract_metadata(
+ lbco_xye_path, r'DIFC\s*=\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)'
)
# %% [markdown]
@@ -861,20 +861,20 @@
# %% tags=["solution", "hide-input"]
project_2.experiments['sim_lbco'].background_type = 'line-segment'
-project_2.experiments['sim_lbco'].background.add(id='1', x=50000, y=0.2)
-project_2.experiments['sim_lbco'].background.add(id='2', x=60000, y=0.2)
-project_2.experiments['sim_lbco'].background.add(id='3', x=70000, y=0.2)
-project_2.experiments['sim_lbco'].background.add(id='4', x=80000, y=0.2)
-project_2.experiments['sim_lbco'].background.add(id='5', x=90000, y=0.2)
-project_2.experiments['sim_lbco'].background.add(id='6', x=100000, y=0.2)
-project_2.experiments['sim_lbco'].background.add(id='7', x=110000, y=0.2)
+project_2.experiments['sim_lbco'].background.create(id='1', x=50000, y=0.2)
+project_2.experiments['sim_lbco'].background.create(id='2', x=60000, y=0.2)
+project_2.experiments['sim_lbco'].background.create(id='3', x=70000, y=0.2)
+project_2.experiments['sim_lbco'].background.create(id='4', x=80000, y=0.2)
+project_2.experiments['sim_lbco'].background.create(id='5', x=90000, y=0.2)
+project_2.experiments['sim_lbco'].background.create(id='6', x=100000, y=0.2)
+project_2.experiments['sim_lbco'].background.create(id='7', x=110000, y=0.2)
# %% [markdown]
-# ### 🧩 Exercise 3: Define a Sample Model – LBCO
+# ### 🧩 Exercise 3: Define a Structure – LBCO
#
-# The LBSO structure is not as simple as the Si model, as it contains
+# The LBSO structure is not as simple as the Si one, as it contains
# multiple atoms in the unit cell. It is not in COD, so we give you the
-# structural parameters in CIF format to create the sample model.
+# structural parameters in CIF format to create the structure.
#
# Note that those parameters are not necessarily the most accurate ones,
# but they are a good starting point for the fit. The aim of the study
@@ -914,7 +914,7 @@
# Note that the `occupancy` of the La and Ba atoms is 0.5
# and those atoms are located in the same position (0, 0, 0) in the unit
# cell. This means that an extra attribute `occupancy` needs to be set
-# for those atoms later in the sample model.
+# for those atoms later in the structure.
#
# We model the La/Ba site using the virtual crystal approximation. In
# this approach, the scattering is taken as a weighted average of La and
@@ -936,9 +936,9 @@
# of the random case and the extra peaks of the ordered case.
# %% [markdown]
-# #### Exercise 3.1: Create Sample Model
+# #### Exercise 3.1: Create Structure
#
-# Add a sample model for LBCO to the project. The sample model
+# Add a structure for LBCO to the project. The structure
# parameters will be set in the next exercises.
# %% [markdown]
@@ -946,19 +946,19 @@
# %% [markdown] tags=["dmsc-school-hint"]
# You can use the same approach as in the previous part of the notebook,
-# but this time you need to use the model name corresponding to the LBCO
+# but this time you need to use the name corresponding to the LBCO
# structure, e.g. 'lbco'.
# %% [markdown]
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.sample_models.add(name='lbco')
+project_2.structures.create(name='lbco')
# %% [markdown]
# #### Exercise 3.2: Set Space Group
#
-# Set the space group for the LBCO sample model.
+# Set the space group for the LBCO structure.
# %% [markdown]
# **Hint:**
@@ -971,13 +971,13 @@
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.sample_models['lbco'].space_group.name_h_m = 'P m -3 m'
-project_2.sample_models['lbco'].space_group.it_coordinate_system_code = '1'
+project_2.structures['lbco'].space_group.name_h_m = 'P m -3 m'
+project_2.structures['lbco'].space_group.it_coordinate_system_code = '1'
# %% [markdown]
# #### Exercise 3.3: Set Lattice Parameters
#
-# Set the lattice parameters for the LBCO sample model.
+# Set the lattice parameters for the LBCO structure.
# %% [markdown]
# **Hint:**
@@ -989,25 +989,25 @@
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.sample_models['lbco'].cell.length_a = 3.88
+project_2.structures['lbco'].cell.length_a = 3.88
# %% [markdown]
# #### Exercise 3.4: Set Atom Sites
#
-# Set the atom sites for the LBCO sample model.
+# Set the atom sites for the LBCO structure.
# %% [markdown]
# **Hint:**
# %% [markdown] tags=["dmsc-school-hint"]
# Use the atom sites from the CIF data. You can use the `add` method of
-# the `atom_sites` attribute of the sample model to add the atom sites.
+# the `atom_sites` attribute of the structure to add the atom sites.
# %% [markdown]
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.sample_models['lbco'].atom_sites.add(
+project_2.structures['lbco'].atom_sites.create(
label='La',
type_symbol='La',
fract_x=0,
@@ -1017,7 +1017,7 @@
b_iso=0.95,
occupancy=0.5,
)
-project_2.sample_models['lbco'].atom_sites.add(
+project_2.structures['lbco'].atom_sites.create(
label='Ba',
type_symbol='Ba',
fract_x=0,
@@ -1027,7 +1027,7 @@
b_iso=0.95,
occupancy=0.5,
)
-project_2.sample_models['lbco'].atom_sites.add(
+project_2.structures['lbco'].atom_sites.create(
label='Co',
type_symbol='Co',
fract_x=0.5,
@@ -1036,7 +1036,7 @@
wyckoff_letter='b',
b_iso=0.80,
)
-project_2.sample_models['lbco'].atom_sites.add(
+project_2.structures['lbco'].atom_sites.create(
label='O',
type_symbol='O',
fract_x=0,
@@ -1047,22 +1047,22 @@
)
# %% [markdown]
-# ### 🔗 Exercise 4: Assign Sample Model to Experiment
+# ### 🔗 Exercise 4: Assign Structure to Experiment
#
-# Now assign the LBCO sample model to the experiment created above.
+# Now assign the LBCO structure to the experiment created above.
# %% [markdown]
# **Hint:**
# %% [markdown] tags=["dmsc-school-hint"]
-# Use the `linked_phases` attribute of the experiment to link the sample
-# model.
+# Use the `linked_phases` attribute of the experiment to link the
+# crystal structure.
# %% [markdown]
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.experiments['sim_lbco'].linked_phases.add(id='lbco', scale=1.0)
+project_2.experiments['sim_lbco'].linked_phases.create(id='lbco', scale=1.0)
# %% [markdown]
# ### 🚀 Exercise 5: Analyze and Fit the Data
@@ -1176,7 +1176,7 @@
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.sample_models['lbco'].cell.length_a.free = True
+project_2.structures['lbco'].cell.length_a.free = True
project_2.analysis.fit()
project_2.analysis.show_fit_results()
@@ -1208,7 +1208,7 @@
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.plot_meas_vs_calc(expt_name='sim_lbco', d_spacing=True)
+project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing')
# %% [markdown]
# #### Exercise 5.6: Refine the Peak Profile Parameters
@@ -1225,7 +1225,7 @@
# perfectly describe the peak at about 1.38 Å, as can be seen below:
# %%
-project_2.plot_meas_vs_calc(expt_name='sim_lbco', d_spacing=True, x_min=1.35, x_max=1.40)
+project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1.35, x_max=1.40)
# %% [markdown]
# The peak profile parameters are determined based on both the
@@ -1260,7 +1260,7 @@
project_2.analysis.fit()
project_2.analysis.show_fit_results()
-project_2.plot_meas_vs_calc(expt_name='sim_lbco', d_spacing=True, x_min=1.35, x_max=1.40)
+project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1.35, x_max=1.40)
# %% [markdown]
# #### Exercise 5.7: Find Undefined Features
@@ -1283,7 +1283,7 @@
# **Solution:**
# %% tags=["solution", "hide-input"]
-project_2.plot_meas_vs_calc(expt_name='sim_lbco', x_min=1.53, x_max=1.7, d_spacing=True)
+project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1.53, x_max=1.7)
# %% [markdown]
# #### Exercise 5.8: Identify the Cause of the Unexplained Peaks
@@ -1348,14 +1348,14 @@
# confirm this hypothesis.
# %% tags=["solution", "hide-input"]
-project_1.plot_meas_vs_calc(expt_name='sim_si', x_min=1, x_max=1.7, d_spacing=True)
-project_2.plot_meas_vs_calc(expt_name='sim_lbco', x_min=1, x_max=1.7, d_spacing=True)
+project_1.plot_meas_vs_calc(expt_name='sim_si', x='d_spacing', x_min=1, x_max=1.7)
+project_2.plot_meas_vs_calc(expt_name='sim_lbco', x='d_spacing', x_min=1, x_max=1.7)
# %% [markdown]
-# #### Exercise 5.10: Create a Second Sample Model – Si as Impurity
+# #### Exercise 5.10: Create a Second Structure – Si as Impurity
#
-# Create a second sample model for the Si phase, which is the impurity
-# phase identified in the previous step. Link this sample model to the
+# Create a second structure for the Si phase, which is the impurity
+# phase identified in the previous step. Link this structure to the
# LBCO experiment.
# %% [markdown]
@@ -1363,7 +1363,7 @@
# %% [markdown] tags=["dmsc-school-hint"]
# You can use the same approach as in the previous part of the notebook,
-# but this time you need to create a sample model for Si and link it to
+# but this time you need to create a structure for Si and link it to
# the LBCO experiment.
# %% [markdown]
@@ -1371,15 +1371,15 @@
# %% tags=["solution", "hide-input"]
# Set Space Group
-project_2.sample_models.add(name='si')
-project_2.sample_models['si'].space_group.name_h_m = 'F d -3 m'
-project_2.sample_models['si'].space_group.it_coordinate_system_code = '2'
+project_2.structures.create(name='si')
+project_2.structures['si'].space_group.name_h_m = 'F d -3 m'
+project_2.structures['si'].space_group.it_coordinate_system_code = '2'
# Set Lattice Parameters
-project_2.sample_models['si'].cell.length_a = 5.43
+project_2.structures['si'].cell.length_a = 5.43
# Set Atom Sites
-project_2.sample_models['si'].atom_sites.add(
+project_2.structures['si'].atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0,
@@ -1389,8 +1389,8 @@
b_iso=0.89,
)
-# Assign Sample Model to Experiment
-project_2.experiments['sim_lbco'].linked_phases.add(id='si', scale=1.0)
+# Assign Structure to Experiment
+project_2.experiments['sim_lbco'].linked_phases.create(id='si', scale=1.0)
# %% [markdown]
# #### Exercise 5.11: Refine the Scale of the Si Phase
@@ -1443,7 +1443,7 @@
#
# To review the analysis results, you can generate and print a summary
# report using the `show_report()` method, as demonstrated in the cell
-# below. The report includes parameters related to the sample model and
+# below. The report includes parameters related to the structure and
# the experiment, such as the refined unit cell parameter `a` of LBCO.
#
# Information about the crystal or magnetic structure, along with
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-14.py b/docs/docs/tutorials/ed-14.py
new file mode 100644
index 00000000..eaa3da2a
--- /dev/null
+++ b/docs/docs/tutorials/ed-14.py
@@ -0,0 +1,109 @@
+# %% [markdown]
+# # Structure Refinement: Tb2TiO7, HEiDi
+#
+# Crystal structure refinement of Tb2TiO7 using single crystal neutron
+# diffraction data from HEiDi at FRM II.
+
+# %% [markdown]
+# ## Import Library
+
+# %%
+import easydiffraction as ed
+
+# %% [markdown]
+# ## Step 1: Define Project
+
+# %%
+# Create minimal project without name and description
+project = ed.Project()
+
+# %% [markdown]
+# ## Step 2: Define Structure
+
+# %%
+# Download CIF file from repository
+structure_path = ed.download_data(id=20, destination='data')
+
+# %%
+project.structures.add_from_cif_path(structure_path)
+
+# %%
+project.structures.show_names()
+
+# %%
+structure = project.structures['tbti']
+
+# %%
+structure.atom_sites['Tb'].b_iso = 0.0
+structure.atom_sites['Ti'].b_iso = 0.0
+structure.atom_sites['O1'].b_iso = 0.0
+structure.atom_sites['O2'].b_iso = 0.0
+
+# %%
+structure.show_as_cif()
+
+# %% [markdown]
+# ## Step 3: Define Experiment
+
+# %%
+data_path = ed.download_data(id=19, destination='data')
+
+# %%
+project.experiments.add_from_data_path(
+ name='heidi',
+ data_path=data_path,
+ sample_form='single crystal',
+ beam_mode='constant wavelength',
+ radiation_probe='neutron',
+)
+
+# %%
+experiment = project.experiments['heidi'] # TODO:
+
+# %%
+experiment.linked_crystal.id = 'tbti'
+experiment.linked_crystal.scale = 1.0
+
+# %%
+experiment.instrument.setup_wavelength = 0.793
+
+# %%
+experiment.extinction.mosaicity = 29820
+experiment.extinction.radius = 30
+
+# %% [markdown]
+# ## Step 4: Perform Analysis
+
+# %%
+project.plot_meas_vs_calc(expt_name='heidi')
+
+# %%
+experiment.linked_crystal.scale.free = True
+experiment.extinction.radius.free = True
+
+# %%
+experiment.show_as_cif()
+
+# %%
+# Start refinement. All parameters, which have standard uncertainties
+# in the input CIF files, are refined by default.
+project.analysis.fit()
+
+# %%
+# Show fit results summary
+project.analysis.show_fit_results()
+
+# %%
+experiment.show_as_cif()
+
+# %%
+project.experiments.show_names()
+
+# %%
+project.plot_meas_vs_calc(expt_name='heidi')
+
+# %% [markdown]
+# ## Step 5: Show Project Summary
+
+# %%
+project.summary.show_report()
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-15.py b/docs/docs/tutorials/ed-15.py
new file mode 100644
index 00000000..4ae4933a
--- /dev/null
+++ b/docs/docs/tutorials/ed-15.py
@@ -0,0 +1,100 @@
+# %% [markdown]
+# # Structure Refinement: Taurine, SENJU
+#
+# Crystal structure refinement of Taurine using time-of-flight single
+# crystal neutron diffraction data from SENJU at J-PARC.
+
+# %% [markdown]
+# ## Import Library
+
+# %%
+import easydiffraction as ed
+
+# %% [markdown]
+# ## Step 1: Define Project
+
+# %%
+# Create minimal project without name and description
+project = ed.Project()
+
+# %% [markdown]
+# ## Step 2: Define Structure
+
+# %%
+# Download CIF file from repository
+structure_path = ed.download_data(id=21, destination='data')
+
+# %%
+project.structures.add_from_cif_path(structure_path)
+
+# %%
+project.structures.show_names()
+
+# %%
+structure = project.structures['taurine']
+
+# %%
+# structure.show_as_cif()
+
+# %% [markdown]
+# ## Step 3: Define Experiment
+
+# %%
+data_path = ed.download_data(id=22, destination='data')
+
+# %%
+project.experiments.add_from_data_path(
+ name='senju',
+ data_path=data_path,
+ sample_form='single crystal',
+ beam_mode='time-of-flight',
+ radiation_probe='neutron',
+)
+
+# %%
+experiment = project.experiments['senju']
+
+# %%
+experiment.linked_crystal.id = 'taurine'
+experiment.linked_crystal.scale = 1.0
+
+# %%
+experiment.extinction.mosaicity = 1000.0
+experiment.extinction.radius = 100.0
+
+# %% [markdown]
+# ## Step 4: Perform Analysis
+
+# %%
+project.plot_meas_vs_calc(expt_name='senju')
+
+# %%
+experiment.linked_crystal.scale.free = True
+experiment.extinction.radius.free = True
+
+# %%
+# experiment.show_as_cif()
+
+# %%
+# Start refinement. All parameters, which have standard uncertainties
+# in the input CIF files, are refined by default.
+project.analysis.fit()
+
+# %%
+# Show fit results summary
+project.analysis.show_fit_results()
+
+# %%
+# experiment.show_as_cif()
+
+# %%
+project.experiments.show_names()
+
+# %%
+project.plot_meas_vs_calc(expt_name='senju')
+
+# %% [markdown]
+# ## Step 5: Show Project Summary
+
+# %%
+project.summary.show_report()
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-16.py b/docs/docs/tutorials/ed-16.py
new file mode 100644
index 00000000..e57f8449
--- /dev/null
+++ b/docs/docs/tutorials/ed-16.py
@@ -0,0 +1,259 @@
+# %% [markdown]
+# # Joint Refinement: Si, Bragg + PDF
+#
+# This example demonstrates a joint refinement of the Si crystal
+# structure combining Bragg diffraction and pair distribution function
+# (PDF) analysis. The Bragg experiment uses time-of-flight neutron
+# powder diffraction data from SEPD at Argonne, while the PDF
+# experiment uses data from NOMAD at SNS. A single shared Si structure
+# is refined simultaneously against both datasets.
+
+# %% [markdown]
+# ## Import Library
+
+# %%
+from easydiffraction import ExperimentFactory
+from easydiffraction import Project
+from easydiffraction import StructureFactory
+from easydiffraction import download_data
+
+# %% [markdown]
+# ## Define Structure
+#
+# A single Si structure is shared between the Bragg and PDF
+# experiments. Structural parameters refined against both datasets
+# simultaneously.
+#
+# #### Create Structure
+
+# %%
+structure = StructureFactory.from_scratch(name='si')
+
+# %% [markdown]
+# #### Set Space Group
+
+# %%
+structure.space_group.name_h_m = 'F d -3 m'
+structure.space_group.it_coordinate_system_code = '1'
+
+# %% [markdown]
+# #### Set Unit Cell
+
+# %%
+structure.cell.length_a = 5.42
+
+# %% [markdown]
+# #### Set Atom Sites
+
+# %%
+structure.atom_sites.create(
+ label='Si',
+ type_symbol='Si',
+ fract_x=0,
+ fract_y=0,
+ fract_z=0,
+ wyckoff_letter='a',
+ b_iso=0.2,
+)
+
+# %% [markdown]
+# ## Define Experiments
+#
+# Two experiments are defined: one for Bragg diffraction and one for
+# PDF analysis. Both are linked to the same Si structure.
+#
+# ### Experiment 1: Bragg (SEPD, TOF)
+#
+# #### Download Data
+
+# %%
+bragg_data_path = download_data(id=7, destination='data')
+
+# %% [markdown]
+# #### Create Experiment
+
+# %%
+bragg_expt = ExperimentFactory.from_data_path(
+ name='sepd', data_path=bragg_data_path, beam_mode='time-of-flight'
+)
+
+# %% [markdown]
+# #### Set Instrument
+
+# %%
+bragg_expt.instrument.setup_twotheta_bank = 144.845
+bragg_expt.instrument.calib_d_to_tof_offset = -9.2
+bragg_expt.instrument.calib_d_to_tof_linear = 7476.91
+bragg_expt.instrument.calib_d_to_tof_quad = -1.54
+
+# %% [markdown]
+# #### Set Peak Profile
+
+# %%
+bragg_expt.peak_profile_type = 'pseudo-voigt * ikeda-carpenter'
+bragg_expt.peak.broad_gauss_sigma_0 = 5.0
+bragg_expt.peak.broad_gauss_sigma_1 = 45.0
+bragg_expt.peak.broad_gauss_sigma_2 = 1.0
+bragg_expt.peak.broad_mix_beta_0 = 0.04221
+bragg_expt.peak.broad_mix_beta_1 = 0.00946
+bragg_expt.peak.asym_alpha_0 = 0.0
+bragg_expt.peak.asym_alpha_1 = 0.5971
+
+# %% [markdown]
+# #### Set Background
+
+# %%
+bragg_expt.background_type = 'line-segment'
+for x in range(0, 35000, 5000):
+ bragg_expt.background.create(id=str(x), x=x, y=200)
+
+# %% [markdown]
+# #### Set Linked Phases
+
+# %%
+bragg_expt.linked_phases.create(id='si', scale=13.0)
+
+# %% [markdown]
+# ### Experiment 2: PDF (NOMAD, TOF)
+#
+# #### Download Data
+
+# %%
+pdf_data_path = download_data(id=5, destination='data')
+
+# %% [markdown]
+# #### Create Experiment
+
+# %%
+pdf_expt = ExperimentFactory.from_data_path(
+ name='nomad',
+ data_path=pdf_data_path,
+ beam_mode='time-of-flight',
+ scattering_type='total',
+)
+
+# %% [markdown]
+# #### Set Peak Profile (PDF Parameters)
+
+# %%
+pdf_expt.peak.damp_q = 0.02
+pdf_expt.peak.broad_q = 0.02
+pdf_expt.peak.cutoff_q = 35.0
+pdf_expt.peak.sharp_delta_1 = 0.001
+pdf_expt.peak.sharp_delta_2 = 4.0
+pdf_expt.peak.damp_particle_diameter = 0
+
+# %% [markdown]
+# #### Set Linked Phases
+
+# %%
+pdf_expt.linked_phases.create(id='si', scale=1.0)
+
+# %% [markdown]
+# ## Define Project
+#
+# The project object manages the shared structure, both experiments,
+# and the analysis.
+#
+# #### Create Project
+
+# %%
+project = Project()
+
+# %% [markdown]
+# #### Add Structure
+
+# %%
+project.structures.add(structure)
+
+# %% [markdown]
+# #### Add Experiments
+
+# %%
+project.experiments.add(bragg_expt)
+project.experiments.add(pdf_expt)
+
+# %% [markdown]
+# ## Perform Analysis
+#
+# This section shows the joint analysis process. The calculator is
+# auto-resolved per experiment: CrysPy for Bragg, PDFfit for PDF.
+#
+# #### Set Fit Mode and Weights
+
+# %%
+project.analysis.fit_mode.mode = 'joint'
+project.analysis.joint_fit_experiments.create(id='sepd', weight=0.7)
+project.analysis.joint_fit_experiments.create(id='nomad', weight=0.3)
+
+# %% [markdown]
+# #### Set Minimizer
+
+# %%
+project.analysis.current_minimizer = 'lmfit'
+
+# %% [markdown]
+# #### Plot Measured vs Calculated (Before Fit)
+
+# %%
+project.plot_meas_vs_calc(expt_name='sepd', show_residual=False)
+
+# %%
+project.plot_meas_vs_calc(expt_name='nomad', show_residual=False)
+
+# %% [markdown]
+# #### Set Fitting Parameters
+#
+# Shared structural parameters are refined against both datasets
+# simultaneously.
+
+# %%
+structure.cell.length_a.free = True
+structure.atom_sites['Si'].b_iso.free = True
+
+# %% [markdown]
+# Bragg experiment parameters.
+
+# %%
+bragg_expt.linked_phases['si'].scale.free = True
+bragg_expt.instrument.calib_d_to_tof_offset.free = True
+bragg_expt.peak.broad_gauss_sigma_0.free = True
+bragg_expt.peak.broad_gauss_sigma_1.free = True
+bragg_expt.peak.broad_gauss_sigma_2.free = True
+for point in bragg_expt.background:
+ point.y.free = True
+
+# %% [markdown]
+# PDF experiment parameters.
+
+# %%
+pdf_expt.linked_phases['si'].scale.free = True
+pdf_expt.peak.damp_q.free = True
+pdf_expt.peak.broad_q.free = True
+pdf_expt.peak.sharp_delta_1.free = True
+pdf_expt.peak.sharp_delta_2.free = True
+
+# %% [markdown]
+# #### Show Free Parameters
+
+# %%
+project.analysis.show_free_params()
+
+# %% [markdown]
+# #### Run Fitting
+
+# %%
+project.analysis.fit()
+project.analysis.show_fit_results()
+
+# %% [markdown]
+# #### Plot Measured vs Calculated (After Fit)
+
+# %%
+project.plot_meas_vs_calc(expt_name='sepd', show_residual=False)
+
+# %%
+project.plot_meas_vs_calc(expt_name='nomad', show_residual=False)
+
+
+# %%
diff --git a/docs/docs/tutorials/ed-17.ipynb b/docs/docs/tutorials/ed-17.ipynb
new file mode 100644
index 00000000..a6500f67
--- /dev/null
+++ b/docs/docs/tutorials/ed-17.ipynb
@@ -0,0 +1,658 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "0",
+ "metadata": {},
+ "source": [
+ "# Structure Refinement: Co2SiO4, D20 (T-scan)\n",
+ "\n",
+ "This example demonstrates a Rietveld refinement of the Co2SiO4 crystal\n",
+ "structure using constant-wavelength neutron powder diffraction data\n",
+ "from D20 at ILL. A sequential refinement of the same structure against\n",
+ "a temperature scan is performed to show how to manage multiple\n",
+ "experiments in a project."
+ ]
+ },
+ {
+ "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\n",
+ "\n",
+ "The project object manages structures, experiments, and analysis."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project = ed.Project()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5",
+ "metadata": {},
+ "source": [
+ "Set output verbosity level to \"short\" to show only one-line status\n",
+ "messages during the analysis process."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.verbosity = 'short'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7",
+ "metadata": {},
+ "source": [
+ "## Step 2: Define Crystal 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": "8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.structures.create(name='cosio')\n",
+ "structure = project.structures['cosio']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9",
+ "metadata": {},
+ "source": [
+ "#### Set Space Group"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "10",
+ "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": "11",
+ "metadata": {},
+ "source": [
+ "#### Set Unit Cell"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "12",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "structure.cell.length_a = 10.31\n",
+ "structure.cell.length_b = 6.0\n",
+ "structure.cell.length_c = 4.79"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "13",
+ "metadata": {},
+ "source": [
+ "#### Set Atom Sites"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "14",
+ "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.3,\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.3,\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.34,\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.63,\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.59,\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.83,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "15",
+ "metadata": {},
+ "source": [
+ "## Step 3: Define Experiments\n",
+ "\n",
+ "This section shows how to add experiments, configure their parameters,\n",
+ "and link the structures defined above.\n",
+ "\n",
+ "#### Download Measured Data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "16",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "file_path = ed.download_data(id=27, destination='data')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "17",
+ "metadata": {},
+ "source": [
+ "#### Create Experiments and Set Temperature"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "18",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data_paths = ed.extract_data_paths_from_zip(file_path)\n",
+ "for i, data_path in enumerate(data_paths, start=1):\n",
+ " name = f'd20_{i}'\n",
+ " project.experiments.add_from_data_path(\n",
+ " name=name,\n",
+ " data_path=data_path,\n",
+ " )\n",
+ " expt = project.experiments[name]\n",
+ " expt.diffrn.ambient_temperature = ed.extract_metadata(\n",
+ " file_path=data_path,\n",
+ " pattern=r'^TEMP\\s+([0-9.]+)',\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "19",
+ "metadata": {},
+ "source": [
+ "#### Set Instrument"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "20",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for expt in project.experiments:\n",
+ " expt.instrument.setup_wavelength = 1.87\n",
+ " expt.instrument.calib_twotheta_offset = 0.29"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "21",
+ "metadata": {},
+ "source": [
+ "#### Set Peak Profile"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "22",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for expt in project.experiments:\n",
+ " expt.peak.broad_gauss_u = 0.24\n",
+ " expt.peak.broad_gauss_v = -0.53\n",
+ " expt.peak.broad_gauss_w = 0.38\n",
+ " expt.peak.broad_lorentz_y = 0.02"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "23",
+ "metadata": {},
+ "source": [
+ "#### Set Excluded Regions"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "24",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for expt in project.experiments:\n",
+ " expt.excluded_regions.create(id='1', start=0, end=8)\n",
+ " expt.excluded_regions.create(id='2', start=150, end=180)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "25",
+ "metadata": {},
+ "source": [
+ "#### Set Background"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "26",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for expt in project.experiments:\n",
+ " expt.background.create(id='1', x=8, y=609)\n",
+ " expt.background.create(id='2', x=9, y=581)\n",
+ " expt.background.create(id='3', x=10, y=563)\n",
+ " expt.background.create(id='4', x=11, y=540)\n",
+ " expt.background.create(id='5', x=12, y=520)\n",
+ " expt.background.create(id='6', x=15, y=507)\n",
+ " expt.background.create(id='7', x=25, y=463)\n",
+ " expt.background.create(id='8', x=30, y=434)\n",
+ " expt.background.create(id='9', x=50, y=451)\n",
+ " expt.background.create(id='10', x=70, y=431)\n",
+ " expt.background.create(id='11', x=90, y=414)\n",
+ " expt.background.create(id='12', x=110, y=361)\n",
+ " expt.background.create(id='13', x=130, y=292)\n",
+ " expt.background.create(id='14', x=150, y=241)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "27",
+ "metadata": {},
+ "source": [
+ "#### Set Linked Phases"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "28",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for expt in project.experiments:\n",
+ " expt.linked_phases.create(id='cosio', scale=1.2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "29",
+ "metadata": {},
+ "source": [
+ "## Step 4: Perform Analysis\n",
+ "\n",
+ "This section shows how to set free parameters, define constraints,\n",
+ "and run the refinement."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "30",
+ "metadata": {},
+ "source": [
+ "#### Set Free Parameters"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "31",
+ "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": "32",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for expt in project.experiments:\n",
+ " 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": "33",
+ "metadata": {},
+ "source": [
+ "#### Set Constraints\n",
+ "\n",
+ "Set aliases for parameters."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "34",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.analysis.aliases.create(\n",
+ " label='biso_Co1',\n",
+ " param_uid=structure.atom_sites['Co1'].b_iso.uid,\n",
+ ")\n",
+ "project.analysis.aliases.create(\n",
+ " label='biso_Co2',\n",
+ " param_uid=structure.atom_sites['Co2'].b_iso.uid,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "35",
+ "metadata": {},
+ "source": [
+ "Set constraints."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "36",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.analysis.constraints.create(\n",
+ " expression='biso_Co2 = biso_Co1',\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "37",
+ "metadata": {},
+ "source": [
+ "Apply constraints."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "38",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.analysis.apply_constraints()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "39",
+ "metadata": {},
+ "source": [
+ "#### Set Fit Mode"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "40",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.analysis.fit_mode.mode = 'single'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "41",
+ "metadata": {},
+ "source": [
+ "#### Run Fitting"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "42",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.analysis.fit()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "43",
+ "metadata": {},
+ "source": [
+ "#### Plot Measured vs Calculated"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "44",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "last_expt_name = project.experiments.names[-1]\n",
+ "project.plot_meas_vs_calc(expt_name=last_expt_name, show_residual=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "45",
+ "metadata": {},
+ "source": [
+ "#### Plot Parameter Evolution\n",
+ "\n",
+ "Define the quantity to use as the x-axis in the following plots."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "46",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "temperature = project.experiments[0].diffrn.ambient_temperature"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "47",
+ "metadata": {},
+ "source": [
+ "Plot unit cell parameters vs. temperature."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "48",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.plot_param_series(structure.cell.length_a, versus=temperature)\n",
+ "project.plot_param_series(structure.cell.length_b, versus=temperature)\n",
+ "project.plot_param_series(structure.cell.length_c, versus=temperature)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "49",
+ "metadata": {},
+ "source": [
+ "Plot isotropic displacement parameters vs. temperature."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "50",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.plot_param_series(structure.atom_sites['Co1'].b_iso, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['Si'].b_iso, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['O1'].b_iso, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['O2'].b_iso, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['O3'].b_iso, versus=temperature)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "51",
+ "metadata": {},
+ "source": [
+ "Plot selected fractional coordinates vs. temperature."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "52",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "project.plot_param_series(structure.atom_sites['Co2'].fract_x, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['Co2'].fract_z, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['O1'].fract_z, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['O2'].fract_z, versus=temperature)\n",
+ "project.plot_param_series(structure.atom_sites['O3'].fract_z, versus=temperature)"
+ ]
+ }
+ ],
+ "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-17.py b/docs/docs/tutorials/ed-17.py
new file mode 100644
index 00000000..af06031f
--- /dev/null
+++ b/docs/docs/tutorials/ed-17.py
@@ -0,0 +1,327 @@
+# %% [markdown]
+# # Structure Refinement: Co2SiO4, D20 (T-scan)
+#
+# This example demonstrates a Rietveld refinement of the Co2SiO4 crystal
+# structure using constant-wavelength neutron powder diffraction data
+# from D20 at ILL. A sequential refinement of the same structure against
+# a temperature scan is performed to show how to manage multiple
+# experiments in a project.
+
+# %% [markdown]
+# ## Import Library
+
+# %%
+import easydiffraction as ed
+
+# %% [markdown]
+# ## Step 1: Define Project
+#
+# The project object manages structures, experiments, and analysis.
+
+# %%
+project = ed.Project()
+
+# %% [markdown]
+# Set output verbosity level to "short" to show only one-line status
+# messages during the analysis process.
+
+# %%
+project.verbosity = 'short'
+
+# %% [markdown]
+# ## Step 2: Define Crystal Structure
+#
+# This section shows how to add structures and modify their
+# parameters.
+#
+# #### Create Structure
+
+# %%
+project.structures.create(name='cosio')
+structure = project.structures['cosio']
+
+# %% [markdown]
+# #### Set Space Group
+
+# %%
+structure.space_group.name_h_m = 'P n m a'
+structure.space_group.it_coordinate_system_code = 'abc'
+
+# %% [markdown]
+# #### Set Unit Cell
+
+# %%
+structure.cell.length_a = 10.31
+structure.cell.length_b = 6.0
+structure.cell.length_c = 4.79
+
+# %% [markdown]
+# #### Set Atom Sites
+
+# %%
+structure.atom_sites.create(
+ label='Co1',
+ type_symbol='Co',
+ fract_x=0,
+ fract_y=0,
+ fract_z=0,
+ wyckoff_letter='a',
+ b_iso=0.3,
+)
+structure.atom_sites.create(
+ label='Co2',
+ type_symbol='Co',
+ fract_x=0.279,
+ fract_y=0.25,
+ fract_z=0.985,
+ wyckoff_letter='c',
+ b_iso=0.3,
+)
+structure.atom_sites.create(
+ label='Si',
+ type_symbol='Si',
+ fract_x=0.094,
+ fract_y=0.25,
+ fract_z=0.429,
+ wyckoff_letter='c',
+ b_iso=0.34,
+)
+structure.atom_sites.create(
+ label='O1',
+ type_symbol='O',
+ fract_x=0.091,
+ fract_y=0.25,
+ fract_z=0.771,
+ wyckoff_letter='c',
+ b_iso=0.63,
+)
+structure.atom_sites.create(
+ label='O2',
+ type_symbol='O',
+ fract_x=0.448,
+ fract_y=0.25,
+ fract_z=0.217,
+ wyckoff_letter='c',
+ b_iso=0.59,
+)
+structure.atom_sites.create(
+ label='O3',
+ type_symbol='O',
+ fract_x=0.164,
+ fract_y=0.032,
+ fract_z=0.28,
+ wyckoff_letter='d',
+ b_iso=0.83,
+)
+
+# %% [markdown]
+# ## Step 3: Define Experiments
+#
+# This section shows how to add experiments, configure their parameters,
+# and link the structures defined above.
+#
+# #### Download Measured Data
+
+# %%
+file_path = ed.download_data(id=27, destination='data')
+
+# %% [markdown]
+# #### Create Experiments and Set Temperature
+
+# %%
+data_paths = ed.extract_data_paths_from_zip(file_path)
+for i, data_path in enumerate(data_paths, start=1):
+ name = f'd20_{i}'
+ project.experiments.add_from_data_path(
+ name=name,
+ data_path=data_path,
+ )
+ expt = project.experiments[name]
+ expt.diffrn.ambient_temperature = ed.extract_metadata(
+ file_path=data_path,
+ pattern=r'^TEMP\s+([0-9.]+)',
+ )
+
+# %% [markdown]
+# #### Set Instrument
+
+# %%
+for expt in project.experiments:
+ expt.instrument.setup_wavelength = 1.87
+ expt.instrument.calib_twotheta_offset = 0.29
+
+# %% [markdown]
+# #### Set Peak Profile
+
+# %%
+for expt in project.experiments:
+ expt.peak.broad_gauss_u = 0.24
+ expt.peak.broad_gauss_v = -0.53
+ expt.peak.broad_gauss_w = 0.38
+ expt.peak.broad_lorentz_y = 0.02
+
+# %% [markdown]
+# #### Set Excluded Regions
+
+# %%
+for expt in project.experiments:
+ expt.excluded_regions.create(id='1', start=0, end=8)
+ expt.excluded_regions.create(id='2', start=150, end=180)
+
+# %% [markdown]
+# #### Set Background
+
+# %%
+for expt in project.experiments:
+ expt.background.create(id='1', x=8, y=609)
+ expt.background.create(id='2', x=9, y=581)
+ expt.background.create(id='3', x=10, y=563)
+ expt.background.create(id='4', x=11, y=540)
+ expt.background.create(id='5', x=12, y=520)
+ expt.background.create(id='6', x=15, y=507)
+ expt.background.create(id='7', x=25, y=463)
+ expt.background.create(id='8', x=30, y=434)
+ expt.background.create(id='9', x=50, y=451)
+ expt.background.create(id='10', x=70, y=431)
+ expt.background.create(id='11', x=90, y=414)
+ expt.background.create(id='12', x=110, y=361)
+ expt.background.create(id='13', x=130, y=292)
+ expt.background.create(id='14', x=150, y=241)
+
+# %% [markdown]
+# #### Set Linked Phases
+
+# %%
+for expt in project.experiments:
+ expt.linked_phases.create(id='cosio', scale=1.2)
+
+# %% [markdown]
+# ## Step 4: Perform Analysis
+#
+# This section shows how to set free parameters, define constraints,
+# and run the refinement.
+
+# %% [markdown]
+# #### Set Free Parameters
+
+# %%
+structure.cell.length_a.free = True
+structure.cell.length_b.free = True
+structure.cell.length_c.free = True
+
+structure.atom_sites['Co2'].fract_x.free = True
+structure.atom_sites['Co2'].fract_z.free = True
+structure.atom_sites['Si'].fract_x.free = True
+structure.atom_sites['Si'].fract_z.free = True
+structure.atom_sites['O1'].fract_x.free = True
+structure.atom_sites['O1'].fract_z.free = True
+structure.atom_sites['O2'].fract_x.free = True
+structure.atom_sites['O2'].fract_z.free = True
+structure.atom_sites['O3'].fract_x.free = True
+structure.atom_sites['O3'].fract_y.free = True
+structure.atom_sites['O3'].fract_z.free = True
+
+structure.atom_sites['Co1'].b_iso.free = True
+structure.atom_sites['Co2'].b_iso.free = True
+structure.atom_sites['Si'].b_iso.free = True
+structure.atom_sites['O1'].b_iso.free = True
+structure.atom_sites['O2'].b_iso.free = True
+structure.atom_sites['O3'].b_iso.free = True
+
+# %%
+for expt in project.experiments:
+ expt.linked_phases['cosio'].scale.free = True
+
+ expt.instrument.calib_twotheta_offset.free = True
+
+ expt.peak.broad_gauss_u.free = True
+ expt.peak.broad_gauss_v.free = True
+ expt.peak.broad_gauss_w.free = True
+ expt.peak.broad_lorentz_y.free = True
+
+ for point in expt.background:
+ point.y.free = True
+
+# %% [markdown]
+# #### Set Constraints
+#
+# Set aliases for parameters.
+
+# %%
+project.analysis.aliases.create(
+ label='biso_Co1',
+ param_uid=structure.atom_sites['Co1'].b_iso.uid,
+)
+project.analysis.aliases.create(
+ label='biso_Co2',
+ param_uid=structure.atom_sites['Co2'].b_iso.uid,
+)
+
+# %% [markdown]
+# Set constraints.
+
+# %%
+project.analysis.constraints.create(
+ expression='biso_Co2 = biso_Co1',
+)
+
+# %% [markdown]
+# Apply constraints.
+
+# %%
+project.analysis.apply_constraints()
+
+# %% [markdown]
+# #### Set Fit Mode
+
+# %%
+project.analysis.fit_mode.mode = 'single'
+
+# %% [markdown]
+# #### Run Fitting
+
+# %%
+project.analysis.fit()
+
+# %% [markdown]
+# #### Plot Measured vs Calculated
+
+# %%
+last_expt_name = project.experiments.names[-1]
+project.plot_meas_vs_calc(expt_name=last_expt_name, show_residual=True)
+
+# %% [markdown]
+# #### Plot Parameter Evolution
+#
+# Define the quantity to use as the x-axis in the following plots.
+
+# %%
+temperature = project.experiments[0].diffrn.ambient_temperature
+
+# %% [markdown]
+# Plot unit cell parameters vs. temperature.
+
+# %%
+project.plot_param_series(structure.cell.length_a, versus=temperature)
+project.plot_param_series(structure.cell.length_b, versus=temperature)
+project.plot_param_series(structure.cell.length_c, versus=temperature)
+
+# %% [markdown]
+# Plot isotropic displacement parameters vs. temperature.
+
+# %%
+project.plot_param_series(structure.atom_sites['Co1'].b_iso, versus=temperature)
+project.plot_param_series(structure.atom_sites['Si'].b_iso, versus=temperature)
+project.plot_param_series(structure.atom_sites['O1'].b_iso, versus=temperature)
+project.plot_param_series(structure.atom_sites['O2'].b_iso, versus=temperature)
+project.plot_param_series(structure.atom_sites['O3'].b_iso, versus=temperature)
+
+# %% [markdown]
+# Plot selected fractional coordinates vs. temperature.
+
+# %%
+project.plot_param_series(structure.atom_sites['Co2'].fract_x, versus=temperature)
+project.plot_param_series(structure.atom_sites['Co2'].fract_z, versus=temperature)
+project.plot_param_series(structure.atom_sites['O1'].fract_z, versus=temperature)
+project.plot_param_series(structure.atom_sites['O2'].fract_z, versus=temperature)
+project.plot_param_series(structure.atom_sites['O3'].fract_z, versus=temperature)
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/tutorials/ed-2.py b/docs/docs/tutorials/ed-2.py
similarity index 69%
rename from tutorials/ed-2.py
rename to docs/docs/tutorials/ed-2.py
index 4390b65b..3c8d033e 100644
--- a/tutorials/ed-2.py
+++ b/docs/docs/tutorials/ed-2.py
@@ -2,9 +2,9 @@
# # Structure Refinement: LBCO, HRPT
#
# This minimalistic example is designed to show how Rietveld refinement
-# of a crystal structure can be performed when both the sample model and
-# experiment are defined directly in code. Only the experimentally
-# measured data is loaded from an external file.
+# can be performed when both the crystal structure and experiment are
+# defined directly in code. Only the experimentally measured data is
+# loaded from an external file.
#
# For this example, constant-wavelength neutron powder diffraction data
# for La0.5Ba0.5CoO3 from HRPT at PSI is used.
@@ -33,23 +33,23 @@
project = ed.Project()
# %% [markdown]
-# ## Step 2: Define Sample Model
+# ## Step 2: Define Structure
# %%
-project.sample_models.add(name='lbco')
+project.structures.create(name='lbco')
# %%
-sample_model = project.sample_models['lbco']
+structure = project.structures['lbco']
# %%
-sample_model.space_group.name_h_m = 'P m -3 m'
-sample_model.space_group.it_coordinate_system_code = '1'
+structure.space_group.name_h_m = 'P m -3 m'
+structure.space_group.it_coordinate_system_code = '1'
# %%
-sample_model.cell.length_a = 3.88
+structure.cell.length_a = 3.88
# %%
-sample_model.atom_sites.add(
+structure.atom_sites.create(
label='La',
type_symbol='La',
fract_x=0,
@@ -59,7 +59,7 @@
b_iso=0.5,
occupancy=0.5,
)
-sample_model.atom_sites.add(
+structure.atom_sites.create(
label='Ba',
type_symbol='Ba',
fract_x=0,
@@ -69,7 +69,7 @@
b_iso=0.5,
occupancy=0.5,
)
-sample_model.atom_sites.add(
+structure.atom_sites.create(
label='Co',
type_symbol='Co',
fract_x=0.5,
@@ -78,7 +78,7 @@
wyckoff_letter='b',
b_iso=0.5,
)
-sample_model.atom_sites.add(
+structure.atom_sites.create(
label='O',
type_symbol='O',
fract_x=0,
@@ -95,7 +95,7 @@
data_path = ed.download_data(id=3, destination='data')
# %%
-project.experiments.add(
+project.experiments.add_from_data_path(
name='hrpt',
data_path=data_path,
sample_form='powder',
@@ -117,29 +117,29 @@
experiment.peak.broad_lorentz_y = 0.1
# %%
-experiment.background.add(id='1', x=10, y=170)
-experiment.background.add(id='2', x=30, y=170)
-experiment.background.add(id='3', x=50, y=170)
-experiment.background.add(id='4', x=110, y=170)
-experiment.background.add(id='5', x=165, y=170)
+experiment.background.create(id='1', x=10, y=170)
+experiment.background.create(id='2', x=30, y=170)
+experiment.background.create(id='3', x=50, y=170)
+experiment.background.create(id='4', x=110, y=170)
+experiment.background.create(id='5', x=165, y=170)
# %%
-experiment.excluded_regions.add(id='1', start=0, end=5)
-experiment.excluded_regions.add(id='2', start=165, end=180)
+experiment.excluded_regions.create(id='1', start=0, end=5)
+experiment.excluded_regions.create(id='2', start=165, end=180)
# %%
-experiment.linked_phases.add(id='lbco', scale=10.0)
+experiment.linked_phases.create(id='lbco', scale=10.0)
# %% [markdown]
# ## Step 4: Perform Analysis
# %%
-sample_model.cell.length_a.free = True
+structure.cell.length_a.free = True
-sample_model.atom_sites['La'].b_iso.free = True
-sample_model.atom_sites['Ba'].b_iso.free = True
-sample_model.atom_sites['Co'].b_iso.free = True
-sample_model.atom_sites['O'].b_iso.free = True
+structure.atom_sites['La'].b_iso.free = True
+structure.atom_sites['Ba'].b_iso.free = True
+structure.atom_sites['Co'].b_iso.free = True
+structure.atom_sites['O'].b_iso.free = True
# %%
experiment.instrument.calib_twotheta_offset.free = True
diff --git a/docs/docs/tutorials/ed-3.ipynb b/docs/docs/tutorials/ed-3.ipynb
new file mode 100644
index 00000000..5104fcab
--- /dev/null
+++ b/docs/docs/tutorials/ed-3.ipynb
@@ -0,0 +1,1804 @@
+{
+ "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(expression='biso_Ba = 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",
+ " expression='occ_Ba = 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/tutorials/ed-3.py b/docs/docs/tutorials/ed-3.py
similarity index 78%
rename from tutorials/ed-3.py
rename to docs/docs/tutorials/ed-3.py
index c0aefa1b..23b60d88 100644
--- a/tutorials/ed-3.py
+++ b/docs/docs/tutorials/ed-3.py
@@ -8,12 +8,13 @@
#
# It is intended for users with minimal programming experience who want
# to learn how to perform standard crystal structure fitting using
-# diffraction data. This script covers creating a project, adding sample
-# models and experiments, performing analysis, and refining parameters.
+# diffraction data. This script covers creating a project, adding
+# crystal structures and experiments, performing analysis, and refining
+# parameters.
#
# Only a single import of `easydiffraction` is required, and all
# operations are performed through high-level components of the
-# `project` object, such as `project.sample_models`,
+# `project` object, such as `project.structures`,
# `project.experiments`, and `project.analysis`. The `project` object is
# the main container for all information.
@@ -84,26 +85,27 @@
# project.plotter.engine = 'plotly'
# %% [markdown]
-# ## Step 2: Define Sample Model
+# ## Step 2: Define Structure
#
-# This section shows how to add sample models and modify their
+# This section shows how to add structures and modify their
# parameters.
# %% [markdown]
-# #### Add Sample Model
+# #### Add Structure
# %%
-project.sample_models.add(name='lbco')
+project.structures.create(name='lbco')
# %% [markdown]
-# #### Show Defined Sample Models
+# #### Show Defined Structures
#
-# Show the names of the models added. These names are used to access the
-# model using the syntax: `project.sample_models['model_name']`. All
-# model parameters can be accessed via the `project` object.
+# Show the names of the crystal structures added. These names are used
+# to access the structure using the syntax:
+# `project.structures[name]`. All structure parameters can be accessed
+# via the `project` object.
# %%
-project.sample_models.show_names()
+project.structures.show_names()
# %% [markdown]
# #### Set Space Group
@@ -111,8 +113,8 @@
# Modify the default space group parameters.
# %%
-project.sample_models['lbco'].space_group.name_h_m = 'P m -3 m'
-project.sample_models['lbco'].space_group.it_coordinate_system_code = '1'
+project.structures['lbco'].space_group.name_h_m = 'P m -3 m'
+project.structures['lbco'].space_group.it_coordinate_system_code = '1'
# %% [markdown]
# #### Set Unit Cell
@@ -120,15 +122,15 @@
# Modify the default unit cell parameters.
# %%
-project.sample_models['lbco'].cell.length_a = 3.88
+project.structures['lbco'].cell.length_a = 3.88
# %% [markdown]
# #### Set Atom Sites
#
-# Add atom sites to the sample model.
+# Add atom sites to the structure.
# %%
-project.sample_models['lbco'].atom_sites.add(
+project.structures['lbco'].atom_sites.create(
label='La',
type_symbol='La',
fract_x=0,
@@ -138,7 +140,7 @@
b_iso=0.5,
occupancy=0.5,
)
-project.sample_models['lbco'].atom_sites.add(
+project.structures['lbco'].atom_sites.create(
label='Ba',
type_symbol='Ba',
fract_x=0,
@@ -148,7 +150,7 @@
b_iso=0.5,
occupancy=0.5,
)
-project.sample_models['lbco'].atom_sites.add(
+project.structures['lbco'].atom_sites.create(
label='Co',
type_symbol='Co',
fract_x=0.5,
@@ -157,7 +159,7 @@
wyckoff_letter='b',
b_iso=0.5,
)
-project.sample_models['lbco'].atom_sites.add(
+project.structures['lbco'].atom_sites.create(
label='O',
type_symbol='O',
fract_x=0,
@@ -168,21 +170,21 @@
)
# %% [markdown]
-# #### Show Sample Model as CIF
+# #### Show Structure as CIF
# %%
-project.sample_models['lbco'].show_as_cif()
+project.structures['lbco'].show_as_cif()
# %% [markdown]
-# #### Show Sample Model Structure
+# #### Show Structure Structure
# %%
-project.sample_models['lbco'].show_structure()
+project.structures['lbco'].show()
# %% [markdown]
# #### Save Project State
#
-# Save the project state after adding the sample model. This ensures
+# Save the project state after adding the structure. This ensures
# that all changes are stored and can be accessed later. The project
# state is saved in the directory specified during project creation.
@@ -193,7 +195,7 @@
# ## Step 3: Define Experiment
#
# This section shows how to add experiments, configure their parameters,
-# and link the sample models defined in the previous step.
+# and link the structures defined in the previous step.
# %% [markdown]
# #### Download Measured Data
@@ -207,7 +209,7 @@
# #### Add Diffraction Experiment
# %%
-project.experiments.add(
+project.experiments.add_from_data_path(
name='hrpt',
data_path=data_path,
sample_form='powder',
@@ -291,11 +293,11 @@
# Add background points.
# %%
-project.experiments['hrpt'].background.add(id='10', x=10, y=170)
-project.experiments['hrpt'].background.add(id='30', x=30, y=170)
-project.experiments['hrpt'].background.add(id='50', x=50, y=170)
-project.experiments['hrpt'].background.add(id='110', x=110, y=170)
-project.experiments['hrpt'].background.add(id='165', x=165, y=170)
+project.experiments['hrpt'].background.create(id='10', x=10, y=170)
+project.experiments['hrpt'].background.create(id='30', x=30, y=170)
+project.experiments['hrpt'].background.create(id='50', x=50, y=170)
+project.experiments['hrpt'].background.create(id='110', x=110, y=170)
+project.experiments['hrpt'].background.create(id='165', x=165, y=170)
# %% [markdown]
# Show current background points.
@@ -306,10 +308,10 @@
# %% [markdown]
# #### Set Linked Phases
#
-# Link the sample model defined in the previous step to the experiment.
+# Link the structure defined in the previous step to the experiment.
# %%
-project.experiments['hrpt'].linked_phases.add(id='lbco', scale=10.0)
+project.experiments['hrpt'].linked_phases.create(id='lbco', scale=10.0)
# %% [markdown]
# #### Show Experiment as CIF
@@ -331,22 +333,22 @@
#
# #### Set Calculator
#
-# Show supported calculation engines.
+# Show supported calculation engines for this experiment.
# %%
-project.analysis.show_supported_calculators()
+project.experiments['hrpt'].show_supported_calculator_types()
# %% [markdown]
-# Show current calculation engine.
+# Show current calculation engine for this experiment.
# %%
-project.analysis.show_current_calculator()
+project.experiments['hrpt'].show_current_calculator_type()
# %% [markdown]
# Select the desired calculation engine.
# %%
-project.analysis.current_calculator = 'cryspy'
+project.experiments['hrpt'].calculator_type = 'cryspy'
# %% [markdown]
# #### Show Calculated Data
@@ -395,19 +397,19 @@
# Show supported fit modes.
# %%
-project.analysis.show_available_fit_modes()
+project.analysis.show_supported_fit_mode_types()
# %% [markdown]
# Show current fit mode.
# %%
-project.analysis.show_current_fit_mode()
+project.analysis.show_current_fit_mode_type()
# %% [markdown]
# Select desired fit mode.
# %%
-project.analysis.fit_mode = 'single'
+project.analysis.fit_mode.mode = 'single'
# %% [markdown]
# #### Set Minimizer
@@ -427,15 +429,15 @@
# Select desired fitting engine.
# %%
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+project.analysis.current_minimizer = 'lmfit'
# %% [markdown]
# ### Perform Fit 1/5
#
-# Set sample model parameters to be refined.
+# Set structure parameters to be refined.
# %%
-project.sample_models['lbco'].cell.length_a.free = True
+project.structures['lbco'].cell.length_a.free = True
# %% [markdown]
# Set experiment parameters to be refined.
@@ -522,10 +524,10 @@
# Set more parameters to be refined.
# %%
-project.sample_models['lbco'].atom_sites['La'].b_iso.free = True
-project.sample_models['lbco'].atom_sites['Ba'].b_iso.free = True
-project.sample_models['lbco'].atom_sites['Co'].b_iso.free = True
-project.sample_models['lbco'].atom_sites['O'].b_iso.free = True
+project.structures['lbco'].atom_sites['La'].b_iso.free = True
+project.structures['lbco'].atom_sites['Ba'].b_iso.free = True
+project.structures['lbco'].atom_sites['Co'].b_iso.free = True
+project.structures['lbco'].atom_sites['O'].b_iso.free = True
# %% [markdown]
# Show free parameters after selection.
@@ -563,20 +565,20 @@
# Set aliases for parameters.
# %%
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='biso_La',
- param_uid=project.sample_models['lbco'].atom_sites['La'].b_iso.uid,
+ param_uid=project.structures['lbco'].atom_sites['La'].b_iso.uid,
)
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='biso_Ba',
- param_uid=project.sample_models['lbco'].atom_sites['Ba'].b_iso.uid,
+ param_uid=project.structures['lbco'].atom_sites['Ba'].b_iso.uid,
)
# %% [markdown]
# Set constraints.
# %%
-project.analysis.constraints.add(lhs_alias='biso_Ba', rhs_expr='biso_La')
+project.analysis.constraints.create(expression='biso_Ba = biso_La')
# %% [markdown]
# Show defined constraints.
@@ -632,22 +634,21 @@
# Set more aliases for parameters.
# %%
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='occ_La',
- param_uid=project.sample_models['lbco'].atom_sites['La'].occupancy.uid,
+ param_uid=project.structures['lbco'].atom_sites['La'].occupancy.uid,
)
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='occ_Ba',
- param_uid=project.sample_models['lbco'].atom_sites['Ba'].occupancy.uid,
+ param_uid=project.structures['lbco'].atom_sites['Ba'].occupancy.uid,
)
# %% [markdown]
# Set more constraints.
# %%
-project.analysis.constraints.add(
- lhs_alias='occ_Ba',
- rhs_expr='1 - occ_La',
+project.analysis.constraints.create(
+ expression='occ_Ba = 1 - occ_La',
)
# %% [markdown]
@@ -663,10 +664,10 @@
project.analysis.apply_constraints()
# %% [markdown]
-# Set sample model parameters to be refined.
+# Set structure parameters to be refined.
# %%
-project.sample_models['lbco'].atom_sites['La'].occupancy.free = True
+project.structures['lbco'].atom_sites['La'].occupancy.free = True
# %% [markdown]
# Show free parameters after selection.
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/tutorials/ed-4.py b/docs/docs/tutorials/ed-4.py
similarity index 78%
rename from tutorials/ed-4.py
rename to docs/docs/tutorials/ed-4.py
index ce857a2a..3275deab 100644
--- a/tutorials/ed-4.py
+++ b/docs/docs/tutorials/ed-4.py
@@ -2,7 +2,7 @@
# # Structure Refinement: PbSO4, NPD + XRD
#
# This example demonstrates a more advanced use of the EasyDiffraction
-# library by explicitly creating and configuring sample models and
+# library by explicitly creating and configuring structures and
# experiments before adding them to a project. It could be more suitable
# for users who are interested in creating custom workflows. This
# tutorial provides minimal explanation and is intended for users
@@ -17,39 +17,39 @@
# %%
from easydiffraction import ExperimentFactory
from easydiffraction import Project
-from easydiffraction import SampleModelFactory
+from easydiffraction import StructureFactory
from easydiffraction import download_data
# %% [markdown]
-# ## Define Sample Model
+# ## Define Structure
#
-# This section shows how to add sample models and modify their
+# This section shows how to add structures and modify their
# parameters.
#
-# #### Create Sample Model
+# #### Create Structure
# %%
-model = SampleModelFactory.create(name='pbso4')
+structure = StructureFactory.from_scratch(name='pbso4')
# %% [markdown]
# #### Set Space Group
# %%
-model.space_group.name_h_m = 'P n m a'
+structure.space_group.name_h_m = 'P n m a'
# %% [markdown]
# #### Set Unit Cell
# %%
-model.cell.length_a = 8.47
-model.cell.length_b = 5.39
-model.cell.length_c = 6.95
+structure.cell.length_a = 8.47
+structure.cell.length_b = 5.39
+structure.cell.length_c = 6.95
# %% [markdown]
# #### Set Atom Sites
# %%
-model.atom_sites.add(
+structure.atom_sites.create(
label='Pb',
type_symbol='Pb',
fract_x=0.1876,
@@ -58,7 +58,7 @@
wyckoff_letter='c',
b_iso=1.37,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='S',
type_symbol='S',
fract_x=0.0654,
@@ -67,7 +67,7 @@
wyckoff_letter='c',
b_iso=0.3777,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='O1',
type_symbol='O',
fract_x=0.9082,
@@ -76,7 +76,7 @@
wyckoff_letter='c',
b_iso=1.9764,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='O2',
type_symbol='O',
fract_x=0.1935,
@@ -85,7 +85,7 @@
wyckoff_letter='c',
b_iso=1.4456,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='O3',
type_symbol='O',
fract_x=0.0811,
@@ -100,7 +100,7 @@
# ## Define Experiments
#
# This section shows how to add experiments, configure their parameters,
-# and link the sample models defined in the previous step.
+# and link the structures defined in the previous step.
#
# ### Experiment 1: npd
#
@@ -113,7 +113,7 @@
# #### Create Experiment
# %%
-expt1 = ExperimentFactory.create(
+expt1 = ExperimentFactory.from_data_path(
name='npd',
data_path=data_path1,
radiation_probe='neutron',
@@ -159,13 +159,13 @@
('7', 120.0, 244.4525),
('8', 153.0, 226.0595),
]:
- expt1.background.add(id=id, x=x, y=y)
+ expt1.background.create(id=id, x=x, y=y)
# %% [markdown]
# #### Set Linked Phases
# %%
-expt1.linked_phases.add(id='pbso4', scale=1.5)
+expt1.linked_phases.create(id='pbso4', scale=1.5)
# %% [markdown]
# ### Experiment 2: xrd
@@ -179,7 +179,7 @@
# #### Create Experiment
# %%
-expt2 = ExperimentFactory.create(
+expt2 = ExperimentFactory.from_data_path(
name='xrd',
data_path=data_path2,
radiation_probe='xray',
@@ -209,7 +209,7 @@
# Select background type.
# %%
-expt2.background_type = 'chebyshev polynomial'
+expt2.background_type = 'chebyshev'
# %% [markdown]
# Add background points.
@@ -223,18 +223,18 @@
('5', 4, 54.552),
('6', 5, -20.661),
]:
- expt2.background.add(id=id, order=x, coef=y)
+ expt2.background.create(id=id, order=x, coef=y)
# %% [markdown]
# #### Set Linked Phases
# %%
-expt2.linked_phases.add(id='pbso4', scale=0.001)
+expt2.linked_phases.create(id='pbso4', scale=0.001)
# %% [markdown]
# ## Define Project
#
-# The project object is used to manage sample models, experiments, and
+# The project object is used to manage structures, experiments, and
# analysis.
#
# #### Create Project
@@ -243,17 +243,17 @@
project = Project()
# %% [markdown]
-# #### Add Sample Model
+# #### Add Structure
# %%
-project.sample_models.add(sample_model=model)
+project.structures.add(structure)
# %% [markdown]
# #### Add Experiments
# %%
-project.experiments.add(experiment=expt1)
-project.experiments.add(experiment=expt2)
+project.experiments.add(expt1)
+project.experiments.add(expt2)
# %% [markdown]
# ## Perform Analysis
@@ -261,32 +261,26 @@
# This section outlines the analysis process, including how to configure
# calculation and fitting engines.
#
-# #### Set Calculator
-
-# %%
-project.analysis.current_calculator = 'cryspy'
-
-# %% [markdown]
# #### Set Fit Mode
# %%
-project.analysis.fit_mode = 'joint'
+project.analysis.fit_mode.mode = 'joint'
# %% [markdown]
# #### Set Minimizer
# %%
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+project.analysis.current_minimizer = 'lmfit'
# %% [markdown]
# #### Set Fitting Parameters
#
-# Set sample model parameters to be optimized.
+# Set structure parameters to be optimized.
# %%
-model.cell.length_a.free = True
-model.cell.length_b.free = True
-model.cell.length_c.free = True
+structure.cell.length_a.free = True
+structure.cell.length_b.free = True
+structure.cell.length_c.free = True
# %% [markdown]
# Set experiment parameters to be optimized.
diff --git a/docs/docs/tutorials/ed-5.ipynb b/docs/docs/tutorials/ed-5.ipynb
new file mode 100644
index 00000000..903fe2ae
--- /dev/null
+++ b/docs/docs/tutorials/ed-5.ipynb
@@ -0,0 +1,637 @@
+{
+ "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",
+ " expression='biso_Co2 = 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/tutorials/ed-5.py b/docs/docs/tutorials/ed-5.py
similarity index 58%
rename from tutorials/ed-5.py
rename to docs/docs/tutorials/ed-5.py
index 0e46baa8..74e1d887 100644
--- a/tutorials/ed-5.py
+++ b/docs/docs/tutorials/ed-5.py
@@ -11,40 +11,40 @@
# %%
from easydiffraction import ExperimentFactory
from easydiffraction import Project
-from easydiffraction import SampleModelFactory
+from easydiffraction import StructureFactory
from easydiffraction import download_data
# %% [markdown]
-# ## Define Sample Model
+# ## Define Structure
#
-# This section shows how to add sample models and modify their
+# This section shows how to add structures and modify their
# parameters.
#
-# #### Create Sample Model
+# #### Create Structure
# %%
-model = SampleModelFactory.create(name='cosio')
+structure = StructureFactory.from_scratch(name='cosio')
# %% [markdown]
# #### Set Space Group
# %%
-model.space_group.name_h_m = 'P n m a'
-model.space_group.it_coordinate_system_code = 'abc'
+structure.space_group.name_h_m = 'P n m a'
+structure.space_group.it_coordinate_system_code = 'abc'
# %% [markdown]
# #### Set Unit Cell
# %%
-model.cell.length_a = 10.3
-model.cell.length_b = 6.0
-model.cell.length_c = 4.8
+structure.cell.length_a = 10.3
+structure.cell.length_b = 6.0
+structure.cell.length_c = 4.8
# %% [markdown]
# #### Set Atom Sites
# %%
-model.atom_sites.add(
+structure.atom_sites.create(
label='Co1',
type_symbol='Co',
fract_x=0,
@@ -53,7 +53,7 @@
wyckoff_letter='a',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='Co2',
type_symbol='Co',
fract_x=0.279,
@@ -62,7 +62,7 @@
wyckoff_letter='c',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0.094,
@@ -71,7 +71,7 @@
wyckoff_letter='c',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='O1',
type_symbol='O',
fract_x=0.091,
@@ -80,7 +80,7 @@
wyckoff_letter='c',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='O2',
type_symbol='O',
fract_x=0.448,
@@ -89,7 +89,7 @@
wyckoff_letter='c',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='O3',
type_symbol='O',
fract_x=0.164,
@@ -103,7 +103,7 @@
# ## Define Experiment
#
# This section shows how to add experiments, configure their parameters,
-# and link the sample models defined in the previous step.
+# and link the structures defined in the previous step.
#
# #### Download Measured Data
@@ -114,7 +114,7 @@
# #### Create Experiment
# %%
-expt = ExperimentFactory.create(name='d20', data_path=data_path)
+expt = ExperimentFactory.from_data_path(name='d20', data_path=data_path)
# %% [markdown]
# #### Set Instrument
@@ -135,31 +135,31 @@
# #### Set Background
# %%
-expt.background.add(id='1', x=8, y=500)
-expt.background.add(id='2', x=9, y=500)
-expt.background.add(id='3', x=10, y=500)
-expt.background.add(id='4', x=11, y=500)
-expt.background.add(id='5', x=12, y=500)
-expt.background.add(id='6', x=15, y=500)
-expt.background.add(id='7', x=25, y=500)
-expt.background.add(id='8', x=30, y=500)
-expt.background.add(id='9', x=50, y=500)
-expt.background.add(id='10', x=70, y=500)
-expt.background.add(id='11', x=90, y=500)
-expt.background.add(id='12', x=110, y=500)
-expt.background.add(id='13', x=130, y=500)
-expt.background.add(id='14', x=150, y=500)
+expt.background.create(id='1', x=8, y=500)
+expt.background.create(id='2', x=9, y=500)
+expt.background.create(id='3', x=10, y=500)
+expt.background.create(id='4', x=11, y=500)
+expt.background.create(id='5', x=12, y=500)
+expt.background.create(id='6', x=15, y=500)
+expt.background.create(id='7', x=25, y=500)
+expt.background.create(id='8', x=30, y=500)
+expt.background.create(id='9', x=50, y=500)
+expt.background.create(id='10', x=70, y=500)
+expt.background.create(id='11', x=90, y=500)
+expt.background.create(id='12', x=110, y=500)
+expt.background.create(id='13', x=130, y=500)
+expt.background.create(id='14', x=150, y=500)
# %% [markdown]
# #### Set Linked Phases
# %%
-expt.linked_phases.add(id='cosio', scale=1.0)
+expt.linked_phases.create(id='cosio', scale=1.0)
# %% [markdown]
# ## Define Project
#
-# The project object is used to manage the sample model, experiment, and
+# The project object is used to manage the structure, experiment, and
# analysis.
#
# #### Create Project
@@ -176,16 +176,16 @@
# project.plotter.engine = 'plotly'
# %% [markdown]
-# #### Add Sample Model
+# #### Add Structure
# %%
-project.sample_models.add(sample_model=model)
+project.structures.add(structure)
# %% [markdown]
# #### Add Experiment
# %%
-project.experiments.add(experiment=expt)
+project.experiments.add(expt)
# %% [markdown]
# ## Perform Analysis
@@ -193,16 +193,10 @@
# This section shows the analysis process, including how to set up
# calculation and fitting engines.
#
-# #### Set Calculator
-
-# %%
-project.analysis.current_calculator = 'cryspy'
-
-# %% [markdown]
# #### Set Minimizer
# %%
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+project.analysis.current_minimizer = 'lmfit'
# %% [markdown]
# #### Plot Measured vs Calculated
@@ -217,28 +211,28 @@
# #### Set Free Parameters
# %%
-model.cell.length_a.free = True
-model.cell.length_b.free = True
-model.cell.length_c.free = True
-
-model.atom_sites['Co2'].fract_x.free = True
-model.atom_sites['Co2'].fract_z.free = True
-model.atom_sites['Si'].fract_x.free = True
-model.atom_sites['Si'].fract_z.free = True
-model.atom_sites['O1'].fract_x.free = True
-model.atom_sites['O1'].fract_z.free = True
-model.atom_sites['O2'].fract_x.free = True
-model.atom_sites['O2'].fract_z.free = True
-model.atom_sites['O3'].fract_x.free = True
-model.atom_sites['O3'].fract_y.free = True
-model.atom_sites['O3'].fract_z.free = True
-
-model.atom_sites['Co1'].b_iso.free = True
-model.atom_sites['Co2'].b_iso.free = True
-model.atom_sites['Si'].b_iso.free = True
-model.atom_sites['O1'].b_iso.free = True
-model.atom_sites['O2'].b_iso.free = True
-model.atom_sites['O3'].b_iso.free = True
+structure.cell.length_a.free = True
+structure.cell.length_b.free = True
+structure.cell.length_c.free = True
+
+structure.atom_sites['Co2'].fract_x.free = True
+structure.atom_sites['Co2'].fract_z.free = True
+structure.atom_sites['Si'].fract_x.free = True
+structure.atom_sites['Si'].fract_z.free = True
+structure.atom_sites['O1'].fract_x.free = True
+structure.atom_sites['O1'].fract_z.free = True
+structure.atom_sites['O2'].fract_x.free = True
+structure.atom_sites['O2'].fract_z.free = True
+structure.atom_sites['O3'].fract_x.free = True
+structure.atom_sites['O3'].fract_y.free = True
+structure.atom_sites['O3'].fract_z.free = True
+
+structure.atom_sites['Co1'].b_iso.free = True
+structure.atom_sites['Co2'].b_iso.free = True
+structure.atom_sites['Si'].b_iso.free = True
+structure.atom_sites['O1'].b_iso.free = True
+structure.atom_sites['O2'].b_iso.free = True
+structure.atom_sites['O3'].b_iso.free = True
# %%
expt.linked_phases['cosio'].scale.free = True
@@ -259,22 +253,21 @@
# Set aliases for parameters.
# %%
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='biso_Co1',
- param_uid=project.sample_models['cosio'].atom_sites['Co1'].b_iso.uid,
+ param_uid=project.structures['cosio'].atom_sites['Co1'].b_iso.uid,
)
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='biso_Co2',
- param_uid=project.sample_models['cosio'].atom_sites['Co2'].b_iso.uid,
+ param_uid=project.structures['cosio'].atom_sites['Co2'].b_iso.uid,
)
# %% [markdown]
# Set constraints.
# %%
-project.analysis.constraints.add(
- lhs_alias='biso_Co2',
- rhs_expr='biso_Co1',
+project.analysis.constraints.create(
+ expression='biso_Co2 = biso_Co1',
)
# %% [markdown]
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/tutorials/ed-6.py b/docs/docs/tutorials/ed-6.py
similarity index 73%
rename from tutorials/ed-6.py
rename to docs/docs/tutorials/ed-6.py
index 106a088c..e0339c91 100644
--- a/tutorials/ed-6.py
+++ b/docs/docs/tutorials/ed-6.py
@@ -11,40 +11,40 @@
# %%
from easydiffraction import ExperimentFactory
from easydiffraction import Project
-from easydiffraction import SampleModelFactory
+from easydiffraction import StructureFactory
from easydiffraction import download_data
# %% [markdown]
-# ## Define Sample Model
+# ## Define Structure
#
-# This section shows how to add sample models and modify their
+# This section shows how to add structures and modify their
# parameters.
#
-# #### Create Sample Model
+# #### Create Structure
# %%
-model = SampleModelFactory.create(name='hs')
+structure = StructureFactory.from_scratch(name='hs')
# %% [markdown]
# #### Set Space Group
# %%
-model.space_group.name_h_m = 'R -3 m'
-model.space_group.it_coordinate_system_code = 'h'
+structure.space_group.name_h_m = 'R -3 m'
+structure.space_group.it_coordinate_system_code = 'h'
# %% [markdown]
# #### Set Unit Cell
# %%
-model.cell.length_a = 6.9
-model.cell.length_c = 14.1
+structure.cell.length_a = 6.9
+structure.cell.length_c = 14.1
# %% [markdown]
# #### Set Atom Sites
# %%
-model.atom_sites.add(
+structure.atom_sites.create(
label='Zn',
type_symbol='Zn',
fract_x=0,
@@ -53,7 +53,7 @@
wyckoff_letter='b',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='Cu',
type_symbol='Cu',
fract_x=0.5,
@@ -62,7 +62,7 @@
wyckoff_letter='e',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='O',
type_symbol='O',
fract_x=0.21,
@@ -71,7 +71,7 @@
wyckoff_letter='h',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='Cl',
type_symbol='Cl',
fract_x=0,
@@ -80,7 +80,7 @@
wyckoff_letter='c',
b_iso=0.5,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='H',
type_symbol='2H',
fract_x=0.13,
@@ -94,7 +94,7 @@
# ## Define Experiment
#
# This section shows how to add experiments, configure their parameters,
-# and link the sample models defined in the previous step.
+# and link the structures defined in the previous step.
#
# #### Download Measured Data
@@ -105,7 +105,7 @@
# #### Create Experiment
# %%
-expt = ExperimentFactory.create(name='hrpt', data_path=data_path)
+expt = ExperimentFactory.from_data_path(name='hrpt', data_path=data_path)
# %% [markdown]
# #### Set Instrument
@@ -128,26 +128,26 @@
# #### Set Background
# %%
-expt.background.add(id='1', x=4.4196, y=500)
-expt.background.add(id='2', x=6.6207, y=500)
-expt.background.add(id='3', x=10.4918, y=500)
-expt.background.add(id='4', x=15.4634, y=500)
-expt.background.add(id='5', x=45.6041, y=500)
-expt.background.add(id='6', x=74.6844, y=500)
-expt.background.add(id='7', x=103.4187, y=500)
-expt.background.add(id='8', x=121.6311, y=500)
-expt.background.add(id='9', x=159.4116, y=500)
+expt.background.create(id='1', x=4.4196, y=500)
+expt.background.create(id='2', x=6.6207, y=500)
+expt.background.create(id='3', x=10.4918, y=500)
+expt.background.create(id='4', x=15.4634, y=500)
+expt.background.create(id='5', x=45.6041, y=500)
+expt.background.create(id='6', x=74.6844, y=500)
+expt.background.create(id='7', x=103.4187, y=500)
+expt.background.create(id='8', x=121.6311, y=500)
+expt.background.create(id='9', x=159.4116, y=500)
# %% [markdown]
# #### Set Linked Phases
# %%
-expt.linked_phases.add(id='hs', scale=0.5)
+expt.linked_phases.create(id='hs', scale=0.5)
# %% [markdown]
# ## Define Project
#
-# The project object is used to manage the sample model, experiment, and
+# The project object is used to manage the structure, experiment, and
# analysis.
#
# #### Create Project
@@ -164,16 +164,16 @@
# project.plotter.engine = 'plotly'
# %% [markdown]
-# #### Add Sample Model
+# #### Add Structure
# %%
-project.sample_models.add(sample_model=model)
+project.structures.add(structure)
# %% [markdown]
# #### Add Experiment
# %%
-project.experiments.add(experiment=expt)
+project.experiments.add(expt)
# %% [markdown]
# ## Perform Analysis
@@ -181,16 +181,10 @@
# This section shows the analysis process, including how to set up
# calculation and fitting engines.
#
-# #### Set Calculator
-
-# %%
-project.analysis.current_calculator = 'cryspy'
-
-# %% [markdown]
# #### Set Minimizer
# %%
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+project.analysis.current_minimizer = 'lmfit'
# %% [markdown]
# #### Plot Measured vs Calculated
@@ -207,8 +201,8 @@
# Set parameters to be refined.
# %%
-model.cell.length_a.free = True
-model.cell.length_c.free = True
+structure.cell.length_a.free = True
+structure.cell.length_c.free = True
expt.linked_phases['hs'].scale.free = True
expt.instrument.calib_twotheta_offset.free = True
@@ -281,11 +275,11 @@
# Set more parameters to be refined.
# %%
-model.atom_sites['O'].fract_x.free = True
-model.atom_sites['O'].fract_z.free = True
-model.atom_sites['Cl'].fract_z.free = True
-model.atom_sites['H'].fract_x.free = True
-model.atom_sites['H'].fract_z.free = True
+structure.atom_sites['O'].fract_x.free = True
+structure.atom_sites['O'].fract_z.free = True
+structure.atom_sites['Cl'].fract_z.free = True
+structure.atom_sites['H'].fract_x.free = True
+structure.atom_sites['H'].fract_z.free = True
# %% [markdown]
# Show free parameters after selection.
@@ -317,11 +311,11 @@
# Set more parameters to be refined.
# %%
-model.atom_sites['Zn'].b_iso.free = True
-model.atom_sites['Cu'].b_iso.free = True
-model.atom_sites['O'].b_iso.free = True
-model.atom_sites['Cl'].b_iso.free = True
-model.atom_sites['H'].b_iso.free = True
+structure.atom_sites['Zn'].b_iso.free = True
+structure.atom_sites['Cu'].b_iso.free = True
+structure.atom_sites['O'].b_iso.free = True
+structure.atom_sites['Cl'].b_iso.free = True
+structure.atom_sites['H'].b_iso.free = True
# %% [markdown]
# Show free parameters after selection.
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/tutorials/ed-7.py b/docs/docs/tutorials/ed-7.py
similarity index 82%
rename from tutorials/ed-7.py
rename to docs/docs/tutorials/ed-7.py
index 1e12be88..cd719154 100644
--- a/tutorials/ed-7.py
+++ b/docs/docs/tutorials/ed-7.py
@@ -11,38 +11,38 @@
# %%
from easydiffraction import ExperimentFactory
from easydiffraction import Project
-from easydiffraction import SampleModelFactory
+from easydiffraction import StructureFactory
from easydiffraction import download_data
# %% [markdown]
-# ## Define Sample Model
+# ## Define Structure
#
-# This section shows how to add sample models and modify their
+# This section shows how to add structures and modify their
# parameters.
#
-# #### Create Sample Model
+# #### Create Structure
# %%
-model = SampleModelFactory.create(name='si')
+structure = StructureFactory.from_scratch(name='si')
# %% [markdown]
# #### Set Space Group
# %%
-model.space_group.name_h_m = 'F d -3 m'
-model.space_group.it_coordinate_system_code = '2'
+structure.space_group.name_h_m = 'F d -3 m'
+structure.space_group.it_coordinate_system_code = '2'
# %% [markdown]
# #### Set Unit Cell
# %%
-model.cell.length_a = 5.431
+structure.cell.length_a = 5.431
# %% [markdown]
# #### Set Atom Sites
# %%
-model.atom_sites.add(
+structure.atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0.125,
@@ -55,7 +55,7 @@
# ## Define Experiment
#
# This section shows how to add experiments, configure their
-# parameters, and link the sample models defined in the previous step.
+# parameters, and link the structures defined in the previous step.
#
# #### Download Measured Data
@@ -66,7 +66,9 @@
# #### Create Experiment
# %%
-expt = ExperimentFactory.create(name='sepd', data_path=data_path, beam_mode='time-of-flight')
+expt = ExperimentFactory.from_data_path(
+ name='sepd', data_path=data_path, beam_mode='time-of-flight'
+)
# %% [markdown]
# #### Set Instrument
@@ -101,18 +103,18 @@
# %%
expt.background_type = 'line-segment'
for x in range(0, 35000, 5000):
- expt.background.add(id=str(x), x=x, y=200)
+ expt.background.create(id=str(x), x=x, y=200)
# %% [markdown]
# #### Set Linked Phases
# %%
-expt.linked_phases.add(id='si', scale=10.0)
+expt.linked_phases.create(id='si', scale=10.0)
# %% [markdown]
# ## Define Project
#
-# The project object is used to manage the sample model, experiment, and
+# The project object is used to manage the structure, experiment, and
# analysis.
#
# #### Create Project
@@ -121,16 +123,16 @@
project = Project()
# %% [markdown]
-# #### Add Sample Model
+# #### Add Structure
# %%
-project.sample_models.add(sample_model=model)
+project.structures.add(structure)
# %% [markdown]
# #### Add Experiment
# %%
-project.experiments.add(experiment=expt)
+project.experiments.add(expt)
# %% [markdown]
# ## Perform Analysis
@@ -138,16 +140,10 @@
# This section shows the analysis process, including how to set up
# calculation and fitting engines.
#
-# #### Set Calculator
-
-# %%
-project.analysis.current_calculator = 'cryspy'
-
-# %% [markdown]
# #### Set Minimizer
# %%
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+project.analysis.current_minimizer = 'lmfit'
# %% [markdown]
# #### Plot Measured vs Calculated
@@ -162,7 +158,7 @@
# Set parameters to be refined.
# %%
-model.cell.length_a.free = True
+structure.cell.length_a.free = True
expt.linked_phases['si'].scale.free = True
expt.instrument.calib_d_to_tof_offset.free = True
@@ -265,7 +261,7 @@
# Set more parameters to be refined.
# %%
-model.atom_sites['Si'].b_iso.free = True
+structure.atom_sites['Si'].b_iso.free = True
# %% [markdown]
# Show free parameters after selection.
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/tutorials/ed-8.py b/docs/docs/tutorials/ed-8.py
similarity index 79%
rename from tutorials/ed-8.py
rename to docs/docs/tutorials/ed-8.py
index 2aa85227..b8cdf0bd 100644
--- a/tutorials/ed-8.py
+++ b/docs/docs/tutorials/ed-8.py
@@ -14,38 +14,38 @@
# %%
from easydiffraction import ExperimentFactory
from easydiffraction import Project
-from easydiffraction import SampleModelFactory
+from easydiffraction import StructureFactory
from easydiffraction import download_data
# %% [markdown]
-# ## Define Sample Model
+# ## Define Structure
#
-# This section covers how to add sample models and modify their
+# This section covers how to add structures and modify their
# parameters.
#
-# #### Create Sample Model
+# #### Create Structure
# %%
-model = SampleModelFactory.create(name='ncaf')
+structure = StructureFactory.from_scratch(name='ncaf')
# %% [markdown]
# #### Set Space Group
# %%
-model.space_group.name_h_m = 'I 21 3'
-model.space_group.it_coordinate_system_code = '1'
+structure.space_group.name_h_m = 'I 21 3'
+structure.space_group.it_coordinate_system_code = '1'
# %% [markdown]
# #### Set Unit Cell
# %%
-model.cell.length_a = 10.250256
+structure.cell.length_a = 10.250256
# %% [markdown]
# #### Set Atom Sites
# %%
-model.atom_sites.add(
+structure.atom_sites.create(
label='Ca',
type_symbol='Ca',
fract_x=0.4663,
@@ -54,7 +54,7 @@
wyckoff_letter='b',
b_iso=0.92,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='Al',
type_symbol='Al',
fract_x=0.2521,
@@ -63,7 +63,7 @@
wyckoff_letter='a',
b_iso=0.73,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='Na',
type_symbol='Na',
fract_x=0.0851,
@@ -72,7 +72,7 @@
wyckoff_letter='a',
b_iso=2.08,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='F1',
type_symbol='F',
fract_x=0.1377,
@@ -81,7 +81,7 @@
wyckoff_letter='c',
b_iso=0.90,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='F2',
type_symbol='F',
fract_x=0.3625,
@@ -90,7 +90,7 @@
wyckoff_letter='c',
b_iso=1.37,
)
-model.atom_sites.add(
+structure.atom_sites.create(
label='F3',
type_symbol='F',
fract_x=0.4612,
@@ -104,7 +104,7 @@
# ## Define Experiment
#
# This section shows how to add experiments, configure their parameters,
-# and link the sample models defined in the previous step.
+# and link the structures defined in the previous step.
#
# #### Download Measured Data
@@ -118,14 +118,14 @@
# #### Create Experiment
# %%
-expt56 = ExperimentFactory.create(
+expt56 = ExperimentFactory.from_data_path(
name='wish_5_6',
data_path=data_path56,
beam_mode='time-of-flight',
)
# %%
-expt47 = ExperimentFactory.create(
+expt47 = ExperimentFactory.from_data_path(
name='wish_4_7',
data_path=data_path47,
beam_mode='time-of-flight',
@@ -205,7 +205,7 @@
],
start=1,
):
- expt56.background.add(id=str(idx), x=x, y=y)
+ expt56.background.create(id=str(idx), x=x, y=y)
# %%
expt47.background_type = 'line-segment'
@@ -241,32 +241,32 @@
],
start=1,
):
- expt47.background.add(id=str(idx), x=x, y=y)
+ expt47.background.create(id=str(idx), x=x, y=y)
# %% [markdown]
# #### Set Linked Phases
# %%
-expt56.linked_phases.add(id='ncaf', scale=1.0)
+expt56.linked_phases.create(id='ncaf', scale=1.0)
# %%
-expt47.linked_phases.add(id='ncaf', scale=2.0)
+expt47.linked_phases.create(id='ncaf', scale=2.0)
# %% [markdown]
# #### Set Excluded Regions
# %%
-expt56.excluded_regions.add(id='1', start=0, end=10010)
-expt56.excluded_regions.add(id='2', start=100010, end=200000)
+expt56.excluded_regions.create(id='1', start=0, end=10010)
+expt56.excluded_regions.create(id='2', start=100010, end=200000)
# %%
-expt47.excluded_regions.add(id='1', start=0, end=10006)
-expt47.excluded_regions.add(id='2', start=100004, end=200000)
+expt47.excluded_regions.create(id='1', start=0, end=10006)
+expt47.excluded_regions.create(id='2', start=100004, end=200000)
# %% [markdown]
# ## Define Project
#
-# The project object is used to manage the sample model, experiments,
+# The project object is used to manage the structure, experiments,
# and analysis
#
# #### Create Project
@@ -283,17 +283,17 @@
# project.plotter.engine = 'plotly'
# %% [markdown]
-# #### Add Sample Model
+# #### Add Structure
# %%
-project.sample_models.add(sample_model=model)
+project.structures.add(structure)
# %% [markdown]
# #### Add Experiment
# %%
-project.experiments.add(experiment=expt56)
-project.experiments.add(experiment=expt47)
+project.experiments.add(expt56)
+project.experiments.add(expt47)
# %% [markdown]
# ## Perform Analysis
@@ -301,33 +301,27 @@
# This section shows the analysis process, including how to set up
# calculation and fitting engines.
#
-# #### Set Calculator
-
-# %%
-project.analysis.current_calculator = 'cryspy'
-
-# %% [markdown]
# #### Set Minimizer
# %%
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+project.analysis.current_minimizer = 'lmfit'
# %% [markdown]
# #### Set Fit Mode
# %%
-project.analysis.fit_mode = 'joint'
+project.analysis.fit_mode.mode = 'joint'
# %% [markdown]
# #### Set Free Parameters
# %%
-model.atom_sites['Ca'].b_iso.free = True
-model.atom_sites['Al'].b_iso.free = True
-model.atom_sites['Na'].b_iso.free = True
-model.atom_sites['F1'].b_iso.free = True
-model.atom_sites['F2'].b_iso.free = True
-model.atom_sites['F3'].b_iso.free = True
+structure.atom_sites['Ca'].b_iso.free = True
+structure.atom_sites['Al'].b_iso.free = True
+structure.atom_sites['Na'].b_iso.free = True
+structure.atom_sites['F1'].b_iso.free = True
+structure.atom_sites['F2'].b_iso.free = True
+structure.atom_sites['F3'].b_iso.free = True
# %%
expt56.linked_phases['ncaf'].scale.free = True
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
+}
diff --git a/tutorials/ed-9.py b/docs/docs/tutorials/ed-9.py
similarity index 65%
rename from tutorials/ed-9.py
rename to docs/docs/tutorials/ed-9.py
index a5fe1647..34da9359 100644
--- a/tutorials/ed-9.py
+++ b/docs/docs/tutorials/ed-9.py
@@ -11,38 +11,38 @@
# %%
from easydiffraction import ExperimentFactory
from easydiffraction import Project
-from easydiffraction import SampleModelFactory
+from easydiffraction import StructureFactory
from easydiffraction import download_data
# %% [markdown]
-# ## Define Sample Models
+# ## Define Structures
#
-# This section shows how to add sample models and modify their
+# This section shows how to add structures and modify their
# parameters.
#
-# ### Create Sample Model 1: LBCO
+# ### Create Structure 1: LBCO
# %%
-model_1 = SampleModelFactory.create(name='lbco')
+structure_1 = StructureFactory.from_scratch(name='lbco')
# %% [markdown]
# #### Set Space Group
# %%
-model_1.space_group.name_h_m = 'P m -3 m'
-model_1.space_group.it_coordinate_system_code = '1'
+structure_1.space_group.name_h_m = 'P m -3 m'
+structure_1.space_group.it_coordinate_system_code = '1'
# %% [markdown]
# #### Set Unit Cell
# %%
-model_1.cell.length_a = 3.8909
+structure_1.cell.length_a = 3.8909
# %% [markdown]
# #### Set Atom Sites
# %%
-model_1.atom_sites.add(
+structure_1.atom_sites.create(
label='La',
type_symbol='La',
fract_x=0,
@@ -52,7 +52,7 @@
b_iso=0.2,
occupancy=0.5,
)
-model_1.atom_sites.add(
+structure_1.atom_sites.create(
label='Ba',
type_symbol='Ba',
fract_x=0,
@@ -62,7 +62,7 @@
b_iso=0.2,
occupancy=0.5,
)
-model_1.atom_sites.add(
+structure_1.atom_sites.create(
label='Co',
type_symbol='Co',
fract_x=0.5,
@@ -71,7 +71,7 @@
wyckoff_letter='b',
b_iso=0.2567,
)
-model_1.atom_sites.add(
+structure_1.atom_sites.create(
label='O',
type_symbol='O',
fract_x=0,
@@ -82,29 +82,29 @@
)
# %% [markdown]
-# ### Create Sample Model 2: Si
+# ### Create Structure 2: Si
# %%
-model_2 = SampleModelFactory.create(name='si')
+structure_2 = StructureFactory.from_scratch(name='si')
# %% [markdown]
# #### Set Space Group
# %%
-model_2.space_group.name_h_m = 'F d -3 m'
-model_2.space_group.it_coordinate_system_code = '2'
+structure_2.space_group.name_h_m = 'F d -3 m'
+structure_2.space_group.it_coordinate_system_code = '2'
# %% [markdown]
# #### Set Unit Cell
# %%
-model_2.cell.length_a = 5.43146
+structure_2.cell.length_a = 5.43146
# %% [markdown]
# #### Set Atom Sites
# %%
-model_2.atom_sites.add(
+structure_2.atom_sites.create(
label='Si',
type_symbol='Si',
fract_x=0.0,
@@ -118,7 +118,7 @@
# ## Define Experiment
#
# This section shows how to add experiments, configure their parameters,
-# and link the sample models defined in the previous step.
+# and link the structures defined in the previous step.
#
# #### Download Data
@@ -129,7 +129,7 @@
# #### Create Experiment
# %%
-experiment = ExperimentFactory.create(
+experiment = ExperimentFactory.from_data_path(
name='mcstas',
data_path=data_path,
sample_form='powder',
@@ -173,31 +173,31 @@
# Add background points.
# %%
-experiment.background.add(id='1', x=45000, y=0.2)
-experiment.background.add(id='2', x=50000, y=0.2)
-experiment.background.add(id='3', x=55000, y=0.2)
-experiment.background.add(id='4', x=65000, y=0.2)
-experiment.background.add(id='5', x=70000, y=0.2)
-experiment.background.add(id='6', x=75000, y=0.2)
-experiment.background.add(id='7', x=80000, y=0.2)
-experiment.background.add(id='8', x=85000, y=0.2)
-experiment.background.add(id='9', x=90000, y=0.2)
-experiment.background.add(id='10', x=95000, y=0.2)
-experiment.background.add(id='11', x=100000, y=0.2)
-experiment.background.add(id='12', x=105000, y=0.2)
-experiment.background.add(id='13', x=110000, y=0.2)
+experiment.background.create(id='1', x=45000, y=0.2)
+experiment.background.create(id='2', x=50000, y=0.2)
+experiment.background.create(id='3', x=55000, y=0.2)
+experiment.background.create(id='4', x=65000, y=0.2)
+experiment.background.create(id='5', x=70000, y=0.2)
+experiment.background.create(id='6', x=75000, y=0.2)
+experiment.background.create(id='7', x=80000, y=0.2)
+experiment.background.create(id='8', x=85000, y=0.2)
+experiment.background.create(id='9', x=90000, y=0.2)
+experiment.background.create(id='10', x=95000, y=0.2)
+experiment.background.create(id='11', x=100000, y=0.2)
+experiment.background.create(id='12', x=105000, y=0.2)
+experiment.background.create(id='13', x=110000, y=0.2)
# %% [markdown]
# #### Set Linked Phases
# %%
-experiment.linked_phases.add(id='lbco', scale=4.0)
-experiment.linked_phases.add(id='si', scale=0.2)
+experiment.linked_phases.create(id='lbco', scale=4.0)
+experiment.linked_phases.create(id='si', scale=0.2)
# %% [markdown]
# ## Define Project
#
-# The project object is used to manage sample models, experiments, and
+# The project object is used to manage structures, experiments, and
# analysis.
#
# #### Create Project
@@ -206,23 +206,23 @@
project = Project()
# %% [markdown]
-# #### Add Sample Models
+# #### Add Structures
# %%
-project.sample_models.add(sample_model=model_1)
-project.sample_models.add(sample_model=model_2)
+project.structures.add(structure_1)
+project.structures.add(structure_2)
# %% [markdown]
-# #### Show Sample Models
+# #### Show Structures
# %%
-project.sample_models.show_names()
+project.structures.show_names()
# %% [markdown]
# #### Add Experiments
# %%
-project.experiments.add(experiment=experiment)
+project.experiments.add(experiment)
# %% [markdown]
# #### Set Excluded Regions
@@ -236,8 +236,8 @@
# Add excluded regions.
# %%
-experiment.excluded_regions.add(id='1', start=0, end=40000)
-experiment.excluded_regions.add(id='2', start=108000, end=200000)
+experiment.excluded_regions.create(id='1', start=0, end=40000)
+experiment.excluded_regions.create(id='2', start=108000, end=200000)
# %% [markdown]
# Show excluded regions.
@@ -263,28 +263,22 @@
# This section outlines the analysis process, including how to configure
# calculation and fitting engines.
#
-# #### Set Calculator
-
-# %%
-project.analysis.current_calculator = 'cryspy'
-
-# %% [markdown]
# #### Set Minimizer
# %%
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+project.analysis.current_minimizer = 'lmfit'
# %% [markdown]
# #### Set Fitting Parameters
#
-# Set sample model parameters to be optimized.
+# Set structure parameters to be optimized.
# %%
-model_1.cell.length_a.free = True
-model_1.atom_sites['Co'].b_iso.free = True
-model_1.atom_sites['O'].b_iso.free = True
+structure_1.cell.length_a.free = True
+structure_1.atom_sites['Co'].b_iso.free = True
+structure_1.atom_sites['O'].b_iso.free = True
-model_2.cell.length_a.free = True
+structure_2.cell.length_a.free = True
# %% [markdown]
# Set experiment parameters to be optimized.
diff --git a/tutorials/index.json b/docs/docs/tutorials/index.json
similarity index 73%
rename from tutorials/index.json
rename to docs/docs/tutorials/index.json
index 983499fd..3f2f223c 100644
--- a/tutorials/index.json
+++ b/docs/docs/tutorials/index.json
@@ -3,14 +3,14 @@
"url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-1/ed-1.ipynb",
"original_name": "quick_from-cif_pd-neut-cwl_LBCO-HRPT",
"title": "Quick Start: LBCO from CIF",
- "description": "Minimalistic Rietveld refinement of La0.5Ba0.5CoO3 using sample model and experiment defined via CIF files",
+ "description": "Minimalistic Rietveld refinement of La0.5Ba0.5CoO3 using structure and experiment defined via CIF files",
"level": "quick"
},
"2": {
"url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-2/ed-2.ipynb",
"original_name": "quick_from-code_pd-neut-cwl_LBCO-HRPT",
"title": "Quick Start: LBCO from Code",
- "description": "Minimalistic Rietveld refinement of La0.5Ba0.5CoO3 with sample model and experiment defined directly in code",
+ "description": "Minimalistic Rietveld refinement of La0.5Ba0.5CoO3 with structure and experiment defined directly in code",
"level": "quick"
},
"3": {
@@ -86,8 +86,36 @@
"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"
+ },
+ "14": {
+ "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-14/ed-14.ipynb",
+ "original_name": "",
+ "title": "Crystal Structure: Tb2TiO7, HEiDi",
+ "description": "Crystal structure refinement of Tb2TiO7 using single crystal neutron diffraction data from HEiDi at FRM II",
+ "level": "intermediate"
+ },
+ "15": {
+ "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-15/ed-15.ipynb",
+ "original_name": "",
+ "title": "Crystal Structure: Taurine, SENJU (TOF)",
+ "description": "Crystal structure refinement of Taurine using time-of-flight neutron single crystal diffraction data from SENJU at J-PARC",
+ "level": "intermediate"
+ },
+ "16": {
+ "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-16/ed-16.ipynb",
+ "original_name": "advanced_joint-fit_bragg-pdf_pd-neut-tof_Si",
+ "title": "Advanced: Si Joint Bragg+PDF Fit",
+ "description": "Joint refinement of Si crystal structure combining Bragg diffraction (SEPD) and pair distribution function (NOMAD) analysis",
+ "level": "advanced"
+ },
+ "17": {
+ "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-17/ed-17.ipynb",
+ "original_name": "",
+ "title": "Structure Refinement: Co2SiO4, D20 (Temperature scan)",
+ "description": "Sequential Rietveld refinement of Co2SiO4 using constant wavelength neutron powder diffraction data from D20 at ILL across a temperature scan",
+ "level": "advanced"
}
}
diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md
new file mode 100644
index 00000000..ef22e949
--- /dev/null
+++ b/docs/docs/tutorials/index.md
@@ -0,0 +1,98 @@
+---
+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.
+
+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)
+section of the documentation.
+
+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.
+
+## 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.
+
+## 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.
+
+## 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.
+
+## 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.
+- [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.
+- [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.
+- [Co2SiO4 Temperature scan](ed-17.ipynb) – Sequential Rietveld
+ refinement of Co2SiO4 using constant wavelength neutron powder
+ diffraction data from D20 at ILL across a temperature scan.
+
+## 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.
diff --git a/docs/user-guide/analysis-workflow/analysis.md b/docs/docs/user-guide/analysis-workflow/analysis.md
similarity index 50%
rename from docs/user-guide/analysis-workflow/analysis.md
rename to docs/docs/user-guide/analysis-workflow/analysis.md
index cdb948d2..76aaa699 100644
--- a/docs/user-guide/analysis-workflow/analysis.md
+++ b/docs/docs/user-guide/analysis-workflow/analysis.md
@@ -5,80 +5,86 @@ 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 sample 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
-To show the supported calculation engines:
+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.analysis.show_supported_calculators()
+project.experiments['hrpt'].show_supported_calculator_types()
```
The example of the output is:
-Supported calculators
+Supported calculator types
-| Calculator | Description |
-| ---------- | ----------------------------------------------------------- |
-| cryspy | CrysPy library for crystallographic calculations |
-| pdffit | PDFfit2 library for pair distribution function calculations |
+| Calculator | Description |
+| ---------- | ------------------------------------------------ |
+| cryspy | CrysPy library for crystallographic calculations |
-To select the desired calculation engine, e.g., 'cryspy':
+To explicitly select a calculation engine for an experiment:
```python
-project.analysis.current_calculator = 'cryspy'
+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
@@ -94,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
@@ -136,65 +145,57 @@ Supported minimizers
| --------------------- | ------------------------------------------------------------------------ |
| lmfit | LMFIT library using the default Levenberg-Marquardt least squares method |
| lmfit (leastsq) | LMFIT library with Levenberg-Marquardt least squares method |
-| lmfit (least_squares) | LMFIT library with SciPy’s trust region reflective algorithm |
+| lmfit (least_squares) | LMFIT library with SciPy's trust region reflective algorithm |
| dfols | DFO-LS library for derivative-free least-squares optimization |
-To select the desired calculation engine, e.g., 'lmfit (least_squares)':
+To select the desired minimizer, e.g., 'lmfit':
```python
-project.analysis.current_minimizer = 'lmfit (leastsq)'
+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.
-To show the supported fit modes:
+The supported fit modes are:
-```python
-project.analysis.show_supported_fit_modes()
-```
-
-An example of supported fit modes is:
-
-Supported fit modes
+| Mode | Description |
+| ------ | ------------------------------------------------------------------- |
+| single | Independent fitting of each experiment; no shared parameters |
+| joint | Simultaneous fitting of all experiments; some parameters are shared |
-| Strategy | Description |
-| -------- | ------------------------------------------------------------------- |
-| single | Independent fitting of each experiment; no shared parameters |
-| joint | Simultaneous fitting of all experiments; some parameters are shared |
-
-You can set the fit mode using the `set_fit_mode` method of the `analysis`
-object:
+You can set the fit mode on the `analysis` object:
```python
-project.analysis.fit_mode = 'joint'
+project.analysis.fit_mode.mode = 'joint'
```
-To check the current fit mode, you can use the `show_current_fit_mode` method:
+To check the current fit mode:
```python
-project.analysis.show_current_fit_mode()
+print(project.analysis.fit_mode.mode.value)
```
### Perform Fit
-Refining the sample model 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:
```python
-# Set sample model parameters to be refined.
-project.sample_models['lbco'].cell.length_a.free = True
+# Set structure parameters to be refined.
+project.structures['lbco'].cell.length_a.free = True
# Set experiment parameters to be refined.
project.experiments['hrpt'].linked_phases['lbco'].scale.free = True
@@ -203,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:
@@ -241,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:
@@ -254,72 +256,67 @@ 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 sample model:
+An example of setting aliases for parameters in a structure:
```python
# Set aliases for the atomic displacement parameters
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='biso_La',
- param_uid=project.sample_models['lbco'].atom_sites['La'].b_iso.uid
+ param_uid=project.structures['lbco'].atom_sites['La'].b_iso.uid,
)
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='biso_Ba',
- param_uid=project.sample_models['lbco'].atom_sites['Ba'].b_iso.uid
+ param_uid=project.structures['lbco'].atom_sites['Ba'].b_iso.uid,
)
# Set aliases for the occupancies of the atom sites
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='occ_La',
- param_uid=project.sample_models['lbco'].atom_sites['La'].occupancy.uid
+ param_uid=project.structures['lbco'].atom_sites['La'].occupancy.uid,
)
-project.analysis.aliases.add(
+project.analysis.aliases.create(
label='occ_Ba',
- param_uid=project.sample_models['lbco'].atom_sites['Ba'].occupancy.uid
+ param_uid=project.structures['lbco'].atom_sites['Ba'].occupancy.uid,
)
```
### 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
+`create` method of the `constraints` object. Each constraint is a single
+expression string of the form `lhs = rhs`, where the left-hand side is
+an alias and the right-hand side is an expression involving other
+aliases.
An example of setting constraints for the aliases defined above:
```python
-project.analysis.constraints.add(
- lhs_alias='biso_Ba',
- rhs_expr='biso_La'
-)
+project.analysis.constraints.create(expression='biso_Ba = biso_La')
-project.analysis.constraints.add(
- lhs_alias='occ_Ba',
- rhs_expr='1 - occ_La'
-)
+project.analysis.constraints.create(expression='occ_Ba = 1 - occ_La')
```
-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()
@@ -329,18 +326,18 @@ The example of the output is:
User defined constraints
-| lhs_alias | rhs_expr | full expression |
-| --------- | ---------- | ------------------- |
-| biso_Ba | biso_La | biso_Ba = biso_La |
-| occ_Ba | 1 - occ_La | occ_Ba = 1 - occ_La |
+| expression |
+| ------------------- |
+| biso_Ba = biso_La |
+| occ_Ba = 1 - occ_La |
## Analysis as CIF
To inspect an analysis configuration in CIF format, use:
```python
-# Show sample model as CIF
-project.sample_models['lbco'].show_as_cif()
+# Show structure as CIF
+project.structures['lbco'].show_as_cif()
```
Example output:
@@ -360,17 +357,17 @@ Example output:
│ occ_Ba lbco.atom_site.Ba.occupancy │
│ │
│ loop_ │
-│ _constraint.lhs_alias │
-│ _constraint.rhs_expr │
-│ biso_Ba biso_La │
-│ occ_Ba "1 - occ_La" │
+│ _constraint.expression │
+│ "biso_Ba = biso_La" │
+│ "occ_Ba = 1 - occ_La" │
╘════════════════════════════════════════════════╛
```
## 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/user-guide/analysis-workflow/experiment.md b/docs/docs/user-guide/analysis-workflow/experiment.md
similarity index 76%
rename from docs/user-guide/analysis-workflow/experiment.md
rename to docs/docs/user-guide/analysis-workflow/experiment.md
index 3bbc9fdc..c121d6a9 100644
--- a/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
-`sample_models`, as described in the [Sample Model](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](#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,37 +105,43 @@ 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.
-project.experiments.add_from_data_path(name='hrpt',
- data_path='data/hrpt_lbco.xye',
- sample_form='powder',
- beam_mode='constant wavelength',
- radiation_probe='neutron',
- scattering_type='bragg')
+project.experiments.add_from_data_path(
+ name='hrpt',
+ data_path='data/hrpt_lbco.xye',
+ sample_form='powder',
+ beam_mode='constant wavelength',
+ radiation_probe='neutron',
+ scattering_type='bragg',
+)
```
To add an experiment of default type, you can simply do:
```python
# Add an experiment of default type
-project.experiments.add_from_data_path(name='hrpt',
- data_path='data/hrpt_lbco.xye')
+project.experiments.add_from_data_path(
+ name='hrpt',
+ data_path='data/hrpt_lbco.xye',
+)
```
-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
-`add_without_data` 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
-project.experiments.add_without_data(name='hrpt',
- sample_form='powder',
- beam_mode='constant wavelength',
- radiation_probe='x-ray')
+project.experiments.create(
+ name='hrpt',
+ sample_form='powder',
+ beam_mode='constant wavelength',
+ radiation_probe='x-ray',
+)
```
Finally, you can also add an experiment by passing the experiment object
@@ -138,34 +149,39 @@ directly using the `add` method:
```python
# Add an experiment by passing the experiment object directly
-from easydiffraction import Experiment
-experiment = Experiment(name='hrpt',
- sample_form='powder',
- beam_mode='constant wavelength',
- radiation_probe='neutron',
- scattering_type='bragg')
+from easydiffraction import ExperimentFactory
+
+experiment = ExperimentFactory.create(
+ name='hrpt',
+ data_path='data/hrpt_lbco.xye',
+ sample_form='powder',
+ beam_mode='constant wavelength',
+ radiation_probe='neutron',
+ scattering_type='bragg',
+)
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 sample model 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 }
@@ -179,8 +195,8 @@ project.experiments['hrpt'].instrument.calib_twotheta_offset = 0.6
```python
# Add excluded regions to the experiment
-project.experiments['hrpt'].excluded_regions.add(start=0, end=10)
-project.experiments['hrpt'].excluded_regions.add(start=160, end=180)
+project.experiments['hrpt'].excluded_regions.create(start=0, end=10)
+project.experiments['hrpt'].excluded_regions.create(start=160, end=180)
```
### 3. Peak Category { #peak-category }
@@ -204,26 +220,26 @@ project.experiments['hrpt'].peak.broad_lorentz_y = 0.1
project.experiments['hrpt'].background_type = 'line-segment'
# Add background points
-project.experiments['hrpt'].background.add(x=10, y=170)
-project.experiments['hrpt'].background.add(x=30, y=170)
-project.experiments['hrpt'].background.add(x=50, y=170)
-project.experiments['hrpt'].background.add(x=110, y=170)
-project.experiments['hrpt'].background.add(x=165, y=170)
+project.experiments['hrpt'].background.create(x=10, y=170)
+project.experiments['hrpt'].background.create(x=30, y=170)
+project.experiments['hrpt'].background.create(x=50, y=170)
+project.experiments['hrpt'].background.create(x=110, y=170)
+project.experiments['hrpt'].background.create(x=165, y=170)
```
### 5. Linked Phases Category { #linked-phases-category }
```python
-# Link the sample model defined in the previous step to the experiment
-project.experiments['hrpt'].linked_phases.add(id='lbco', scale=10.0)
+# Link the structure defined in the previous step to the experiment
+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:
@@ -235,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.
@@ -592,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
new file mode 100644
index 00000000..84598210
--- /dev/null
+++ b/docs/docs/user-guide/analysis-workflow/index.md
@@ -0,0 +1,37 @@
+# Analysis Workflow
+
+To streamline the **data analysis process**, EasyDiffraction follows a
+structured workflow divided into **five key steps**:
+
+```mermaid
+flowchart LR
+ a(Project)
+ b(Model)
+ c(Experiment)
+ d(Analysis)
+ e(Summary)
+ a --> b
+ b --> c
+ c --> d
+ 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 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.
diff --git a/docs/user-guide/analysis-workflow/model.md b/docs/docs/user-guide/analysis-workflow/model.md
similarity index 64%
rename from docs/user-guide/analysis-workflow/model.md
rename to docs/docs/user-guide/analysis-workflow/model.md
index 69dbe765..95e309cd 100644
--- a/docs/user-guide/analysis-workflow/model.md
+++ b/docs/docs/user-guide/analysis-workflow/model.md
@@ -2,39 +2,41 @@
icon: material/puzzle
---
-# :material-puzzle: Sample Model
+# :material-puzzle: Structure
-The **Sample Model** 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.
+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.
EasyDiffraction allows you to:
- **Load an existing model** from a file (**CIF** format).
-- **Manually define** a new sample model by specifying crystallographic
+- **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 sample model in
-EasyDiffraction. If you have a crystallographic information file (CIF) for your
-sample model, you can add it to your project using the `add_phase_from_file`
-method of the `project` instance. 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.add_phase_from_file('data/lbco.cif')
+project.structures.add_from_cif_path('data/lbco.cif')
```
-Accessing the model after loading it will be done through the `sample_models`
-object 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`,
@@ -52,100 +54,102 @@ data_lbco
you can access it in the code as follows:
```python
-# Access the sample model by its name
-project.sample_models['lbco']
+# Access the structure by its name
+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 `add` method of the `sample_models` 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 sample model with default parameters
-# The sample model name is used to reference it later.
-project.sample_models.add(name='nacl')
+# Add a structure with default parameters
+# The structure name is used to reference it later.
+project.structures.create(name='nacl')
```
-The `add` method creates a new sample model 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 }
```python
# Set space group
-project.sample_models['nacl'].space_group.name_h_m = 'F m -3 m'
+project.structures['nacl'].space_group.name_h_m = 'F m -3 m'
```
### 2. Cell Category { #cell-category }
```python
# Define unit cell parameters
-project.sample_models['nacl'].cell.length_a = 5.691694
+project.structures['nacl'].cell.length_a = 5.691694
```
### 3. Atom Sites Category { #atom-sites-category }
```python
# Add atomic sites
-project.sample_models['nacl'].atom_sites.append(
+project.structures['nacl'].atom_sites.create(
label='Na',
type_symbol='Na',
fract_x=0,
fract_y=0,
fract_z=0,
occupancy=1,
- b_iso_or_equiv=0.5
+ b_iso_or_equiv=0.5,
)
-project.sample_models['nacl'].atom_sites.append(
+project.structures['nacl'].atom_sites.create(
label='Cl',
type_symbol='Cl',
fract_x=0,
fract_y=0,
fract_z=0.5,
occupancy=1,
- b_iso_or_equiv=0.5
+ b_iso_or_equiv=0.5,
)
```
## Listing Defined Models
-To check which sample models have been added to the `project`, use:
+To check which structures have been added to the `project`, use:
```python
-# Show defined sample models
-project.sample_models.show_names()
+# Show defined structures
+project.structures.show_names()
```
Expected output:
```
-Defined sample models 🧩
+Defined structures 🧩
['lbco', 'nacl']
```
## Viewing a Model as CIF
-To inspect a sample model in CIF format, use:
+To inspect a structure in CIF format, use:
```python
-# Show sample model as CIF
-project.sample_models['lbco'].show_as_cif()
+# Show structure as CIF
+project.structures['lbco'].show_as_cif()
```
Example output:
```
-Sample model 🧩 'lbco' as cif
+Structure 🧩 'lbco' as cif
╒═══════════════════════════════════════════╕
│ data_lbco │
│ │
@@ -178,10 +182,10 @@ Sample model 🧩 '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
-`sample_models` 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:
@@ -224,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/user-guide/analysis-workflow/project.md b/docs/docs/user-guide/analysis-workflow/project.md
similarity index 78%
rename from docs/user-guide/analysis-workflow/project.md
rename to docs/docs/user-guide/analysis-workflow/project.md
index 6987f10d..60c944b9 100644
--- a/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 **sample models**.
+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
@@ -30,10 +31,10 @@ project = ed.Project(name='lbco_hrpt')
# Define project info
project.info.title = 'La0.5Ba0.5CoO3 from neutron diffraction at HRPT@PSI'
-project.info.description = '''This project demonstrates a standard refinement
+project.info.description = """This project demonstrates a standard refinement
of La0.5Ba0.5CoO3, which crystallizes in a perovskite-type structure, using
neutron powder diffraction data collected in constant wavelength mode at the
-HRPT diffractometer (PSI).'''
+HRPT diffractometer (PSI)."""
```
## Saving a 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')
@@ -73,7 +76,7 @@ The example below illustrates a typical **project structure** for a
📁 La0.5Ba0.5CoO3 - Project directory.
├── 📄 project.cif - Main project description file.
-├── 📁 sample_models - Folder with sample models (crystallographic structures).
+├── 📁 structures - Folder with structures (crystallographic structures).
│ ├── 📄 lbco.cif - File with La0.5Ba0.5CoO3 structure parameters.
│ └── ...
├── 📁 experiments - Folder with instrumental parameters and measured data.
@@ -89,20 +92,20 @@ 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"
If you save the project right after creating it, the project directory will
only contain the `project.cif` file. The other folders and files will be
- created as you add sample models, experiments, and set up the analysis. The
+ created as you add structures, experiments, and set up the analysis. The
summary folder will be created after the analysis is completed.
### 1. project.cif
-This file provides an overview of the project, including file names of the
-**sample models** 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.
@@ -114,7 +117,7 @@ data_La0.5Ba0.5CoO3
_project .description "neutrons, powder, constant wavelength, HRPT@PSI"
loop_
-_sample_model .cif_file_name
+_structure .cif_file_name
lbco.cif
loop_
@@ -125,11 +128,11 @@ hrpt.cif
-### 2. sample_models / lbco.cif
+### 2. structures / lbco.cif
-This file contains crystallographic information associated with the sample
-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**.
@@ -257,10 +260,9 @@ occ_La lbco.atom_site.La.occupancy
occ_Ba lbco.atom_site.Ba.occupancy
loop_
-_constraint .lhs_alias
-_constraint .rhs_expr
-biso_Ba biso_La
-occ_Ba "1 - occ_La"
+_constraint .expression
+"biso_Ba = biso_La"
+"occ_Ba = 1 - occ_La"
@@ -271,4 +273,4 @@ occ_Ba "1 - occ_La"
---
Now that the Project has been defined, you can proceed to the next step:
-[Sample Model](model.md).
+[Structure](model.md).
diff --git a/docs/user-guide/analysis-workflow/summary.md b/docs/docs/user-guide/analysis-workflow/summary.md
similarity index 72%
rename from docs/user-guide/analysis-workflow/summary.md
rename to docs/docs/user-guide/analysis-workflow/summary.md
index 4790f857..34a89b61 100644
--- a/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.
{ 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.
{ 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
-sample. 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 **sample model** 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.
{ width="450", loading=lazy }
diff --git a/docs/user-guide/data-format.md b/docs/docs/user-guide/data-format.md
similarity index 75%
rename from docs/user-guide/data-format.md
rename to docs/docs/user-guide/data-format.md
index acbe6bfe..da7c92b1 100644
--- a/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 **sample model** (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,16 +199,16 @@ 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
-- **sample model**: defines the sample model
+- **structure**: defines the structure
- **experiment**: contains the experiment setup and measured data
- **analysis**: stores fitting and analysis parameters
- **summary**: captures analysis results
@@ -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
new file mode 100644
index 00000000..b4366684
--- /dev/null
+++ b/docs/docs/user-guide/first-steps.md
@@ -0,0 +1,190 @@
+# 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.
+
+## 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:
+
+```python
+import easydiffraction
+```
+
+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:
+
+```python
+project = ed.Project()
+```
+
+A complete tutorial using the `import` syntax can be found
+[here](../tutorials/ed-3.ipynb).
+
+### 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:
+
+```python
+from easydiffraction import Project
+from easydiffraction import Structure
+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:
+
+```python
+project = Project()
+```
+
+A complete tutorial using the `from` syntax can be found
+[here](../tutorials/ed-4.ipynb).
+
+## 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.
+
+For example, you can download a data file like this:
+
+```python
+import easydiffraction as ed
+
+ed.download_from_repository(
+ 'hrpt_lbco.xye',
+ branch='docs',
+ destination='data',
+)
+```
+
+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.
+
+### 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:
+
+```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.
+
+An example of the output for a Bragg diffraction experiment:
+
+| Calculator | Description |
+| ---------- | ------------------------------------------------ |
+| cryspy | CrysPy library for crystallographic calculations |
+
+### Supported minimizers
+
+You can also check the available minimizers using the
+`show_available_minimizers()` method:
+
+```python
+project.show_available_minimizers()
+```
+
+### Available parameters
+
+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.
+
+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:
+
+| | Code variable | Unique ID for CIF |
+| --- | --------------------------------------------------- | -------------------------------- |
+| 1 | project.structures['lbco'].atom_site['La'].adp_type | lbco.atom_site.La.ADP_type |
+| 2 | project.structures['lbco'].atom_site['La'].b_iso | lbco.atom_site.La.B_iso_or_equiv |
+| 3 | project.structures['lbco'].atom_site['La'].fract_x | lbco.atom_site.La.fract_x |
+| 4 | project.structures['lbco'].atom_site['La'].fract_y | lbco.atom_site.La.fract_y |
+| ... | ... | ... |
+| 59 | project.experiments['hrpt'].peak.broad_gauss_u | hrpt.peak.broad_gauss_u |
+| 60 | project.experiments['hrpt'].peak.broad_gauss_v | hrpt.peak.broad_gauss_v |
+| 61 | project.experiments['hrpt'].peak.broad_gauss_w | hrpt.peak.broad_gauss_w |
+
+### Supported plotters
+
+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()
+```
+
+An example of the output is:
+
+| Engine | Description |
+| ------------ | ------------------------------------------ |
+| asciichartpy | Console ASCII line charts |
+| plotly | Interactive browser-based graphing library |
+
+## 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.
diff --git a/docs/user-guide/glossary.md b/docs/docs/user-guide/glossary.md
similarity index 80%
rename from docs/user-guide/glossary.md
rename to docs/docs/user-guide/glossary.md
index 827cfdf5..75f0300c 100644
--- a/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/user-guide/index.md b/docs/docs/user-guide/index.md
similarity index 57%
rename from docs/user-guide/index.md
rename to docs/docs/user-guide/index.md
index a6671f3d..2ddc28b9 100644
--- a/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/user-guide/parameters.md b/docs/docs/user-guide/parameters.md
similarity index 89%
rename from docs/user-guide/parameters.md
rename to docs/docs/user-guide/parameters.md
index 0fc1d609..622bf6f1 100644
--- a/docs/user-guide/parameters.md
+++ b/docs/docs/user-guide/parameters.md
@@ -1,82 +1,91 @@
# 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 sample model 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`, `sample_models`,
-`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"
Remember that parameters are accessed in code through their parent objects,
- such as `project`, `sample_models`, or `experiments`. For example, if you
- have a sample model with the ID `nacl`, you can access the space group name
+ such as `project`, `structures`, or `experiments`. For example, if you
+ have a structure with the ID `nacl`, you can access the space group name
using the following syntax:
```python
- project.sample_models['nacl'].space_group.name_h_m
+ project.structures['nacl'].space_group.name_h_m
```
-In the example above, `space_group` is a sample model 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.
-## Sample model parameters
+## Structure parameters
-Below is a list of parameters used to describe the sample model in
+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"
@@ -131,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"
@@ -154,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"
@@ -241,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/user-guide/parameters/_diffrn_radiation.md b/docs/docs/user-guide/parameters/_diffrn_radiation.md
similarity index 87%
rename from docs/user-guide/parameters/_diffrn_radiation.md
rename to docs/docs/user-guide/parameters/_diffrn_radiation.md
index e3c0a7e9..6e2f0a06 100644
--- a/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/user-guide/parameters/_diffrn_radiation_wavelength.md b/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md
similarity index 95%
rename from docs/user-guide/parameters/_diffrn_radiation_wavelength.md
rename to docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md
index 4d205788..3b750b22 100644
--- a/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/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 94%
rename from docs/user-guide/parameters/_pd_calib.md
rename to docs/docs/user-guide/parameters/_pd_calib.md
index 2b96a4c8..407c5f73 100644
--- a/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/user-guide/parameters/atom_site.md b/docs/docs/user-guide/parameters/atom_site.md
similarity index 82%
rename from docs/user-guide/parameters/atom_site.md
rename to docs/docs/user-guide/parameters/atom_site.md
index 44325b8a..7acdb650 100644
--- a/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/user-guide/parameters/background.md b/docs/docs/user-guide/parameters/background.md
similarity index 75%
rename from docs/user-guide/parameters/background.md
rename to docs/docs/user-guide/parameters/background.md
index 89985470..e307c981 100644
--- a/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/user-guide/parameters/cell.md b/docs/docs/user-guide/parameters/cell.md
similarity index 95%
rename from docs/user-guide/parameters/cell.md
rename to docs/docs/user-guide/parameters/cell.md
index 5aeb12ae..806df516 100644
--- a/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/user-guide/parameters/expt_type.md b/docs/docs/user-guide/parameters/expt_type.md
similarity index 95%
rename from docs/user-guide/parameters/expt_type.md
rename to docs/docs/user-guide/parameters/expt_type.md
index 5aeb12ae..806df516 100644
--- a/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/user-guide/parameters/instrument.md b/docs/docs/user-guide/parameters/instrument.md
similarity index 73%
rename from docs/user-guide/parameters/instrument.md
rename to docs/docs/user-guide/parameters/instrument.md
index 3975161c..fbeca6c9 100644
--- a/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/user-guide/parameters/linked_phases.md b/docs/docs/user-guide/parameters/linked_phases.md
similarity index 86%
rename from docs/user-guide/parameters/linked_phases.md
rename to docs/docs/user-guide/parameters/linked_phases.md
index df3f6d29..1d00f1d2 100644
--- a/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/user-guide/parameters/pd_meas.md b/docs/docs/user-guide/parameters/pd_meas.md
similarity index 97%
rename from docs/user-guide/parameters/pd_meas.md
rename to docs/docs/user-guide/parameters/pd_meas.md
index b294a96a..fcb4db17 100644
--- a/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/user-guide/parameters/peak.md b/docs/docs/user-guide/parameters/peak.md
similarity index 95%
rename from docs/user-guide/parameters/peak.md
rename to docs/docs/user-guide/parameters/peak.md
index 5aeb12ae..806df516 100644
--- a/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/user-guide/parameters/space_group.md b/docs/docs/user-guide/parameters/space_group.md
similarity index 87%
rename from docs/user-guide/parameters/space_group.md
rename to docs/docs/user-guide/parameters/space_group.md
index 883be103..ae5bce6b 100644
--- a/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/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 6641a35b..c272bdec 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: © 2025 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
-# Jupyter notebooks
+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
+
+# 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: numpy
+ 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: |
@@ -62,7 +184,7 @@ nav:
- Analysis Workflow:
- Analysis Workflow: user-guide/analysis-workflow/index.md
- Project: user-guide/analysis-workflow/project.md
- - Sample Model: user-guide/analysis-workflow/model.md
+ - Structure: user-guide/analysis-workflow/model.md
- Experiment: user-guide/analysis-workflow/experiment.md
- Analysis: user-guide/analysis-workflow/analysis.md
- Summary: user-guide/analysis-workflow/summary.md
@@ -71,29 +193,37 @@ nav:
- Getting Started:
- LBCO quick CIF: tutorials/ed-1.ipynb
- LBCO quick code: tutorials/ed-2.ipynb
- - LBCO basic: tutorials/ed-3.ipynb
- - PbSO4 advanced: tutorials/ed-4.ipynb
- - Standard Diffraction:
+ - LBCO complete: tutorials/ed-3.ipynb
+ - Powder Diffraction:
- Co2SiO4 pd-neut-cwl: tutorials/ed-5.ipynb
- HS pd-neut-cwl: tutorials/ed-6.ipynb
- Si pd-neut-tof: tutorials/ed-7.ipynb
- NCAF pd-neut-tof: tutorials/ed-8.ipynb
- LBCO+Si McStas: tutorials/ed-9.ipynb
+ - Single Crystal Diffraction:
+ - Tb2TiO7 sg-neut-cwl: tutorials/ed-14.ipynb
+ - Taurine sg-neut-tof: tutorials/ed-15.ipynb
- Pair Distribution Function:
- Ni pd-neut-cwl: tutorials/ed-10.ipynb
- Si pd-neut-tof: tutorials/ed-11.ipynb
- NaCl pd-xray: tutorials/ed-12.ipynb
+ - Multiple Data Blocks:
+ - PbSO4 NPD+XRD: tutorials/ed-4.ipynb
+ - LBCO+Si McStas: tutorials/ed-9.ipynb
+ - Si Bragg+PDF: tutorials/ed-16.ipynb
+ - Co2SiO4 T-scan: tutorials/ed-17.ipynb
- Workshops & Schools:
- - 2025 DMSC: tutorials/ed-13.ipynb
+ - DMSC Summer School: tutorials/ed-13.ipynb
- API Reference:
- API Reference: api-reference/index.md
- analysis: api-reference/analysis.md
- core: api-reference/core.md
- crystallography: api-reference/crystallography.md
+ - datablocks:
+ - experiment: api-reference/datablocks/experiment.md
+ - structure: api-reference/datablocks/structure.md
- display: api-reference/display.md
- - experiments: api-reference/experiments.md
- io: api-reference/io.md
- project: api-reference/project.md
- - sample_models: api-reference/sample_models.md
- summary: api-reference/summary.md
- utils: api-reference/utils.md
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 ) %}
+
+
+{% elif config.theme.logo %}
+
+{% else %} {% set icon = config.theme.icon.logo or "material/library" %} {%
+include ".icons/" ~ icon ~ ".svg" %} {% endif %}
diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md
deleted file mode 100644
index 55f406a4..00000000
--- a/docs/tutorials/index.md
+++ /dev/null
@@ -1,79 +0,0 @@
----
-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.
-
-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)
-section of the documentation.
-
-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 sample model 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 sample model 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 `basic`](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.
-- [PbSO4 `advanced`](ed-4.ipynb) – Demonstrates a more flexible and advanced
- approach to using the EasyDiffraction library, intended for users who are more
- comfortable with Python programming. This tutorial covers a Rietveld
- refinement of the PbSO4 crystal structure based on the joint fit of both X-ray
- and neutron diffraction data.
-
-## Standard 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.
-- [LBCO+Si McStas](ed-9.ipynb) – Demonstrates a Rietveld refinement of the
- La0.5Ba0.5CoO3 crystal structure with a small amount of Si impurity as a
- secondary phase using time-of-flight neutron powder diffraction data simulated
- with McStas.
-
-## 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.
-
-## Workshops & Schools
-
-- [2025 DMSC](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 2025.
diff --git a/docs/user-guide/analysis-workflow/index.md b/docs/user-guide/analysis-workflow/index.md
deleted file mode 100644
index 1ce3ec66..00000000
--- a/docs/user-guide/analysis-workflow/index.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Analysis Workflow
-
-To streamline the **data analysis process**, EasyDiffraction follows a
-structured workflow divided into **five key steps**:
-
-```mermaid
-flowchart LR
- a(Project)
- b(Model)
- c(Experiment)
- d(Analysis)
- e(Summary)
- a --> b
- b --> c
- c --> d
- d --> e
-```
-
-- [:material-archive: Project](project.md) – Establish a **project** as a
- container for sample model and experiment parameters, measured and calculated
- data, analysis settings and results.
-- [:material-puzzle: Sample Model](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.
-
-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/user-guide/first-steps.md b/docs/user-guide/first-steps.md
deleted file mode 100644
index 0500b189..00000000
--- a/docs/user-guide/first-steps.md
+++ /dev/null
@@ -1,182 +0,0 @@
-# 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.
-
-## 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:
-
-```python
-import easydiffraction
-```
-
-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:
-
-```python
-project = ed.Project()
-```
-
-A complete tutorial using the `import` syntax can be found
-[here](../../tutorials/ed-3/).
-
-### Importing specific parts
-
-Alternatively, you can import specific classes or methods from the package. For
-example, you can import the `Project`, `SampleModel`, `Experiment` classes and
-`download_from_repository` method like this:
-
-```python
-from easydiffraction import (
- Project,
- SampleModel,
- Experiment,
- 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:
-
-```python
-project = Project()
-```
-
-A complete tutorial using the `from` syntax can be found
-[here](../../tutorials/ed-4/).
-
-## 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.
-
-For example, you can download a sample data file like this:
-
-```python
-import easydiffraction as ed
-
-ed.download_from_repository('hrpt_lbco.xye',
- branch='docs',
- destination='data')
-```
-
-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.
-
-### Supported calculators
-
-For example, you can use the `show_supported_calculators()` method to see which
-calculation engines are available for use in your project:
-
-```python
-project.show_supported_calculators()
-```
-
-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 the `show_supported_calculators()` method is:
-
-| Calculator | Description |
-| ---------- | ----------------------------------------------------------- |
-| cryspy | CrysPy library for crystallographic calculations |
-| pdffit | PDFfit2 library for pair distribution function calculations |
-
-### Supported minimizers
-
-You can also check the available minimizers using the
-`show_available_minimizers()` method:
-
-```python
-project.show_available_minimizers()
-```
-
-### Available parameters
-
-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.
-
-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:
-
-| | Code variable | Unique ID for CIF |
-| --- | ------------------------------------------------------ | -------------------------------- |
-| 1 | project.sample_models['lbco'].atom_site['La'].adp_type | lbco.atom_site.La.ADP_type |
-| 2 | project.sample_models['lbco'].atom_site['La'].b_iso | lbco.atom_site.La.B_iso_or_equiv |
-| 3 | project.sample_models['lbco'].atom_site['La'].fract_x | lbco.atom_site.La.fract_x |
-| 4 | project.sample_models['lbco'].atom_site['La'].fract_y | lbco.atom_site.La.fract_y |
-| ... | ... | ... |
-| 59 | project.experiments['hrpt'].peak.broad_gauss_u | hrpt.peak.broad_gauss_u |
-| 60 | project.experiments['hrpt'].peak.broad_gauss_v | hrpt.peak.broad_gauss_v |
-| 61 | project.experiments['hrpt'].peak.broad_gauss_w | hrpt.peak.broad_gauss_w |
-
-### Supported plotters
-
-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()
-```
-
-An example of the output is:
-
-| Engine | Description |
-| ------------ | ------------------------------------------ |
-| asciichartpy | Console ASCII line charts |
-| plotly | Interactive browser-based graphing library |
-
-## 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 sample models, 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/pixi.lock b/pixi.lock
index c8913145..5195fec7 100644
--- a/pixi.lock
+++ b/pixi.lock
@@ -5,142 +5,163 @@ environments:
- url: https://conda.anaconda.org/conda-forge/
indexes:
- https://pypi.org/simple
+ options:
+ pypi-prerelease-mode: if-necessary-or-explicit
packages:
linux-64:
- - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda
+ - 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.1.4-hbd8a1cb_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-75.1-he02047a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_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/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-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.3-hecca717_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_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/libopenblas-0.3.30-pthreads_h94d23a6_4.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.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
- 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.2.1-he2c55a7_1.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.11-hc97d973_100_cp313.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
- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-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
- 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/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_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/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/ed/34/a6536afaeee07fa351e2087bf7b5b1522aa703bc1f6e29d53c27a722ac33/gemmi-0.7.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_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/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/d9/69/4402ea66272dacc10b298cca18ed73e1c0791ff2ae9ed218d3859f9698ac/h5py-3.15.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
@@ -148,260 +169,303 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/e3/8b/1f02771d91ceafec996cef7f92f6a24010fedc47fd9404f8e11772d8501c/ncrystal_core-4.2.12-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/f2/85/ab6d04733a7d6ff32bfc8382bf1b07078228f5d6ebec5266b91bfc5c4ff7/pandas-3.0.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/29/f8/40e01c350ad9a2b3cb4e6adbcc8a83b17ee50dd5792102b6142385937db5/psutil-7.2.1-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5c/a5/a8c7562ec39f2647245b52ea4aeb13b5b125b3f48c0c152e9ebce7047a0a/pycifrw-5.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0e/50/80a8d080ac7d3d321e5e5d420c9a522b0aa770ec7013ea91f9a8b7d36e4a/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/15/63fb7a6908db2f03716c4a50aea7e27a7440fe6a09854282c401139afaf7/uv-0.9.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: ./
osx-64:
- conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda
- 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.1.4-hbd8a1cb_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-75.1-h120a0e1_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20250512.1-cxx17_hfc00f1c_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/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-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/libcxx-21.1.8-h3d58e20_0.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.3-heffb93a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_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/libopenblas-0.3.30-openmp_h6006d49_4.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hb99441e_1.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
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_18.conda
+ - 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/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.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.1-hd23fc13_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_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.2.1-h5523da6_1.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.11-h17c18a5_100_cp313.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
- conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.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
- 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/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-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/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/3e/e7/b88b72919c910d3233065680cbc74a2a9d00ed65a06b100751d5b78e08e1/gemmi-0.7.4-cp313-cp313-macosx_10_14_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/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/88/b3/40207e0192415cbff7ea1d37b9f24b33f6d38a5a2f5d18a678de78f967ae/h5py-3.15.1-cp313-cp313-macosx_10_13_x86_64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl
@@ -409,259 +473,302 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/ee/0d9d9218d2081e56828194f83d0eac6292b7182708fd07a62756c66f7194/ncrystal_core-4.2.12-py3-none-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/0b/48/aad6ec4f8d007534c091e9a7172b3ec1b1ee6d99a9cbb936b5eab6c6cf58/pandas-3.0.1-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/c5/cf/5180eb8c8bdf6a503c6919f1da28328bd1e6b3b1b5b9d5b01ae64f019616/psutil-7.2.1-cp36-abi3-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/87/0d/6af0bb9a45c771ffccd5c4c035c57ac9005e711b1191ddad1dd954187cfe/pycifrw-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/5e/68/bb76c97c284ce7fb8efa868994c2510588faa7075e60d8865d1373e54b7b/uv-0.9.22-py3-none-macosx_10_12_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl
+ - pypi: ./
osx-arm64:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda
- 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.1.4-hbd8a1cb_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-75.1-hfee45f7_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250512.1-cxx17_hd41c47c_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/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-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/libcxx-21.1.8-hf598326_0.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.3-haf25636_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_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/libopenblas-0.3.30-openmp_ha158390_3.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_18.conda
+ - 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/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.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.1-h8359307_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_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.2.1-h5230ea7_1.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.11-hfc2f54d_100_cp313.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
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.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
- 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/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-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/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/19/5b/0976c1af0dd59a6850e9ea3b6c6d28f3dff0651c694a6a6192a2933e8feb/gemmi-0.7.4-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/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/31/96/ba99a003c763998035b0de4c299598125df5fc6c9ccf834f152ddd60e0fb/h5py-3.15.1-cp313-cp313-macosx_11_0_arm64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl
@@ -669,155 +776,176 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/55/8d/2b26572e909238bb114d50fb0d1b6b54eb6dafa2d83a7264f18146796b0d/ncrystal_core-4.2.12-py3-none-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/a8/14/5990826f779f79148ae9d3a2c39593dc04d61d5d90541e71b5749f35af95/pandas-3.0.1-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/c5/2c/78e4a789306a92ade5000da4f5de3255202c534acdadc3aac7b5458fadef/psutil-7.2.1-cp36-abi3-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/83/81/bdd4bfabe70b7c9a8c0716a722ced4ebd27311afd1f4800cd405d3229c1b/pycifrw-5.0.1-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/49/7230b1d56aeaee0eefd346a70f582463f11fb7036d2d020bcf68053bd994/uv-0.9.22-py3-none-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl
+ - pypi: ./
win-64:
- - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda
- - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-h4c7d964_0.conda
+ - 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/icu-78.1-h637d24d_0.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/libexpat-2.7.3-hac47afa_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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.1-h2466b09_2.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.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-21.1.8-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/nodejs-25.2.1-he453025_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.11-h09917c8_100_cp313.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-hd094cb3_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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
- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h41ae7f8_34.conda
@@ -826,96 +954,117 @@ environments:
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-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
- 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/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-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/fb/4e/931480b9552c7d0feebe40c73725dd7703dcc578ba9efc14fe0e6d31cfd1/debugpy-1.8.19-cp313-cp313-win_amd64.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-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/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/19/f7/1a03aa6b06d26dbd1231b3ac907f8fdfcf02c0b27fc5f4c31493ad6ecdad/gemmi-0.7.4-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/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/e5/ea/fbb258a98863f99befb10ed727152b4ae659f322e1d9c0576f8a62754e81/h5py-3.15.1-cp313-cp313-win_amd64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl
@@ -923,269 +1072,314 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/7b/51/e13a37a8d924feefb444d7eb42094750ba1bba756cbb8c1f9a523414c4fb/ncrystal_core-4.2.12-py3-none-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/d6/7d/216a1588b65a7aa5f4535570418a599d943c85afb1d95b0876fc00aa1468/pandas-3.0.1-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/34/68/d9317542e3f2b180c4306e3f45d3c922d7e86d8ce39f941bb9e2e9d8599e/psutil-7.2.1-cp37-abi3-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/9f/9b/50835e8fd86073fa7aa921df61b4cebc1f0ff400e4338541675cb72b5507/pycifrw-5.0.1-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fc/19/b757fe28008236a4a713e813283721b8a40aa60cd7d3f83549f2e25a3155/pywinpty-3.0.2-cp313-cp313-win_amd64.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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/c0/c5/d17113020b2d43073412aeca09b60d2009442420372123b8d49cc253f8b8/sqlalchemy-2.0.45-cp313-cp313-win_amd64.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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/32/49/9e3e19ba756c4a5e6acb4ea74336d3035f7959254fbb05f5eb77bff067ed/uv-0.9.22-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
- py311-dev:
+ - pypi: https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl
+ - pypi: ./
+ py-311-env:
channels:
- url: https://conda.anaconda.org/conda-forge/
indexes:
- https://pypi.org/simple
+ options:
+ pypi-prerelease-mode: if-necessary-or-explicit
packages:
linux-64:
- - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda
+ - 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.1.4-hbd8a1cb_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-75.1-he02047a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_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/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-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.3-hecca717_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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/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.30-pthreads_h94d23a6_4.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.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/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.2.1-he2c55a7_1.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.14-hd63d673_2_cpython.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
- - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-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
- 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/f7/7c/347280982982383621d29b8c544cf497ae07ac41e44b1ca4903024131f55/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/61/1ca198af22f7dd22c17ab86e9024ed3c06299cfdb08170640e9996d501a0/fonttools-4.61.1-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/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/96/ae/41aff180c36dd3c8f0a84faf38ac8683f8dca99250abcfbc3ed19897290b/gemmi-0.7.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_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/dc/a6/e959a127b630a58e23529972dbc868c107f9d583b5a9f878fb858c46bc1a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/8b/23/4ab1108e87851ccc69694b03b817d92e142966a6c4abd99e17db77f2c066/h5py-3.15.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
@@ -1193,259 +1387,304 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/e3/8b/1f02771d91ceafec996cef7f92f6a24010fedc47fd9404f8e11772d8501c/ncrystal_core-4.2.12-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/51/9f5d7a41f0b51649ddf2f2320595e15e122a40610b233d51928dd6c92353/numpy-2.4.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/2e/7c/870c7e7daec2a6c7ff2ac9e33b23317230d4e4e954b35112759ea4a924a7/pandas-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/5c/1f/8e66ab9be3aaf1435bc03edd1ebdf58ffcd17f7349c1d970cafe87af27d9/pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/29/f8/40e01c350ad9a2b3cb4e6adbcc8a83b17ee50dd5792102b6142385937db5/psutil-7.2.1-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/75/35/a44ce3d7c3f52a2a443cae261a05c2affc52fde7f1643974adbef105785f/pycifrw-5.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/f6/2b/60ce3ee7a5ae172bfcd419ce23259bb874d2cddd44f67c5df3760a1e22f9/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/15/63fb7a6908db2f03716c4a50aea7e27a7440fe6a09854282c401139afaf7/uv-0.9.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: ./
osx-64:
- conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda
- 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.1.4-hbd8a1cb_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-75.1-h120a0e1_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20250512.1-cxx17_hfc00f1c_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/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-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/libcxx-21.1.8-h3d58e20_0.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.3-heffb93a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.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/libopenblas-0.3.30-openmp_h6006d49_4.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hb99441e_1.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
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_18.conda
+ - 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.68.1-h70048d4_0.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.1-hd23fc13_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_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.2.1-h5523da6_1.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.14-h74c2667_2_cpython.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
- - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.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
- 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/9b/77baf488516e9ced25fc215a6f75d803493fc3f6a1a1227ac35697910c2a/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/ac/49/4138d1acb6261499bedde1c07f8c2605d1d8f9d77a151e5507fd3ef084b6/fonttools-4.61.1-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/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/28/bc/e943898c25121f36625ab4913b8e24d0bdd054a17e380d19924066102574/gemmi-0.7.4-cp311-cp311-macosx_10_14_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/1f/cb/48e964c452ca2b92175a9b2dca037a553036cb053ba69e284650ce755f13/greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/41/fd/8349b48b15b47768042cff06ad6e1c229f0a4bd89225bf6b6894fea27e6d/h5py-3.15.1-cp311-cp311-macosx_10_9_x86_64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl
@@ -1453,258 +1692,303 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/ee/0d9d9218d2081e56828194f83d0eac6292b7182708fd07a62756c66f7194/ncrystal_core-4.2.12-py3-none-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/2b/09/3c4abbc1dcd8010bf1a611d174c7aa689fc505585ec806111b4406f6f1b1/numpy-2.4.3-cp311-cp311-macosx_14_0_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/ff/07/c7087e003ceee9b9a82539b40414ec557aa795b584a1a346e89180853d79/pandas-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/43/c4/bf8328039de6cc22182c3ef007a2abfbbdab153661c0a9aa78af8d706391/pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/c5/cf/5180eb8c8bdf6a503c6919f1da28328bd1e6b3b1b5b9d5b01ae64f019616/psutil-7.2.1-cp36-abi3-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/62/b6/84364503e0726da4a263e1736d0e1754526d1b1729d0087c680d96345570/pycifrw-5.0.1-cp311-cp311-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/5e/68/bb76c97c284ce7fb8efa868994c2510588faa7075e60d8865d1373e54b7b/uv-0.9.22-py3-none-macosx_10_12_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: ./
osx-arm64:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda
- 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.1.4-hbd8a1cb_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-75.1-hfee45f7_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250512.1-cxx17_hd41c47c_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/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-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/libcxx-21.1.8-hf598326_0.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.3-haf25636_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.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/libopenblas-0.3.30-openmp_ha158390_3.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_18.conda
+ - 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.68.1-h8f3e76b_0.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.1-h8359307_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_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.2.1-h5230ea7_1.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.14-h18782d2_2_cpython.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
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.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
- 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/d7/cd/7ab01154e6eb79ee2fab76bf4d89e94c6648116557307ee4ebbb85e5c1bf/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/69/12/bf9f4eaa2fad039356cc627587e30ed008c03f1cebd3034376b5ee8d1d44/fonttools-4.61.1-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/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/46/3e/51e7914c8a640548d1b980140b1bd1419c169bee300a556cfd7f4175444d/gemmi-0.7.4-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/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/c1/b0/1c628e26a0b95858f54aba17e1599e7f6cd241727596cc2580b72cb0a9bf/h5py-3.15.1-cp311-cp311-macosx_11_0_arm64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl
@@ -1712,154 +1996,176 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/55/8d/2b26572e909238bb114d50fb0d1b6b54eb6dafa2d83a7264f18146796b0d/ncrystal_core-4.2.12-py3-none-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/ef/27/d26c85cbcd86b26e4f125b0668e7a7c0542d19dd7d23ee12e87b550e95b5/numpy-2.4.3-cp311-cp311-macosx_14_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/c1/27/90683c7122febeefe84a56f2cde86a9f05f68d53885cebcc473298dfc33e/pandas-3.0.1-cp311-cp311-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/43/06/7264c0597e676104cc22ca73ee48f752767cd4b1fe084662620b17e10120/pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/c5/2c/78e4a789306a92ade5000da4f5de3255202c534acdadc3aac7b5458fadef/psutil-7.2.1-cp36-abi3-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f5/5c/b999ea3e64981018d52846b9b69193fa581a70cd255912cb6962a33a666a/pycifrw-5.0.1-cp311-cp311-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a2/1c/769552a9d840065137272ebe86ffbb0bc92b0f1e0a68ee5266a225f8cd7b/sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/49/7230b1d56aeaee0eefd346a70f582463f11fb7036d2d020bcf68053bd994/uv-0.9.22-py3-none-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: ./
win-64:
- - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda
- - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-h4c7d964_0.conda
+ - 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/icu-78.1-h637d24d_0.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/libexpat-2.7.3-hac47afa_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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.1-h2466b09_2.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.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-21.1.8-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/nodejs-25.2.1-he453025_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.14-h0159041_2_cpython.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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
- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h41ae7f8_34.conda
@@ -1868,96 +2174,118 @@ environments:
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-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
- 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/27/56/c216625f453df6e0559ed666d246fcbaaa93f3aa99eaa5080cea1229aa3d/coverage-7.13.1-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/f2/a8/aaac7ff12ddf5d68a39e13a423a8490426f5f661384f5ad8d9062761bd8e/debugpy-1.8.19-cp311-cp311-win_amd64.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/07/ad/37dd1ae5fa6e01612a1fbb954f0927681f282925a86e86198ccd7b15d515/fonttools-4.61.1-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/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/dd/9d/412d75eb7b9c0aa1e939b419a66c7d61471aa387919d4be32893921564af/gemmi-0.7.4-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/1d/d5/c339b3b4bc8198b7caa4f2bd9fd685ac9f29795816d8db112da3d04175bb/greenlet-3.3.0-cp311-cp311-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/23/95/499b4e56452ef8b6c95a271af0dde08dac4ddb70515a75f346d4f400579b/h5py-3.15.1-cp311-cp311-win_amd64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl
@@ -1965,270 +2293,315 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/7b/51/e13a37a8d924feefb444d7eb42094750ba1bba756cbb8c1f9a523414c4fb/ncrystal_core-4.2.12-py3-none-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/76/1d/edccf27adedb754db7c4511d5eac8b83f004ae948fe2d3509e8b78097d4c/numpy-2.4.3-cp311-cp311-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/1f/67/af63f83cd6ca603a00fe8530c10a60f0879265b8be00b5930e8e78c5b30b/pandas-3.0.1-cp311-cp311-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/6c/af/b1d7e301c4cd26cd45d4af884d9ee9b6fab893b0ad2450d4746d74a6968c/pillow-12.1.0-cp311-cp311-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/34/68/d9317542e3f2b180c4306e3f45d3c922d7e86d8ce39f941bb9e2e9d8599e/psutil-7.2.1-cp37-abi3-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/7c/58/e60915c59f4adcbd97af30047694978127d63139ae05a0cf987c6f2e90f9/pycifrw-5.0.1-cp311-cp311-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a6/a1/409c1651c9f874d598c10f51ff586c416625601df4bca315d08baec4c3e3/pywinpty-3.0.2-cp311-cp311-win_amd64.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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bc/fa/09d0a11fe9f15c7fa5c7f0dd26be3d235b0c0cbf2f9544f43bc42efc8a24/sqlalchemy-2.0.45-cp311-cp311-win_amd64.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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/32/49/9e3e19ba756c4a5e6acb4ea74336d3035f7959254fbb05f5eb77bff067ed/uv-0.9.22-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl
+ - 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
- py313-dev:
+ - pypi: ./
+ py-313-env:
channels:
- url: https://conda.anaconda.org/conda-forge/
indexes:
- https://pypi.org/simple
+ options:
+ pypi-prerelease-mode: if-necessary-or-explicit
packages:
linux-64:
- - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2
- - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda
+ - 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.1.4-hbd8a1cb_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-75.1-he02047a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_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/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-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.3-hecca717_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_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/libopenblas-0.3.30-pthreads_h94d23a6_4.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.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
- 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.2.1-he2c55a7_1.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.11-hc97d973_100_cp313.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
- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda
+ - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-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
- 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/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_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/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/ed/34/a6536afaeee07fa351e2087bf7b5b1522aa703bc1f6e29d53c27a722ac33/gemmi-0.7.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_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/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/d9/69/4402ea66272dacc10b298cca18ed73e1c0791ff2ae9ed218d3859f9698ac/h5py-3.15.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
@@ -2236,260 +2609,303 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/e3/8b/1f02771d91ceafec996cef7f92f6a24010fedc47fd9404f8e11772d8501c/ncrystal_core-4.2.12-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/f2/85/ab6d04733a7d6ff32bfc8382bf1b07078228f5d6ebec5266b91bfc5c4ff7/pandas-3.0.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/29/f8/40e01c350ad9a2b3cb4e6adbcc8a83b17ee50dd5792102b6142385937db5/psutil-7.2.1-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5c/a5/a8c7562ec39f2647245b52ea4aeb13b5b125b3f48c0c152e9ebce7047a0a/pycifrw-5.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0e/50/80a8d080ac7d3d321e5e5d420c9a522b0aa770ec7013ea91f9a8b7d36e4a/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/15/63fb7a6908db2f03716c4a50aea7e27a7440fe6a09854282c401139afaf7/uv-0.9.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+ - pypi: ./
osx-64:
- conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda
- 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.1.4-hbd8a1cb_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-75.1-h120a0e1_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20250512.1-cxx17_hfc00f1c_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/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-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/libcxx-21.1.8-h3d58e20_0.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.3-heffb93a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_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/libopenblas-0.3.30-openmp_h6006d49_4.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hb99441e_1.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
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_18.conda
+ - 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/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.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.1-hd23fc13_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_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.2.1-h5523da6_1.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.11-h17c18a5_100_cp313.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
- conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.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
- 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/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-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/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/3e/e7/b88b72919c910d3233065680cbc74a2a9d00ed65a06b100751d5b78e08e1/gemmi-0.7.4-cp313-cp313-macosx_10_14_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/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/88/b3/40207e0192415cbff7ea1d37b9f24b33f6d38a5a2f5d18a678de78f967ae/h5py-3.15.1-cp313-cp313-macosx_10_13_x86_64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl
@@ -2497,259 +2913,302 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/ee/0d9d9218d2081e56828194f83d0eac6292b7182708fd07a62756c66f7194/ncrystal_core-4.2.12-py3-none-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/0b/48/aad6ec4f8d007534c091e9a7172b3ec1b1ee6d99a9cbb936b5eab6c6cf58/pandas-3.0.1-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/c5/cf/5180eb8c8bdf6a503c6919f1da28328bd1e6b3b1b5b9d5b01ae64f019616/psutil-7.2.1-cp36-abi3-macosx_10_9_x86_64.whl
+ - pypi: https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/87/0d/6af0bb9a45c771ffccd5c4c035c57ac9005e711b1191ddad1dd954187cfe/pycifrw-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/5e/68/bb76c97c284ce7fb8efa868994c2510588faa7075e60d8865d1373e54b7b/uv-0.9.22-py3-none-macosx_10_12_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl
+ - pypi: ./
osx-arm64:
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda
- 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.1.4-hbd8a1cb_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-75.1-hfee45f7_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250512.1-cxx17_hd41c47c_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/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-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/libcxx-21.1.8-hf598326_0.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.3-haf25636_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_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/libopenblas-0.3.30-openmp_ha158390_3.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_18.conda
+ - 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/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.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.1-h8359307_2.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_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.2.1-h5230ea7_1.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.11-hfc2f54d_100_cp313.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
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda
- - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda
+ - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda
- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.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
- 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/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-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/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-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/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/19/5b/0976c1af0dd59a6850e9ea3b6c6d28f3dff0651c694a6a6192a2933e8feb/gemmi-0.7.4-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/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/31/96/ba99a003c763998035b0de4c299598125df5fc6c9ccf834f152ddd60e0fb/h5py-3.15.1-cp313-cp313-macosx_11_0_arm64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl
@@ -2757,155 +3216,176 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/55/8d/2b26572e909238bb114d50fb0d1b6b54eb6dafa2d83a7264f18146796b0d/ncrystal_core-4.2.12-py3-none-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/a8/14/5990826f779f79148ae9d3a2c39593dc04d61d5d90541e71b5749f35af95/pandas-3.0.1-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/c5/2c/78e4a789306a92ade5000da4f5de3255202c534acdadc3aac7b5458fadef/psutil-7.2.1-cp36-abi3-macosx_11_0_arm64.whl
+ - pypi: https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/83/81/bdd4bfabe70b7c9a8c0716a722ced4ebd27311afd1f4800cd405d3229c1b/pycifrw-5.0.1-cp313-cp313-macosx_11_0_arm64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.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/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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/49/7230b1d56aeaee0eefd346a70f582463f11fb7036d2d020bcf68053bd994/uv-0.9.22-py3-none-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl
+ - pypi: ./
win-64:
- - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda
- - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-h4c7d964_0.conda
+ - 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/icu-78.1-h637d24d_0.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/libexpat-2.7.3-hac47afa_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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.1-h2466b09_2.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.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-21.1.8-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/nodejs-25.2.1-he453025_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.11-h09917c8_100_cp313.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-hd094cb3_1.conda
- - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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
- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h41ae7f8_34.conda
@@ -2914,96 +3394,117 @@ environments:
- pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.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/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/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/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/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-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
- pypi: https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl
- 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/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-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/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-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
- 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/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-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/fb/4e/931480b9552c7d0feebe40c73725dd7703dcc578ba9efc14fe0e6d31cfd1/debugpy-1.8.19-cp313-cp313-win_amd64.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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-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/dc/b4/a7ec1eaee86761a9dbfd339732b4706db3c6b65e970c12f0f56cfcce3dcf/docformatter-1.7.7-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-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/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/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
- pypi: https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-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/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/19/f7/1a03aa6b06d26dbd1231b3ac907f8fdfcf02c0b27fc5f4c31493ad6ecdad/gemmi-0.7.4-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/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.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/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/e5/ea/fbb258a98863f99befb10ed727152b4ae659f322e1d9c0576f8a62754e81/h5py-3.15.1-cp313-cp313-win_amd64.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/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-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/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.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/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/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-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/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-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/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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
- 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/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl
@@ -3011,150 +3512,166 @@ environments:
- pypi: https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
- 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/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-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/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-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/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/5b/69/93b34728cc386efdde0c342f8c680b9187dea7beb7adaf6b58a0713be101/mpld3-0.5.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
- 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/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.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/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/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-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
- pypi: https://files.pythonhosted.org/packages/eb/be/b257e12f9710819fde40adc972578bee6b72c5992da1bc8369bef2597756/nbmake-1.5.5-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/28/88/4789719fbbe166d12d345b3ac66b96105f10001b16e00a9765ba29261a21/nbqa-1.9.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/7b/51/e13a37a8d924feefb444d7eb42094750ba1bba756cbb8c1f9a523414c4fb/ncrystal_core-4.2.12-py3-none-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/d6/7d/216a1588b65a7aa5f4535570418a599d943c85afb1d95b0876fc00aa1468/pandas-3.0.1-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
+ - 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/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-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/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/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-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
- pypi: https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/34/68/d9317542e3f2b180c4306e3f45d3c922d7e86d8ce39f941bb9e2e9d8599e/psutil-7.2.1-cp37-abi3-win_amd64.whl
+ - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/9f/9b/50835e8fd86073fa7aa921df61b4cebc1f0ff400e4338541675cb72b5507/pycifrw-5.0.1-cp313-cp313-win_amd64.whl
- pypi: https://files.pythonhosted.org/packages/78/3c/2a612b95ddbb9a6bdcb47b7a93c4884f74c6ff22356b2f7b213b16e65c35/pycifstar-0.3.0.tar.gz
- pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+ - 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/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-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/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/fc/19/b757fe28008236a4a713e813283721b8a40aa60cd7d3f83549f2e25a3155/pywinpty-3.0.2-cp313-cp313-win_amd64.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/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.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/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.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
+ - 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
+ - pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
- 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/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/c0/c5/d17113020b2d43073412aeca09b60d2009442420372123b8d49cc253f8b8/sqlalchemy-2.0.45-cp313-cp313-win_amd64.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
- pypi: https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+ - 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/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/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.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/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
- - pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-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
+ - pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
+ - 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/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/32/49/9e3e19ba756c4a5e6acb4ea74336d3035f7959254fbb05f5eb77bff067ed/uv-0.9.22-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-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
- 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
- pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
- pypi: https://files.pythonhosted.org/packages/38/8b/7ec325b4e9e78beefc2d025b01ee8a2fde771ef7c957c3bff99b9e1fbffa/xraydb-4.5.8-py3-none-any.whl
- - pypi: https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl
- - pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
+ - pypi: https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl
+ - pypi: ./
packages:
-- conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2
- sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726
- md5: d7c89558ba9fa0495403155b64376d81
- license: None
- purls: []
- size: 2562
- timestamp: 1578324546067
-- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2
- build_number: 16
- sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22
- md5: 73aaf86a425cc6e73fcf236a5a46396d
- depends:
- - _libgcc_mutex 0.1 conda_forge
+- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda
+ build_number: 20
+ sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9
+ md5: a9f577daf3de00bca7c3c76c0ecbd1de
+ depends:
+ - __glibc >=2.17,<3.0.a0
- libgomp >=7.5.0
constrains:
- - openmp_impl 9999
+ - openmp_impl <0.0a0
license: BSD-3-Clause
license_family: BSD
purls: []
- size: 23621
- timestamp: 1650670423406
+ size: 28948
+ timestamp: 1770939786096
- conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda
build_number: 7
sha256: 30006902a9274de8abdad5a9f02ef7c8bb3d69a503486af0c1faee30b023e5b7
@@ -3334,17 +3851,28 @@ packages:
- frozenlist>=1.1.0
- typing-extensions>=4.2 ; python_full_version < '3.13'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl
+ name: annotated-doc
+ version: 0.0.4
+ sha256: 571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl
+ name: annotated-types
+ version: 0.7.0
+ sha256: 1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53
+ requires_dist:
+ - typing-extensions>=4.0.0 ; python_full_version < '3.9'
+ requires_python: '>=3.8'
+- 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
@@ -3417,6 +3945,27 @@ packages:
requires_dist:
- setuptools
- flake8 ; extra == 'qa'
+- pypi: https://files.pythonhosted.org/packages/6c/25/4f103d1bedb3593718713b3f743df7b3ff3fc68d36d6666c30265ef59c8a/ase-3.28.0-py3-none-any.whl
+ name: ase
+ 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'
+ - pytest-xdist>=3.2.0 ; extra == 'test'
+ - spglib>=1.9 ; extra == 'spglib'
+ - mypy ; extra == 'lint'
+ - ruff ; extra == 'lint'
+ - types-docutils ; extra == 'lint'
+ - types-pymysql ; extra == 'lint'
+ requires_python: '>=3.10'
- pypi: https://files.pythonhosted.org/packages/99/31/6cf181011dc738c33bf6ba7aea2e8e1d3c1f71b7dab1942f3054f66f6202/asteval-1.0.8-py3-none-any.whl
name: asteval
version: 1.0.8
@@ -3441,17 +3990,17 @@ packages:
- pytest-cov ; extra == 'test'
- pytest-xdist ; extra == 'test'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-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.0.5
- sha256: ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943
+ version: 2.3.0
+ sha256: eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315
requires_dist:
- typing-extensions>=4.0.0 ; python_full_version < '3.11'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl
+ requires_python: '>=3.10'
+- 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
@@ -3461,10 +4010,10 @@ packages:
- pycodestyle>=2.12.0
- tomli ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl
name: babel
- version: 2.17.0
- sha256: 4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2
+ version: 2.18.0
+ sha256: e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35
requires_dist:
- pytz>=2015.7 ; python_full_version < '3.9'
- tzdata ; sys_platform == 'win32' and extra == 'dev'
@@ -3476,17 +4025,17 @@ packages:
- pytz ; extra == 'dev'
- setuptools ; extra == 'dev'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl
name: backrefs
- version: '6.1'
- sha256: e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7
+ version: '6.2'
+ sha256: 08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be
requires_dist:
- regex ; extra == 'extras'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl
name: backrefs
- version: '6.1'
- sha256: 4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05
+ version: '6.2'
+ sha256: 12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b
requires_dist:
- regex ; extra == 'extras'
requires_python: '>=3.9'
@@ -3521,12 +4070,12 @@ packages:
version: 1.9.0
sha256: ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.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.3.0
- sha256: 7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4
+ version: 1.4.2
+ sha256: 7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88
requires_dist:
- - packaging>=19.1
+ - packaging>=24.0
- pyproject-hooks
- colorama ; os_name == 'nt'
- importlib-metadata>=4.6 ; python_full_version < '3.10.2'
@@ -3564,40 +4113,40 @@ packages:
- sphinx ; extra == 'dev'
- versioningit ; extra == 'dev'
requires_python: '>=3.9'
-- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda
- sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5
- md5: 51a19bba1b8ebfb60df25cde030b7ebc
+- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda
+ sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6
+ md5: d2ffd7602c02f2b316fd921d39876885
depends:
- __glibc >=2.17,<3.0.a0
- libgcc >=14
license: bzip2-1.0.6
license_family: BSD
purls: []
- size: 260341
- timestamp: 1757437258798
-- conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.conda
- sha256: 8f50b58efb29c710f3cecf2027a8d7325ba769ab10c746eff75cea3ac050b10c
- md5: 97c4b3bd8a90722104798175a1bdddbf
+ size: 260182
+ timestamp: 1771350215188
+- conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda
+ sha256: 9f242f13537ef1ce195f93f0cc162965d6cc79da578568d6d8e50f70dd025c42
+ md5: 4173ac3b19ec0a4f400b4f782910368b
depends:
- __osx >=10.13
license: bzip2-1.0.6
license_family: BSD
purls: []
- size: 132607
- timestamp: 1757437730085
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda
- sha256: b456200636bd5fecb2bec63f7e0985ad2097cf1b83d60ce0b6968dffa6d02aa1
- md5: 58fd217444c2a5701a44244faf518206
+ size: 133427
+ timestamp: 1771350680709
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda
+ sha256: 540fe54be35fac0c17feefbdc3e29725cce05d7367ffedfaaa1bdda234b019df
+ md5: 620b85a3f45526a8bc4d23fd78fc22f0
depends:
- __osx >=11.0
license: bzip2-1.0.6
license_family: BSD
purls: []
- size: 125061
- timestamp: 1757437486465
-- conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda
- sha256: d882712855624641f48aa9dc3f5feea2ed6b4e6004585d3616386a18186fe692
- md5: 1077e9333c41ff0be8edd1a5ec0ddace
+ size: 124834
+ timestamp: 1771350416561
+- conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda
+ sha256: 76dfb71df5e8d1c4eded2dbb5ba15bb8fb2e2b0fe42d94145d5eed4c75c35902
+ md5: 4cb8e6b48f67de0b018719cdf1136306
depends:
- ucrt >=10.0.20348.0
- vc >=14.3,<15
@@ -3605,8 +4154,8 @@ packages:
license: bzip2-1.0.6
license_family: BSD
purls: []
- size: 55977
- timestamp: 1757437738856
+ size: 56115
+ timestamp: 1771350256444
- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda
sha256: cc9accf72fa028d31c2a038460787751127317dcfa991f8d1f1babf216bb454e
md5: 920bb03579f15389b9e512095ad995b7
@@ -3638,28 +4187,28 @@ packages:
purls: []
size: 180327
timestamp: 1765215064054
-- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-h4c7d964_0.conda
- sha256: 4ddcb01be03f85d3db9d881407fb13a673372f1b9fac9c836ea441893390e049
- md5: 84d389c9eee640dda3d26fc5335c67d8
+- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-h4c7d964_0.conda
+ sha256: 37950019c59b99585cee5d30dbc2cc9696ed4e11f5742606a4db1621ed8f94d6
+ md5: f001e6e220355b7f87403a4d0e5bf1ca
depends:
- __win
license: ISC
purls: []
- size: 147139
- timestamp: 1767500904211
-- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-hbd8a1cb_0.conda
- sha256: b5974ec9b50e3c514a382335efa81ed02b05906849827a34061c496f4defa0b2
- md5: bddacf101bb4dd0e51811cb69c7790e2
+ size: 147734
+ timestamp: 1772006322223
+- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda
+ sha256: 67cc7101b36421c5913a1687ef1b99f85b5d6868da3abbf6ec1a4181e79782fc
+ md5: 4492fd26db29495f0ba23f146cd5638d
depends:
- __unix
license: ISC
purls: []
- size: 146519
- timestamp: 1767500828366
-- pypi: https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl
+ size: 147413
+ timestamp: 1772006283803
+- pypi: https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl
name: certifi
- version: 2026.1.4
- sha256: 9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c
+ version: 2026.2.25
+ sha256: 027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa
requires_python: '>=3.7'
- pypi: https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl
name: cffi
@@ -3722,35 +4271,70 @@ packages:
version: 3.5.0
sha256: a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.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.4.0.post1
+ sha256: 2769be12361a6c7873392e435c708eca88c9f0fb6a647af75fa1386db64032d6
+ requires_python: '>=3.10'
+- 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.4.0.post1
+ sha256: 8e1eaa942ae81d43d535092ff3ba660c967344178cc3876b54834a56c1207f3a
+ requires_python: '>=3.10'
+- 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.4.0.post1
+ sha256: e6285d35f79d0cdc8838d3cb01876f979c8419a74662e8de39444e40639e0b2b
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/91/d7/47988d40231b41376f5a66346ef3b322c81091dfd4c0f84df5a1e3bb06b5/chardet-7.4.0.post1-py3-none-any.whl
+ name: chardet
+ version: 7.4.0.post1
+ sha256: 57a62ef50f69bc2fb3a3ea1ffffec6d10f3d2112d3b05d6e3cb15c2c9b55f6cc
+ requires_python: '>=3.10'
+- 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.4.0.post1
+ sha256: 329aa8766c4917d3acc1b1d0462f0b2e820e24e9f341d0f858aee85396ae3002
+ requires_python: '>=3.10'
+- 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.4.0.post1
+ sha256: ad98a6c2e61624b1120919353d222121b8f5848b9d33c885d949fe0235575682
+ requires_python: '>=3.10'
+- 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.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
- version: 3.4.4
- sha256: 5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016
+ version: 3.4.6
+ sha256: 11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-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
name: charset-normalizer
- version: 3.4.4
- sha256: 840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381
+ version: 3.4.6
+ sha256: 530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.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.4
- sha256: e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794
+ version: 3.4.6
+ sha256: 9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-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.4
- sha256: b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14
+ version: 3.4.6
+ sha256: 82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_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.4
- sha256: 6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8
+ version: 3.4.6
+ sha256: 572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.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.4
- sha256: a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894
+ 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
@@ -3976,59 +4560,79 @@ packages:
- pytest-xdist ; extra == 'test-no-images'
- wurlitzer ; extra == 'test-no-images'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-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
+ 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.1
- sha256: fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992
+ 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/27/56/c216625f453df6e0559ed666d246fcbaaa93f3aa99eaa5080cea1229aa3d/coverage-7.13.1-cp311-cp311-win_amd64.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.1
- sha256: a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a
+ 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/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-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.1
- sha256: 1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b
+ 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/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl
name: coverage
- version: 7.13.1
- sha256: 5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500
+ 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/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-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.1
- sha256: cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78
+ 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/b4/9b/77baf488516e9ced25fc215a6f75d803493fc3f6a1a1227ac35697910c2a/coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.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.1
- sha256: 1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88
+ 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/d7/cd/7ab01154e6eb79ee2fab76bf4d89e94c6648116557307ee4ebbb85e5c1bf/coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.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.1
- sha256: 4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3
+ 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/f7/7c/347280982982383621d29b8c544cf497ae07ac41e44b1ca4903024131f55/coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl
name: coverage
- version: 7.13.1
- sha256: bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee
+ version: 7.13.5
+ sha256: 258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9
requires_dist:
- tomli ; python_full_version <= '3.11' and extra == 'toml'
requires_python: '>=3.10'
@@ -4041,6 +4645,13 @@ packages:
- scipy
- pycifstar
- matplotlib
+- pypi: https://files.pythonhosted.org/packages/42/d9/27b13bc9419bf5dae02905b348f16ca827646cd76244ddd326f1a8139a6a/cyclebane-24.10.0-py3-none-any.whl
+ name: cyclebane
+ version: 24.10.0
+ sha256: 902dd318667e4a222afc270cc5bc72c67d5d6047d2e0e1c36018885fb80f5e5d
+ requires_dist:
+ - networkx
+ requires_python: '>=3.10'
- pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl
name: cycler
version: 0.12.1
@@ -4061,20 +4672,52 @@ packages:
requires_dist:
- pyobjc-framework-cocoa ; sys_platform == 'darwin' and extra == 'macos-listener'
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.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.3.0
+ sha256: be614b9242b0b38288060fb2d7696125946469c98a1c30e174883fd199e0428d
+ requires_dist:
+ - click>=8.1
+ - cloudpickle>=3.0.0
+ - fsspec>=2021.9.0
+ - packaging>=20.0
+ - partd>=1.4.0
+ - pyyaml>=5.3.1
+ - toolz>=0.12.0
+ - importlib-metadata>=4.13.0 ; python_full_version < '3.12'
+ - numpy>=1.24 ; extra == 'array'
+ - dask[array] ; extra == 'dataframe'
+ - pandas>=2.0 ; extra == 'dataframe'
+ - pyarrow>=16.0 ; extra == 'dataframe'
+ - 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'
+ - pyarrow>=16.0 ; extra == 'complete'
+ - lz4>=4.3.2 ; extra == 'complete'
+ - pandas[test] ; extra == 'test'
+ - pytest ; extra == 'test'
+ - pytest-cov ; extra == 'test'
+ - pytest-mock ; extra == 'test'
+ - pytest-rerunfailures ; extra == 'test'
+ - pytest-timeout ; extra == 'test'
+ - pytest-xdist ; extra == 'test'
+ - pre-commit ; extra == 'test'
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl
name: debugpy
- version: 1.8.19
- sha256: 360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38
+ version: 1.8.20
+ sha256: eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/f2/a8/aaac7ff12ddf5d68a39e13a423a8490426f5f661384f5ad8d9062761bd8e/debugpy-1.8.19-cp311-cp311-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/d5/92/1cb532e88560cbee973396254b21bece8c5d7c2ece958a67afa08c9f10dc/debugpy-1.8.20-cp311-cp311-win_amd64.whl
name: debugpy
- version: 1.8.19
- sha256: 14035cbdbb1fe4b642babcdcb5935c2da3b1067ac211c5c5a8fdc0bb31adbcaa
+ version: 1.8.20
+ sha256: 1f7650546e0eded1902d0f6af28f787fa1f1dbdbc97ddabaf1cd963a405930cb
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/fb/4e/931480b9552c7d0feebe40c73725dd7703dcc578ba9efc14fe0e6d31cfd1/debugpy-1.8.19-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl
name: debugpy
- version: 1.8.19
- sha256: c30639998a9f9cd9699b4b621942c0179a6527f083c72351f95c6ab1728d5b73
+ version: 1.8.20
+ sha256: 5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7
requires_python: '>=3.8'
- pypi: https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl
name: decorator
@@ -4086,10 +4729,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/68/49/869b49db0bad6fba7c7471d612c356e745929d14e0de63acbc53e88f2a50/dfo_ls-1.6-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'
- sha256: 416edce5537237fa417bd27aef5ba91201e856f3daae52ffd44cfacb10f5b771
+ version: 1.6.5
+ sha256: d147d42e471e240f9abf8bc38351a88f555ea6a8fcfd83119bbbf93c36f75ab2
requires_dist:
- setuptools
- numpy
@@ -4157,53 +4800,97 @@ packages:
- numpy
- pycifrw
requires_python: '>=3.11,<3.14'
-- pypi: https://files.pythonhosted.org/packages/79/c8/f0f4ab7fd08950d5358579364a0a9b9198bf03e179a4fcceae4b353be32e/diffpy_utils-3.6.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.6.1
- sha256: b7917926f35a84db81d2950ce44f243d7d4c7494a96a18dc673ce555cde0d2d2
+ version: 3.7.2
+ sha256: 6100600736791a8e4638e3dd476704f4dabe3cab75bcb5c60c83c16a2032519a
requires_dist:
- numpy
- xraydb
- scipy
- requires_python: '>=3.11,<3.14'
-- pypi: https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl
+ 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.0
- sha256: 44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049
+ version: 0.4.1
+ sha256: 1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d
requires_dist:
- objgraph>=1.7.2 ; extra == 'graph'
- gprof2dot>=2022.7.29 ; extra == 'profile'
- requires_python: '>=3.8'
+ requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl
name: distlib
version: 0.4.0
sha256: 9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16
-- 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/82/ee/c9ca3f81d69ec6c99ca41e10432a58c60fef3a6e63cce6df7e796f90fe22/easydiffraction-0.10.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl
+ name: dnspython
+ version: 2.8.0
+ sha256: 01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af
+ requires_dist:
+ - black>=25.1.0 ; extra == 'dev'
+ - coverage>=7.0 ; extra == 'dev'
+ - flake8>=7 ; extra == 'dev'
+ - hypercorn>=0.17.0 ; extra == 'dev'
+ - mypy>=1.17 ; extra == 'dev'
+ - pylint>=3 ; extra == 'dev'
+ - pytest-cov>=6.2.0 ; extra == 'dev'
+ - pytest>=8.4 ; extra == 'dev'
+ - quart-trio>=0.12.0 ; extra == 'dev'
+ - sphinx-rtd-theme>=3.0.0 ; extra == 'dev'
+ - sphinx>=8.2.0 ; extra == 'dev'
+ - twine>=6.1.0 ; extra == 'dev'
+ - wheel>=0.45.0 ; extra == 'dev'
+ - cryptography>=45 ; extra == 'dnssec'
+ - h2>=4.2.0 ; extra == 'doh'
+ - httpcore>=1.0.0 ; extra == 'doh'
+ - httpx>=0.28.0 ; extra == 'doh'
+ - aioquic>=1.2.0 ; extra == 'doq'
+ - idna>=3.10 ; extra == 'idna'
+ - 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/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.0
- sha256: eef4593f36b9cacf6ded23aa2c22b6b3c317d97b5791c4625c4a66ca457a8e3d
+ version: 0.10.2+dev7
+ sha256: 322f1ccfe97af5f9fa88e7c14eb8b8e12221deb7058cef6320de02e52560d1ad
requires_dist:
- asciichartpy
- asteval
- bumps
- colorama
- cryspy
+ - darkdetect
- dfo-ls
- diffpy-pdffit2
- diffpy-utils
+ - essdiffraction
- gemmi
+ - jupyterlab
- lmfit
- - numpy<2.4
+ - numpy
+ - pandas
+ - pixi-kernel
+ - plotly
- pooch
+ - py3dmol
- rich
- scipy
- sympy
@@ -4212,69 +4899,101 @@ packages:
- typer
- uncertainties
- varname
- - build ; extra == 'all'
- - darkdetect ; extra == 'all'
- - docformatter ; extra == 'all'
- - interrogate ; extra == 'all'
- - jinja2 ; extra == 'all'
- - jupyter-dark-detect ; extra == 'all'
- - jupyterquiz ; extra == 'all'
- - jupytext ; extra == 'all'
- - mike ; extra == 'all'
- - mkdocs ; extra == 'all'
- - mkdocs-autorefs<1.3.0 ; 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'
- - docformatter ; extra == 'dev'
+ - copier ; extra == 'dev'
+ - format-docstring ; 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<1.3.0 ; 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'
- - jupyter-dark-detect ; 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
+ sha256: 80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4
+ requires_dist:
+ - dnspython>=2.0.0
+ - idna>=2.0.0
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/55/cd/d729a1bb63fa95387228cc508552dea4685ea0116e484e73238db10f9521/essdiffraction-26.4.0-py3-none-any.whl
+ name: essdiffraction
+ version: 26.4.0
+ sha256: a58c1ac09c2196ae3769ab8c2d1f8c0152e4e1a69a02bc286caf419cdaa9ec1d
+ requires_dist:
+ - dask>=2022.1.0
+ - essreduce>=26.4.0
+ - graphviz
+ - numpy>=2
+ - plopp>=26.2.0
+ - pythreejs>=2.4.1
+ - sciline>=25.4.1
+ - scipp>=25.11.0
+ - scippneutron>=26.3.0
+ - scippnexus>=23.12.0
+ - tof>=25.12.0
+ - ncrystal[cif]>=4.1.0
+ - spglib!=2.7
+ - pandas>=2.1.2 ; extra == 'test'
+ - pooch>=1.5 ; extra == 'test'
+ - pytest>=7.0 ; extra == 'test'
+ - ipywidgets>=8.1.7 ; extra == 'test'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/f1/bb/66c80d7f801b191f7b3ee6149a39be9d1a8a81c233e20adaf796d171f93a/essreduce-26.4.0-py3-none-any.whl
+ name: essreduce
+ version: 26.4.0
+ sha256: 06a9ebf58cba3cc29ac70f7b89a3e596be92d6a61130361b8c19fa8afec2b1b5
+ requires_dist:
+ - sciline>=25.11.0
+ - scipp>=26.3.0
+ - scippneutron>=25.11.1
+ - scippnexus>=25.6.0
+ - graphviz>=0.20 ; extra == 'test'
+ - ipywidgets>=8.1 ; extra == 'test'
+ - matplotlib>=3.10.7 ; extra == 'test'
+ - numba>=0.63 ; extra == 'test'
+ - pooch>=1.9.0 ; extra == 'test'
+ - pytest>=7.0 ; extra == 'test'
+ - scipy>=1.14 ; extra == 'test'
+ - tof>=25.12.0 ; extra == 'test'
+ - autodoc-pydantic ; extra == 'docs'
+ - graphviz>=0.20 ; extra == 'docs'
+ - ipykernel ; extra == 'docs'
+ - ipython!=8.7.0 ; extra == 'docs'
+ - ipywidgets>=8.1 ; extra == 'docs'
+ - myst-parser ; extra == 'docs'
+ - nbsphinx ; extra == 'docs'
+ - numba>=0.63 ; extra == 'docs'
+ - plopp ; extra == 'docs'
+ - pydata-sphinx-theme>=0.14 ; extra == 'docs'
+ - sphinx>=7 ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - sphinx-copybutton ; extra == 'docs'
+ - sphinx-design ; extra == 'docs'
+ - tof>=25.12.0 ; extra == 'docs'
+ requires_python: '>=3.11'
- pypi: https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl
name: execnet
version: 2.1.2
@@ -4311,15 +5030,15 @@ packages:
- pytest-benchmark ; extra == 'devel'
- pytest-cache ; extra == 'devel'
- validictory ; extra == 'devel'
-- pypi: https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-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.20.2
- sha256: fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8
+ version: 3.25.2
+ sha256: ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/07/ad/37dd1ae5fa6e01612a1fbb954f0927681f282925a86e86198ccd7b15d515/fonttools-4.61.1-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.61.1
- sha256: fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7
+ version: 4.62.1
+ sha256: 68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4350,10 +5069,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/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-cp313-cp313-win_amd64.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.61.1
- sha256: 21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b
+ version: 4.62.1
+ sha256: 9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4384,10 +5103,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/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl
+- pypi: https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl
name: fonttools
- version: 4.61.1
- sha256: 8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c
+ version: 4.62.1
+ sha256: 7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4418,10 +5137,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/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-cp313-cp313-macosx_10_13_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.61.1
- sha256: dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e
+ version: 4.62.1
+ sha256: c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4452,10 +5171,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/69/12/bf9f4eaa2fad039356cc627587e30ed008c03f1cebd3034376b5ee8d1d44/fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.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.61.1
- sha256: c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09
+ version: 4.62.1
+ sha256: 40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4486,10 +5205,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/79/61/1ca198af22f7dd22c17ab86e9024ed3c06299cfdb08170640e9996d501a0/fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.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.61.1
- sha256: 75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9
+ version: 4.62.1
+ sha256: 1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4520,10 +5239,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/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl
name: fonttools
- version: 4.61.1
- sha256: 64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5
+ version: 4.62.1
+ sha256: e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4554,10 +5273,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/ac/49/4138d1acb6261499bedde1c07f8c2605d1d8f9d77a151e5507fd3ef084b6/fonttools-4.61.1-cp311-cp311-macosx_10_9_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.61.1
- sha256: 5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37
+ version: 4.62.1
+ sha256: 6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1
requires_dist:
- lxml>=4.0 ; extra == 'lxml'
- brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff'
@@ -4588,6 +5307,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
@@ -4635,46 +5363,158 @@ packages:
version: 1.8.0
sha256: 17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/19/5b/0976c1af0dd59a6850e9ea3b6c6d28f3dff0651c694a6a6192a2933e8feb/gemmi-0.7.4-cp313-cp313-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl
+ name: fsspec
+ version: 2026.2.0
+ sha256: 98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437
+ requires_dist:
+ - adlfs ; extra == 'abfs'
+ - adlfs ; extra == 'adl'
+ - pyarrow>=1 ; extra == 'arrow'
+ - dask ; extra == 'dask'
+ - distributed ; extra == 'dask'
+ - pre-commit ; extra == 'dev'
+ - ruff>=0.5 ; extra == 'dev'
+ - numpydoc ; extra == 'doc'
+ - sphinx ; extra == 'doc'
+ - sphinx-design ; extra == 'doc'
+ - sphinx-rtd-theme ; extra == 'doc'
+ - yarl ; extra == 'doc'
+ - dropbox ; extra == 'dropbox'
+ - dropboxdrivefs ; extra == 'dropbox'
+ - requests ; extra == 'dropbox'
+ - adlfs ; extra == 'full'
+ - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'full'
+ - dask ; extra == 'full'
+ - distributed ; extra == 'full'
+ - dropbox ; extra == 'full'
+ - dropboxdrivefs ; extra == 'full'
+ - fusepy ; extra == 'full'
+ - gcsfs>2024.2.0 ; extra == 'full'
+ - libarchive-c ; extra == 'full'
+ - ocifs ; extra == 'full'
+ - panel ; extra == 'full'
+ - paramiko ; extra == 'full'
+ - pyarrow>=1 ; extra == 'full'
+ - pygit2 ; extra == 'full'
+ - requests ; extra == 'full'
+ - s3fs>2024.2.0 ; extra == 'full'
+ - smbprotocol ; extra == 'full'
+ - tqdm ; extra == 'full'
+ - fusepy ; extra == 'fuse'
+ - gcsfs>2024.2.0 ; extra == 'gcs'
+ - pygit2 ; extra == 'git'
+ - requests ; extra == 'github'
+ - gcsfs ; extra == 'gs'
+ - panel ; extra == 'gui'
+ - pyarrow>=1 ; extra == 'hdfs'
+ - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'http'
+ - libarchive-c ; extra == 'libarchive'
+ - ocifs ; extra == 'oci'
+ - s3fs>2024.2.0 ; extra == 's3'
+ - paramiko ; extra == 'sftp'
+ - smbprotocol ; extra == 'smb'
+ - paramiko ; extra == 'ssh'
+ - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test'
+ - numpy ; extra == 'test'
+ - pytest ; extra == 'test'
+ - pytest-asyncio!=0.22.0 ; extra == 'test'
+ - pytest-benchmark ; extra == 'test'
+ - pytest-cov ; extra == 'test'
+ - pytest-mock ; extra == 'test'
+ - pytest-recording ; extra == 'test'
+ - pytest-rerunfailures ; extra == 'test'
+ - requests ; extra == 'test'
+ - aiobotocore>=2.5.4,<3.0.0 ; extra == 'test-downstream'
+ - dask[dataframe,test] ; extra == 'test-downstream'
+ - moto[server]>4,<5 ; extra == 'test-downstream'
+ - pytest-timeout ; extra == 'test-downstream'
+ - xarray ; extra == 'test-downstream'
+ - adlfs ; extra == 'test-full'
+ - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test-full'
+ - backports-zstd ; python_full_version < '3.14' and extra == 'test-full'
+ - cloudpickle ; extra == 'test-full'
+ - dask ; extra == 'test-full'
+ - distributed ; extra == 'test-full'
+ - dropbox ; extra == 'test-full'
+ - dropboxdrivefs ; extra == 'test-full'
+ - fastparquet ; extra == 'test-full'
+ - fusepy ; extra == 'test-full'
+ - gcsfs ; extra == 'test-full'
+ - jinja2 ; extra == 'test-full'
+ - kerchunk ; extra == 'test-full'
+ - libarchive-c ; extra == 'test-full'
+ - lz4 ; extra == 'test-full'
+ - notebook ; extra == 'test-full'
+ - numpy ; extra == 'test-full'
+ - ocifs ; extra == 'test-full'
+ - pandas<3.0.0 ; extra == 'test-full'
+ - panel ; extra == 'test-full'
+ - paramiko ; extra == 'test-full'
+ - pyarrow ; extra == 'test-full'
+ - pyarrow>=1 ; extra == 'test-full'
+ - pyftpdlib ; extra == 'test-full'
+ - pygit2 ; extra == 'test-full'
+ - pytest ; extra == 'test-full'
+ - pytest-asyncio!=0.22.0 ; extra == 'test-full'
+ - pytest-benchmark ; extra == 'test-full'
+ - pytest-cov ; extra == 'test-full'
+ - pytest-mock ; extra == 'test-full'
+ - pytest-recording ; extra == 'test-full'
+ - pytest-rerunfailures ; extra == 'test-full'
+ - python-snappy ; extra == 'test-full'
+ - requests ; extra == 'test-full'
+ - smbprotocol ; extra == 'test-full'
+ - tqdm ; extra == 'test-full'
+ - urllib3 ; extra == 'test-full'
+ - zarr ; extra == 'test-full'
+ - 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.4
- sha256: d1757e5210fb4244190af782a175787cdd3baa3128f1157cd43d3ccbd3133a60
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/19/f7/1a03aa6b06d26dbd1231b3ac907f8fdfcf02c0b27fc5f4c31493ad6ecdad/gemmi-0.7.4-cp313-cp313-win_amd64.whl
+ version: 0.7.5
+ sha256: 4db34eaa3d3fc102afea7a156330862cbeb82f557444c079403d4412e326c527
+ requires_python: '>=3.9'
+- 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
name: gemmi
- version: 0.7.4
- sha256: ce51f071a69d72c5c242be17e3cd31fbfe7e4a31c86b532c44ea083761389274
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/28/bc/e943898c25121f36625ab4913b8e24d0bdd054a17e380d19924066102574/gemmi-0.7.4-cp311-cp311-macosx_10_14_x86_64.whl
+ version: 0.7.5
+ sha256: 895c63c7bcf30cffba97cf12c89dc3905f4645f838c17009b4534459a6c53a1e
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/7f/79/b13830a65bf9fc85474a984604f094cc18817dc93a784f4c567a2dc05169/gemmi-0.7.5-cp311-cp311-macosx_10_14_x86_64.whl
name: gemmi
- version: 0.7.4
- sha256: 006e3251a1cc70e050460e5b4bd8ab30225feb32dace39a6354806ce77e61e5a
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/3e/e7/b88b72919c910d3233065680cbc74a2a9d00ed65a06b100751d5b78e08e1/gemmi-0.7.4-cp313-cp313-macosx_10_14_x86_64.whl
+ version: 0.7.5
+ sha256: e134fd33f34bf9f2ffacd9e0207aeac6329dde818f62340e7390217a25ee8e2d
+ requires_python: '>=3.9'
+- 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
name: gemmi
- version: 0.7.4
- sha256: 4088c03764d8b5c0202b43e7def0fa53909b93e13b2fea33473953cd64969e2a
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/46/3e/51e7914c8a640548d1b980140b1bd1419c169bee300a556cfd7f4175444d/gemmi-0.7.4-cp311-cp311-macosx_11_0_arm64.whl
+ version: 0.7.5
+ sha256: 750b4d9751aaf1460ac4f0f45308ddced25f47bcf7a30355eb3b1f779f03952a
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/b9/5e/62402bf021183bc6122cb01b8f1be17cac67545713fb30f888f59357a782/gemmi-0.7.5-cp311-cp311-win_amd64.whl
name: gemmi
- version: 0.7.4
- sha256: ee61dd8579fbe19d658806ca42013e780846a407597777db83abbeb68da7100e
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/96/ae/41aff180c36dd3c8f0a84faf38ac8683f8dca99250abcfbc3ed19897290b/gemmi-0.7.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ version: 0.7.5
+ sha256: 06cb44f4e3657b7e3a2b23cd40b67a8e7b5d00bfb92ea94cb4060bd47ba50df6
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/c1/9c/1236dd7d22ed48527286b613c84e3376ea731b65e6734b6e6a0b4d03744c/gemmi-0.7.5-cp313-cp313-macosx_11_0_arm64.whl
name: gemmi
- version: 0.7.4
- sha256: 7ff3f09305f9f7e8c6a4332c8882f3b909b97efc912cd7b376651f4b2e095d42
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/dd/9d/412d75eb7b9c0aa1e939b419a66c7d61471aa387919d4be32893921564af/gemmi-0.7.4-cp311-cp311-win_amd64.whl
+ version: 0.7.5
+ sha256: c7d8b08c33fe6ba375223306149092440c69cbfbd55c3d3e3436e5fb315a225d
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/c4/80/fd758344a72ca7b5e1c5bbdc1d263f3b215d3897941b5f450380445ca0a9/gemmi-0.7.5-cp313-cp313-macosx_10_14_x86_64.whl
name: gemmi
- version: 0.7.4
- sha256: c802b5dc495e53e055c056d5f647dd28f6c0c815d14a778d0cf607db8691c9f3
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/ed/34/a6536afaeee07fa351e2087bf7b5b1522aa703bc1f6e29d53c27a722ac33/gemmi-0.7.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ version: 0.7.5
+ sha256: ef9b6ada1c00c6ba7c7a5b9e938cc3b45d83e775c23d12bf63b6882d5f3cdd6b
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/ee/ab/7d7463cda94f8b68b969ea97aaad679655a0e436efd6a643e528a8de114e/gemmi-0.7.5-cp313-cp313-win_amd64.whl
name: gemmi
- version: 0.7.4
- sha256: aaa1b1a613649db9f9d6c67df6662c8357c3459112842dd7a77203552b795a02
- requires_python: '>=3.8'
+ version: 0.7.5
+ sha256: ad1f72ffa24adbfaf259e11471f6f071a668667f6ca846051f3bfea024fd337d
+ requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl
name: ghp-import
version: 2.1.0
@@ -4685,10 +5525,59 @@ packages:
- markdown ; extra == 'dev'
- flake8 ; extra == 'dev'
- wheel ; extra == 'dev'
-- pypi: https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl
+- 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'
+ sha256: 54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42
+ requires_dist:
+ - build ; extra == 'dev'
+ - wheel ; extra == 'dev'
+ - twine ; extra == 'dev'
+ - flake8 ; extra == 'dev'
+ - flake8-pyproject ; extra == 'dev'
+ - pep8-naming ; extra == 'dev'
+ - tox>=3 ; extra == 'dev'
+ - pytest>=7,<8.1 ; extra == 'test'
+ - pytest-mock>=3 ; extra == 'test'
+ - pytest-cov ; extra == 'test'
+ - coverage ; extra == 'test'
+ - sphinx>=5,<7 ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - sphinx-rtd-theme>=0.2.5 ; extra == 'docs'
+ requires_python: '>=3.9'
+- 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
name: greenlet
- version: 3.3.0
- sha256: a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739
+ version: 3.3.2
+ sha256: 8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358
requires_dist:
- sphinx ; extra == 'docs'
- furo ; extra == 'docs'
@@ -4696,10 +5585,10 @@ packages:
- psutil ; extra == 'test'
- setuptools ; extra == 'test'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/1d/d5/c339b3b4bc8198b7caa4f2bd9fd685ac9f29795816d8db112da3d04175bb/greenlet-3.3.0-cp311-cp311-win_amd64.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
name: greenlet
- version: 3.3.0
- sha256: 7652ee180d16d447a683c04e4c5f6441bae7ba7b17ffd9f6b3aff4605e9e6f71
+ version: 3.3.2
+ sha256: b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab
requires_dist:
- sphinx ; extra == 'docs'
- furo ; extra == 'docs'
@@ -4707,10 +5596,10 @@ packages:
- psutil ; extra == 'test'
- setuptools ; extra == 'test'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/1f/cb/48e964c452ca2b92175a9b2dca037a553036cb053ba69e284650ce755f13/greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl
+- pypi: https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl
name: greenlet
- version: 3.3.0
- sha256: e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e
+ version: 3.3.2
+ sha256: a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124
requires_dist:
- sphinx ; extra == 'docs'
- furo ; extra == 'docs'
@@ -4718,10 +5607,10 @@ packages:
- psutil ; extra == 'test'
- setuptools ; extra == 'test'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl
name: greenlet
- version: 3.3.0
- sha256: 9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38
+ version: 3.3.2
+ sha256: aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4
requires_dist:
- sphinx ; extra == 'docs'
- furo ; extra == 'docs'
@@ -4729,10 +5618,10 @@ packages:
- psutil ; extra == 'test'
- setuptools ; extra == 'test'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/dc/a6/e959a127b630a58e23529972dbc868c107f9d583b5a9f878fb858c46bc1a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/f1/3a/efb2cf697fbccdf75b24e2c18025e7dfa54c4f31fab75c51d0fe79942cef/greenlet-3.3.2-cp311-cp311-win_amd64.whl
name: greenlet
- version: 3.3.0
- sha256: 6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948
+ version: 3.3.2
+ sha256: 1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5
requires_dist:
- sphinx ; extra == 'docs'
- furo ; extra == 'docs'
@@ -4740,10 +5629,10 @@ packages:
- psutil ; extra == 'test'
- setuptools ; extra == 'test'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl
name: greenlet
- version: 3.3.0
- sha256: 087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527
+ version: 3.3.2
+ sha256: c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86
requires_dist:
- sphinx ; extra == 'docs'
- furo ; extra == 'docs'
@@ -4751,12 +5640,11 @@ packages:
- psutil ; extra == 'test'
- setuptools ; extra == 'test'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl
- name: griffe
- version: 1.15.0
- sha256: 6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3
+- pypi: https://files.pythonhosted.org/packages/4b/4c/cc8c68196db727cfc1432f2ad5de50aa6707e630d44b2e6361dc06d8f134/griffelib-2.0.1-py3-none-any.whl
+ name: griffelib
+ version: 2.0.1
+ sha256: b769eed581c0e857d362fc8fcd8e57ecd2330c124b6104ac8b4c1c86d76970aa
requires_dist:
- - colorama>=0.4
- pip>=24.0 ; extra == 'pypi'
- platformdirs>=4.2 ; extra == 'pypi'
- wheel>=0.42 ; extra == 'pypi'
@@ -4817,59 +5705,59 @@ packages:
version: 0.16.0
sha256: 63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/23/95/499b4e56452ef8b6c95a271af0dde08dac4ddb70515a75f346d4f400579b/h5py-3.15.1-cp311-cp311-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl
name: h5py
- version: 3.15.1
- sha256: 550e51131376889656feec4aff2170efc054a7fe79eb1da3bb92e1625d1ac878
+ version: 3.16.0
+ sha256: 370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/31/96/ba99a003c763998035b0de4c299598125df5fc6c9ccf834f152ddd60e0fb/h5py-3.15.1-cp313-cp313-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl
name: h5py
- version: 3.15.1
- sha256: ab2219dbc6fcdb6932f76b548e2b16f34a1f52b7666e998157a4dfc02e2c4123
+ version: 3.16.0
+ sha256: fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/41/fd/8349b48b15b47768042cff06ad6e1c229f0a4bd89225bf6b6894fea27e6d/h5py-3.15.1-cp311-cp311-macosx_10_9_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl
name: h5py
- version: 3.15.1
- sha256: 5aaa330bcbf2830150c50897ea5dcbed30b5b6d56897289846ac5b9e529ec243
+ version: 3.16.0
+ sha256: 42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/88/b3/40207e0192415cbff7ea1d37b9f24b33f6d38a5a2f5d18a678de78f967ae/h5py-3.15.1-cp313-cp313-macosx_10_13_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl
name: h5py
- version: 3.15.1
- sha256: c8440fd8bee9500c235ecb7aa1917a0389a2adb80c209fa1cc485bd70e0d94a5
+ version: 3.16.0
+ sha256: 719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/8b/23/4ab1108e87851ccc69694b03b817d92e142966a6c4abd99e17db77f2c066/h5py-3.15.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl
name: h5py
- version: 3.15.1
- sha256: 5b849ba619a066196169763c33f9f0f02e381156d61c03e000bb0100f9950faf
+ version: 3.16.0
+ sha256: 9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/c1/b0/1c628e26a0b95858f54aba17e1599e7f6cd241727596cc2580b72cb0a9bf/h5py-3.15.1-cp311-cp311-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl
name: h5py
- version: 3.15.1
- sha256: c970fb80001fffabb0109eaf95116c8e7c0d3ca2de854e0901e8a04c1f098509
+ version: 3.16.0
+ sha256: c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/d9/69/4402ea66272dacc10b298cca18ed73e1c0791ff2ae9ed218d3859f9698ac/h5py-3.15.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl
name: h5py
- version: 3.15.1
- sha256: 121b2b7a4c1915d63737483b7bff14ef253020f617c2fb2811f67a4bed9ac5e8
+ version: 3.16.0
+ sha256: 18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/e5/ea/fbb258a98863f99befb10ed727152b4ae659f322e1d9c0576f8a62754e81/h5py-3.15.1-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl
name: h5py
- version: 3.15.1
- sha256: dea78b092fd80a083563ed79a3171258d4a4d307492e7cf8b2313d464c82ba52
+ version: 3.16.0
+ sha256: 17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999
requires_dist:
- numpy>=1.21.2
requires_python: '>=3.10'
@@ -4903,57 +5791,45 @@ 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-75.1-he02047a_0.conda
- sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e
- md5: 8b189310083baabfb622af68fd9d3ae3
+- 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-ng >=12
- - libstdcxx-ng >=12
- license: MIT
- license_family: MIT
- purls: []
- size: 12129203
- timestamp: 1720853576813
-- conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda
- sha256: 2e64307532f482a0929412976c8450c719d558ba20c0962832132fd0d07ba7a7
- md5: d68d48a3060eb5abdc1cdc8e2a3a5966
- depends:
- - __osx >=10.13
+ - libgcc >=14
+ - libstdcxx >=14
license: MIT
license_family: MIT
purls: []
- size: 11761697
- timestamp: 1720853679409
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda
- sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620
- md5: 5eb22c1d7b3fc4abb50d92d621583137
+ 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 >=11.0
license: MIT
license_family: MIT
purls: []
- size: 11857802
- timestamp: 1720853997952
-- conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.1-h637d24d_0.conda
- sha256: bee083d5a0f05c380fcec1f30a71ef5518b23563aeb0a21f6b60b792645f9689
- md5: cb8048bed35ef01431184d6a88e46b3e
+ size: 12273764
+ timestamp: 1773822733780
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda
+ sha256: 3a7907a17e9937d3a46dfd41cffaf815abad59a569440d1e25177c15fd0684e5
+ md5: f1182c91c0de31a7abd40cedf6a5ebef
depends:
- - ucrt >=10.0.20348.0
- - vc >=14.3,<15
- - vc14_runtime >=14.44.35208
+ - __osx >=11.0
license: MIT
license_family: MIT
purls: []
- size: 13849749
- timestamp: 1766299627069
-- pypi: https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-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.15
- sha256: 1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757
+ version: 2.6.18
+ sha256: 8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737
requires_dist:
- ukkonen ; extra == 'license'
- requires_python: '>=3.9'
+ requires_python: '>=3.10'
- pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl
name: idna
version: '3.11'
@@ -4964,18 +5840,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'
@@ -4983,34 +5857,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'
-- pypi: https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl
- name: importlib-resources
- version: 6.5.2
- sha256: 789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec
- requires_dist:
- - zipp>=3.1.0 ; python_full_version < '3.10'
- - pytest>=6,!=8.1.* ; extra == 'test'
- - zipp>=3.17 ; 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'
- - furo ; extra == 'doc'
- - sphinx-lint ; extra == 'doc'
- - jaraco-tidelift>=1.4 ; extra == 'doc'
- - pytest-checkdocs>=2.4 ; extra == 'check'
- - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check'
- - pytest-cov ; extra == 'cover'
- - pytest-enabler>=2.2 ; extra == 'enabler'
- - pytest-mypy ; 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
@@ -5044,23 +5896,38 @@ packages:
- pytest-mock ; extra == 'tests'
- coverage[toml] ; extra == 'tests'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/f6/d8/502954a4ec0efcf264f99b65b41c3c54e65a647d9f0d6f62cd02227d242c/ipykernel-6.31.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/f1/5b/e63c877c4c94382b66de5045e08ec8cd960e8a4d22f0d62a4dfb1f9e5ac6/ipydatawidgets-4.3.5-py2.py3-none-any.whl
+ name: ipydatawidgets
+ version: 4.3.5
+ sha256: d590cdb7c364f2f6ab346f20b9d2dd661d27a834ef7845bc9d7113118f05ec87
+ requires_dist:
+ - ipywidgets>=7.0.0
+ - numpy
+ - traittypes>=0.2.0
+ - sphinx ; extra == 'docs'
+ - recommonmark ; extra == 'docs'
+ - sphinx-rtd-theme ; extra == 'docs'
+ - pytest>=4 ; extra == 'test'
+ - pytest-cov ; extra == 'test'
+ - nbval>=0.9.2 ; extra == 'test'
+ requires_python: '>=3.7'
+- 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'
@@ -5069,8 +5936,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'
@@ -5082,12 +5949,12 @@ 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'
-- pypi: https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.0-py3-none-any.whl
+ - 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.9.0
- sha256: b457fe9165df2b84e8ec909a97abcf2ed88f565970efba16b1f7229c283d252b
+ version: 9.10.0
+ sha256: c6ab68cc23bba8c7e18e9b932797014cc61ea7fd6f19de180ab9ba73e65ee58d
requires_dist:
- colorama>=0.4.4 ; sys_platform == 'win32'
- decorator>=4.3.2
@@ -5130,6 +5997,52 @@ packages:
- ipython[doc,matplotlib,terminal,test,test-extra] ; extra == 'all'
- argcomplete>=3.0 ; extra == 'all'
requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/b2/90/45c72becc57158facc6a6404f663b77bbcea2519ca57f760e2879ae1315d/ipython-9.11.0-py3-none-any.whl
+ name: ipython
+ version: 9.11.0
+ sha256: 6922d5bcf944c6e525a76a0a304451b60a2b6f875e86656d8bc2dfda5d710e19
+ requires_dist:
+ - colorama>=0.4.4 ; sys_platform == 'win32'
+ - decorator>=5.1.0
+ - ipython-pygments-lexers>=1.0.0
+ - jedi>=0.18.2
+ - matplotlib-inline>=0.1.6
+ - pexpect>4.6 ; sys_platform != 'emscripten' and sys_platform != 'win32'
+ - prompt-toolkit>=3.0.41,<3.1.0
+ - pygments>=2.14.0
+ - stack-data>=0.6.0
+ - traitlets>=5.13.0
+ - black ; extra == 'black'
+ - docrepr ; extra == 'doc'
+ - exceptiongroup ; extra == 'doc'
+ - intersphinx-registry ; extra == 'doc'
+ - ipykernel ; extra == 'doc'
+ - ipython[matplotlib,test] ; extra == 'doc'
+ - setuptools>=80.0 ; extra == 'doc'
+ - sphinx-toml==0.0.4 ; extra == 'doc'
+ - sphinx-rtd-theme>=0.1.8 ; extra == 'doc'
+ - sphinx>=8.0 ; extra == 'doc'
+ - typing-extensions ; extra == 'doc'
+ - pytest>=7.0.0 ; extra == 'test'
+ - pytest-asyncio>=1.0.0 ; extra == 'test'
+ - testpath>=0.2 ; extra == 'test'
+ - packaging>=23.0.0 ; extra == 'test'
+ - setuptools>=80.0 ; extra == 'test'
+ - ipython[test] ; extra == 'test-extra'
+ - curio ; extra == 'test-extra'
+ - jupyter-ai ; extra == 'test-extra'
+ - ipython[matplotlib] ; extra == 'test-extra'
+ - nbformat ; extra == 'test-extra'
+ - nbclient ; extra == 'test-extra'
+ - ipykernel>6.30 ; extra == 'test-extra'
+ - numpy>=2.0 ; extra == 'test-extra'
+ - pandas>2.1 ; extra == 'test-extra'
+ - trio>=0.22.0 ; extra == 'test-extra'
+ - matplotlib>3.9 ; extra == 'matplotlib'
+ - ipython[doc,matplotlib,terminal,test,test-extra] ; extra == 'all'
+ - argcomplete>=3.0 ; extra == 'all'
+ - types-decorator ; extra == 'all'
+ requires_python: '>=3.12'
- pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl
name: ipython-pygments-lexers
version: 1.1.1
@@ -5137,6 +6050,22 @@ packages:
requires_dist:
- pygments
requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl
+ name: ipywidgets
+ version: 8.1.8
+ sha256: ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e
+ requires_dist:
+ - comm>=0.1.3
+ - ipython>=6.1.0
+ - traitlets>=4.3.1
+ - widgetsnbextension~=4.0.14
+ - jupyterlab-widgets~=3.0.15
+ - jsonschema ; extra == 'test'
+ - ipykernel ; extra == 'test'
+ - pytest>=3.6.0 ; extra == 'test'
+ - pytest-cov ; extra == 'test'
+ - pytz ; extra == 'test'
+ requires_python: '>=3.7'
- pypi: https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl
name: isoduration
version: 20.11.0
@@ -5192,25 +6121,34 @@ 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'
-- pypi: https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl
+ 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.25.1
- sha256: 3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63
+ version: 4.26.0
+ sha256: d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce
requires_dist:
- attrs>=22.2.0
- jsonschema-specifications>=2023.3.6
- referencing>=0.28.4
- - rpds-py>=0.7.1
+ - rpds-py>=0.25.0
- fqdn ; extra == 'format'
- idna ; extra == 'format'
- isoduration ; extra == 'format'
@@ -5228,7 +6166,7 @@ packages:
- rfc3987-syntax>=1.1.0 ; extra == 'format-nongpl'
- uri-template ; extra == 'format-nongpl'
- webcolors>=24.6.0 ; extra == 'format-nongpl'
- requires_python: '>=3.9'
+ requires_python: '>=3.10'
- pypi: https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl
name: jsonschema-specifications
version: 2025.9.1
@@ -5236,10 +6174,10 @@ packages:
requires_dist:
- referencing>=0.31.0
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/bb/f5/fddaec430367be9d62a7ed125530e133bfd4a1c0350fe221149ee0f2b526/jupyter_client-8.7.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl
name: jupyter-client
- version: 8.7.0
- sha256: 3671a94fd25e62f5f2f554f5e95389c2294d89822378a5f2dd24353e1494a9e0
+ version: 8.8.0
+ sha256: f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a
requires_dist:
- jupyter-core>=5.1
- python-dateutil>=2.8.2
@@ -5253,10 +6191,12 @@ packages:
- sphinx>=4 ; extra == 'docs'
- sphinxcontrib-github-alt ; extra == 'docs'
- sphinxcontrib-spelling ; extra == 'docs'
+ - orjson ; extra == 'orjson'
- anyio ; extra == 'test'
- coverage ; extra == 'test'
- ipykernel>=6.14 ; extra == 'test'
- - mypy ; extra == 'test'
+ - msgpack ; extra == 'test'
+ - mypy ; platform_python_implementation != 'PyPy' and extra == 'test'
- paramiko ; sys_platform == 'win32' and extra == 'test'
- pre-commit ; extra == 'test'
- pytest ; extra == 'test'
@@ -5283,13 +6223,6 @@ packages:
- pytest-timeout ; extra == 'test'
- pytest<9 ; extra == 'test'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/76/c0/8cc378aad5ccfcc37adeda27d3864f20ea808ad5b63572f5b986a42a649c/jupyter_dark_detect-0.1.0-py3-none-any.whl
- name: jupyter-dark-detect
- version: 0.1.0
- sha256: 198a7fa1270c6ce1ac2fd056cea9300e018bb8d04211de4c946cbe9e03e7a89d
- requires_dist:
- - ipython>=7.0.0
- requires_python: '>=3.7'
- pypi: https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl
name: jupyter-events
version: 0.12.0
@@ -5325,6 +6258,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
@@ -5373,10 +6311,10 @@ packages:
- pytest>=7.0,<9 ; extra == 'test'
- requests ; extra == 'test'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl
name: jupyter-server-terminals
- version: 0.5.3
- sha256: 41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa
+ version: 0.5.4
+ sha256: 55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14
requires_dist:
- pywinpty>=2.0.3 ; os_name == 'nt'
- terminado>=0.8.3
@@ -5397,10 +6335,10 @@ packages:
- pytest-timeout ; extra == 'test'
- pytest>=7.0 ; extra == 'test'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/af/c3/acced767eecc11a70c65c45295db5396c4f0c1937874937d5a76d7b177b6/jupyterlab-4.5.1-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.1
- sha256: 31b059de96de0754ff1f2ce6279774b6aab8c34d7082e9752db58207c99bd514
+ version: 4.5.6
+ sha256: d6b3dac883aa4d9993348e0f8e95b24624f75099aed64eab6a4351a9cdd1e580
requires_dist:
- async-lru>=1.0.0
- httpx>=0.25.0,<1
@@ -5501,14 +6439,19 @@ packages:
- strict-rfc3339 ; extra == 'test'
- werkzeug ; extra == 'test'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/f7/5a/488f492b49b712ca38326e12c78d0d48ab6be682a2989ce533f0f2c4dfd2/jupyterquiz-2.9.6.2-py2.py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl
+ name: jupyterlab-widgets
+ version: 3.0.16
+ sha256: 45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8
+ requires_python: '>=3.7'
+- pypi: https://files.pythonhosted.org/packages/b3/52/bc858b1665d0dec3a2511f4e6f5c18ea85c0977563d624d597c95d6d0fd7/jupyterquiz-2.9.6.4-py2.py3-none-any.whl
name: jupyterquiz
- version: 2.9.6.2
- sha256: f60f358c809d06a38c423c804740c1113c8840e130227aef50694a9201474bef
-- pypi: https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl
+ version: 2.9.6.4
+ sha256: f8c4418f6c827454523fc882a30d744b585cb58ac1ae277769c3059d04fc272b
+- pypi: https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl
name: jupytext
- version: 1.18.1
- sha256: 24f999400726a1c658beae55e15fdd2a6255ab1a418697864cd779874e6011ab
+ version: 1.19.1
+ sha256: d8975035155d034bdfde5c0c37891425314b7ea8d3a6c4b5d18c294348714cd9
requires_dist:
- markdown-it-py>=1.0
- mdit-py-plugins
@@ -5524,6 +6467,7 @@ packages:
- isort ; extra == 'dev'
- jupyter-fs[fs]>=1.0 ; extra == 'dev'
- jupyter-server!=2.11 ; extra == 'dev'
+ - marimo>=0.17.6,<=0.19.4 ; extra == 'dev'
- nbconvert ; extra == 'dev'
- pre-commit ; extra == 'dev'
- pytest ; extra == 'dev'
@@ -5558,6 +6502,7 @@ packages:
- isort ; extra == 'test-external'
- jupyter-fs[fs]>=1.0 ; extra == 'test-external'
- jupyter-server!=2.11 ; extra == 'test-external'
+ - marimo>=0.17.6,<=0.19.4 ; extra == 'test-external'
- nbconvert ; extra == 'test-external'
- pre-commit ; extra == 'test-external'
- pytest ; extra == 'test-external'
@@ -5581,45 +6526,45 @@ packages:
- pytest-xdist ; extra == 'test-integration'
- bash-kernel ; extra == 'test-ui'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl
name: kiwisolver
- version: 1.4.9
- sha256: 1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f
+ version: 1.5.0
+ sha256: 0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl
name: kiwisolver
- version: 1.4.9
- sha256: 2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543
+ version: 1.5.0
+ sha256: 3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
name: kiwisolver
- version: 1.4.9
- sha256: be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2
+ version: 1.5.0
+ sha256: 332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
name: kiwisolver
- version: 1.4.9
- sha256: dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61
+ version: 1.5.0
+ sha256: 2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl
name: kiwisolver
- version: 1.4.9
- sha256: dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d
+ version: 1.5.0
+ sha256: dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl
name: kiwisolver
- version: 1.4.9
- sha256: 39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089
+ version: 1.5.0
+ sha256: beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl
name: kiwisolver
- version: 1.4.9
- sha256: efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2
+ version: 1.5.0
+ sha256: 0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl
name: kiwisolver
- version: 1.4.9
- sha256: b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098
+ version: 1.5.0
+ sha256: c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3
requires_python: '>=3.10'
- pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl
name: lark
@@ -5631,132 +6576,141 @@ packages:
- atomicwrites ; extra == 'atomic-cache'
- interegular>=0.3.1,<0.4.0 ; extra == 'interegular'
requires_python: '>=3.8'
-- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda
- sha256: 1027bd8aa0d5144e954e426ab6218fd5c14e54a98f571985675468b339c808ca
- md5: 3ec0aa5037d39b06554109a01e6fb0c6
+- pypi: https://files.pythonhosted.org/packages/8a/a1/8d812e53a5da1687abb10445275d41a8b13adb781bbf7196ddbcf8d88505/lazy_loader-0.5-py3-none-any.whl
+ name: lazy-loader
+ version: '0.5'
+ sha256: ab0ea149e9c554d4ffeeb21105ac60bed7f3b4fd69b1d2360a4add51b170b005
+ requires_dist:
+ - packaging
+ - pytest>=8.0 ; extra == 'test'
+ - pytest-cov>=5.0 ; extra == 'test'
+ - coverage[toml]>=7.2 ; extra == 'test'
+ - pre-commit==4.3.0 ; extra == 'lint'
+ - 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_102.conda
+ sha256: 3d584956604909ff5df353767f3a2a2f60e07d070b328d109f30ac40cd62df6c
+ md5: 18335a698559cdbcd86150a48bf54ba6
depends:
- __glibc >=2.17,<3.0.a0
- zstd >=1.5.7,<1.6.0a0
constrains:
- - binutils_impl_linux-64 2.45
+ - binutils_impl_linux-64 2.45.1
license: GPL-3.0-only
license_family: GPL
purls: []
- size: 730831
- timestamp: 1766513089214
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda
- sha256: dcd1429a1782864c452057a6c5bc1860f2b637dc20a2b7e6eacd57395bbceff8
- md5: 83b160d4da3e1e847bf044997621ed63
+ size: 728002
+ timestamp: 1774197446916
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda
+ sha256: a7a4481a4d217a3eadea0ec489826a69070fcc3153f00443aa491ed21527d239
+ md5: 6f7b4302263347698fd24565fbf11310
depends:
- __glibc >=2.17,<3.0.a0
- - libgcc >=13
- - libstdcxx >=13
+ - libgcc >=14
+ - libstdcxx >=14
constrains:
- - libabseil-static =20250512.1=cxx17*
- - abseil-cpp =20250512.1
+ - libabseil-static =20260107.1=cxx17*
+ - abseil-cpp =20260107.1
license: Apache-2.0
license_family: Apache
purls: []
- size: 1310612
- timestamp: 1750194198254
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20250512.1-cxx17_hfc00f1c_0.conda
- sha256: a878efebf62f039a1f1733c1e150a75a99c7029ece24e34efdf23d56256585b1
- md5: ddf1acaed2276c7eb9d3c76b49699a11
+ size: 1384817
+ timestamp: 1770863194876
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20260107.1-cxx17_h7ed6875_0.conda
+ sha256: 2b4ff36082ddfbacc47ac6e11d4dd9f3403cd109ce8d7f0fbee0cdd47cdef013
+ md5: 317f40d7bd7bf6d54b56d4a5b5f5085d
depends:
- __osx >=10.13
- - libcxx >=18
+ - libcxx >=19
constrains:
- - abseil-cpp =20250512.1
- - libabseil-static =20250512.1=cxx17*
+ - libabseil-static =20260107.1=cxx17*
+ - abseil-cpp =20260107.1
license: Apache-2.0
license_family: Apache
purls: []
- size: 1162435
- timestamp: 1750194293086
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250512.1-cxx17_hd41c47c_0.conda
- sha256: 7f0ee9ae7fa2cf7ac92b0acf8047c8bac965389e48be61bf1d463e057af2ea6a
- md5: 360dbb413ee2c170a0a684a33c4fc6b8
+ size: 1217836
+ timestamp: 1770863510112
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda
+ sha256: 756611fbb8d2957a5b4635d9772bd8432cb6ddac05580a6284cca6fdc9b07fca
+ md5: bb65152e0d7c7178c0f1ee25692c9fd1
depends:
- __osx >=11.0
- - libcxx >=18
+ - libcxx >=19
constrains:
- - libabseil-static =20250512.1=cxx17*
- - abseil-cpp =20250512.1
+ - abseil-cpp =20260107.1
+ - libabseil-static =20260107.1=cxx17*
license: Apache-2.0
license_family: Apache
purls: []
- size: 1174081
- timestamp: 1750194620012
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda
- build_number: 5
- sha256: 18c72545080b86739352482ba14ba2c4815e19e26a7417ca21a95b76ec8da24c
- md5: c160954f7418d7b6e87eaf05a8913fa9
+ 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.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
@@ -5856,86 +6810,82 @@ 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
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda
- sha256: cbd8e821e97436d8fc126c24b50df838b05ba4c80494fbb93ccaf2e3b2d109fb
- md5: 9f8a60a77ecafb7966ca961c94f33bd1
+ size: 68221
+ timestamp: 1774503722413
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.2-h19cb2f5_0.conda
+ sha256: 46561199545890e050a8a90edcfce984e5f881da86b09388926e3a6c6b759dec
+ md5: ed6f7b7a35f942a0301e581d72616f7d
depends:
- - __osx >=10.13
+ - __osx >=11.0
license: Apache-2.0 WITH LLVM-exception
license_family: Apache
purls: []
- size: 569777
- timestamp: 1765919624323
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda
- sha256: 82e228975fd491bcf1071ecd0a6ec2a0fcc5f57eb0bd1d52cb13a18d57c67786
- md5: 780f0251b757564e062187044232c2b7
+ 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: 569118
- timestamp: 1765919724254
+ size: 568094
+ timestamp: 1774439202359
- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda
sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4
md5: 172bf1cd1ff8629f2b1179945ed45055
@@ -5962,91 +6912,91 @@ packages:
purls: []
size: 107458
timestamp: 1702146414478
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda
- sha256: 1e1b08f6211629cbc2efe7a5bca5953f8f6b3cae0eeb04ca4dacee1bd4e2db2f
- md5: 8b09ae86839581147ef2e5c5e229d164
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda
+ sha256: d78f1d3bea8c031d2f032b760f36676d87929b18146351c4464c66b0869df3f5
+ md5: e7f7ce06ec24cfcfb9e36d28cf82ba57
depends:
- __glibc >=2.17,<3.0.a0
- libgcc >=14
constrains:
- - expat 2.7.3.*
+ - expat 2.7.4.*
license: MIT
license_family: MIT
purls: []
- size: 76643
- timestamp: 1763549731408
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda
- sha256: d11b3a6ce5b2e832f430fd112084533a01220597221bee16d6c7dc3947dffba6
- md5: 222e0732a1d0780a622926265bee14ef
+ size: 76798
+ timestamp: 1771259418166
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.4-h991f03e_0.conda
+ sha256: 8d9d79b2de7d6f335692391f5281607221bf5d040e6724dad4c4d77cd603ce43
+ md5: a684eb8a19b2aa68fde0267df172a1e3
depends:
- __osx >=10.13
constrains:
- - expat 2.7.3.*
+ - expat 2.7.4.*
license: MIT
license_family: MIT
purls: []
- size: 74058
- timestamp: 1763549886493
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda
- sha256: fce22610ecc95e6d149e42a42fbc3cc9d9179bd4eb6232639a60f06e080eec98
- md5: b79875dbb5b1db9a4a22a4520f918e1a
+ size: 74578
+ timestamp: 1771260142624
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.4-hf6b4638_0.conda
+ sha256: 03887d8080d6a8fe02d75b80929271b39697ecca7628f0657d7afaea87761edf
+ md5: a92e310ae8dfc206ff449f362fc4217f
depends:
- __osx >=11.0
constrains:
- - expat 2.7.3.*
+ - expat 2.7.4.*
license: MIT
license_family: MIT
purls: []
- size: 67800
- timestamp: 1763549994166
-- conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda
- sha256: 844ab708594bdfbd7b35e1a67c379861bcd180d6efe57b654f482ae2f7f5c21e
- md5: 8c9e4f1a0e688eef2e95711178061a0f
+ size: 68199
+ timestamp: 1771260020767
+- conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.4-hac47afa_0.conda
+ sha256: b31f6fb629c4e17885aaf2082fb30384156d16b48b264e454de4a06a313b533d
+ md5: 1c1ced969021592407f16ada4573586d
depends:
- ucrt >=10.0.20348.0
- vc >=14.3,<15
- vc14_runtime >=14.44.35208
constrains:
- - expat 2.7.3.*
+ - expat 2.7.4.*
license: MIT
license_family: MIT
purls: []
- size: 70137
- timestamp: 1763550049107
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda
- sha256: 25cbdfa65580cfab1b8d15ee90b4c9f1e0d72128f1661449c9a999d341377d54
- md5: 35f29eec58405aaf55e01cb470d8c26a
+ size: 70323
+ timestamp: 1771259521393
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda
+ sha256: 31f19b6a88ce40ebc0d5a992c131f57d919f73c0b92cd1617a5bec83f6e961e6
+ md5: a360c33a5abe61c07959e449fa1453eb
depends:
- __glibc >=2.17,<3.0.a0
- libgcc >=14
license: MIT
license_family: MIT
purls: []
- size: 57821
- timestamp: 1760295480630
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda
- sha256: 277dc89950f5d97f1683f26e362d6dca3c2efa16cb2f6fdb73d109effa1cd3d0
- md5: d214916b24c625bcc459b245d509f22e
+ size: 58592
+ timestamp: 1769456073053
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda
+ sha256: 951958d1792238006fdc6fce7f71f1b559534743b26cc1333497d46e5903a2d6
+ md5: 66a0dc7464927d0853b590b6f53ba3ea
depends:
- __osx >=10.13
license: MIT
license_family: MIT
purls: []
- size: 52573
- timestamp: 1760295626449
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda
- sha256: 9b8acdf42df61b7bfe8bdc545c016c29e61985e79748c64ad66df47dbc2e295f
- md5: 411ff7cd5d1472bba0f55c0faf04453b
+ size: 53583
+ timestamp: 1769456300951
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda
+ sha256: 6686a26466a527585e6a75cc2a242bf4a3d97d6d6c86424a441677917f28bec7
+ md5: 43c04d9cb46ef176bb2a4c77e324d599
depends:
- __osx >=11.0
license: MIT
license_family: MIT
purls: []
- size: 40251
- timestamp: 1760295839166
-- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda
- sha256: ddff25aaa4f0aa535413f5d831b04073789522890a4d8626366e43ecde1534a3
- md5: ba4ad812d2afc22b9a34ce8327a0930f
+ size: 40979
+ timestamp: 1769456747661
+- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda
+ sha256: 59d01f2dfa8b77491b5888a5ab88ff4e1574c9359f7e229da254cdfe27ddc190
+ md5: 720b39f5ec0610457b725eb3f396219a
depends:
- ucrt >=10.0.20348.0
- vc >=14.3,<15
@@ -6054,97 +7004,97 @@ packages:
license: MIT
license_family: MIT
purls: []
- size: 44866
- timestamp: 1760295760649
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda
- sha256: 6eed58051c2e12b804d53ceff5994a350c61baf117ec83f5f10c953a3f311451
- md5: 6d0363467e6ed84f11435eb309f2ff06
+ size: 45831
+ timestamp: 1769456418774
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda
+ sha256: faf7d2017b4d718951e3a59d081eb09759152f93038479b768e3d612688f83f5
+ md5: 0aa00f03f9e39fb9876085dee11a85d4
depends:
- __glibc >=2.17,<3.0.a0
- _openmp_mutex >=4.5
constrains:
- - libgcc-ng ==15.2.0=*_16
- - libgomp 15.2.0 he0feb66_16
+ - libgcc-ng ==15.2.0=*_18
+ - libgomp 15.2.0 he0feb66_18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 1042798
- timestamp: 1765256792743
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda
- sha256: e04b115ae32f8cbf95905971856ff557b296511735f4e1587b88abf519ff6fb8
- md5: c816665789d1e47cdfd6da8a81e1af64
+ size: 1041788
+ timestamp: 1771378212382
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_18.conda
+ sha256: 83366f11615ab234aa1e0797393f9e07b78124b5a24c4a9f8af0113d02df818e
+ md5: 9a5cb96e43f5c2296690186e15b3296f
depends:
- _openmp_mutex
constrains:
- - libgomp 15.2.0 15
- - libgcc-ng ==15.2.0=*_15
+ - libgcc-ng ==15.2.0=*_18
+ - libgomp 15.2.0 18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 422960
- timestamp: 1764839601296
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda
- sha256: 646c91dbc422fe92a5f8a3a5409c9aac66549f4ce8f8d1cab7c2aa5db789bb69
- md5: 8b216bac0de7a9d60f3ddeba2515545c
+ size: 423025
+ timestamp: 1771378225170
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_18.conda
+ sha256: 1d9c4f35586adb71bcd23e31b68b7f3e4c4ab89914c26bed5f2859290be5560e
+ md5: 92df6107310b1fff92c4cc84f0de247b
depends:
- _openmp_mutex
constrains:
- - libgcc-ng ==15.2.0=*_16
- - libgomp 15.2.0 16
+ - libgcc-ng ==15.2.0=*_18
+ - libgomp 15.2.0 18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 402197
- timestamp: 1765258985740
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda
- sha256: 5f07f9317f596a201cc6e095e5fc92621afca64829785e483738d935f8cab361
- md5: 5a68259fac2da8f2ee6f7bfe49c9eb8b
+ size: 401974
+ timestamp: 1771378877463
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda
+ sha256: e318a711400f536c81123e753d4c797a821021fb38970cebfb3f454126016893
+ md5: d5e96b1ed75ca01906b3d2469b4ce493
depends:
- - libgcc 15.2.0 he0feb66_16
+ - libgcc 15.2.0 he0feb66_18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 27256
- timestamp: 1765256804124
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda
- sha256: 8a7b01e1ee1c462ad243524d76099e7174ebdd94ff045fe3e9b1e58db196463b
- md5: 40d9b534410403c821ff64f00d0adc22
+ 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_16
+ - libgfortran5 15.2.0 h68bc16d_18
constrains:
- - libgfortran-ng ==15.2.0=*_16
+ - libgfortran-ng ==15.2.0=*_18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 27215
- timestamp: 1765256845586
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda
- sha256: 7bb4d51348e8f7c1a565df95f4fc2a2021229d42300aab8366eda0ea1af90587
- md5: a089323fefeeaba2ae60e1ccebf86ddc
+ size: 27523
+ timestamp: 1771378269450
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_18.conda
+ sha256: fb06c2a2ef06716a0f2a6550f5d13cdd1d89365993068512b7ae3c34e6e665d9
+ md5: 34a9f67498721abcfef00178bcf4b190
depends:
- - libgfortran5 15.2.0 hd16e46c_15
+ - libgfortran5 15.2.0 hd16e46c_18
constrains:
- - libgfortran-ng ==15.2.0=*_15
+ - libgfortran-ng ==15.2.0=*_18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 139002
- timestamp: 1764839892631
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda
- sha256: 68a6c1384d209f8654112c4c57c68c540540dd8e09e17dd1facf6cf3467798b5
- md5: 11e09edf0dde4c288508501fe621bab4
+ size: 139761
+ timestamp: 1771378423828
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_18.conda
+ sha256: 63f89087c3f0c8621c5c89ecceec1e56e5e1c84f65fc9c5feca33a07c570a836
+ md5: 26981599908ed2205366e8fc91b37fc6
depends:
- - libgfortran5 15.2.0 hdae7583_16
+ - libgfortran5 15.2.0 hdae7583_18
constrains:
- - libgfortran-ng ==15.2.0=*_16
+ - libgfortran-ng ==15.2.0=*_18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 138630
- timestamp: 1765259217400
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda
- sha256: d0e974ebc937c67ae37f07a28edace978e01dc0f44ee02f29ab8a16004b8148b
- md5: 39183d4e0c05609fd65f130633194e37
+ 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
@@ -6153,11 +7103,11 @@ packages:
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 2480559
- timestamp: 1765256819588
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda
- sha256: 456385a7d3357d5fdfc8e11bf18dcdf71753c4016c440f92a2486057524dd59a
- md5: c2a6149bf7f82774a0118b9efef966dd
+ size: 2482475
+ timestamp: 1771378241063
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_18.conda
+ sha256: ddaf9dcf008c031b10987991aa78643e03c24a534ad420925cbd5851b31faa11
+ md5: ca52daf58cea766656266c8771d8be81
depends:
- libgcc >=15.2.0
constrains:
@@ -6165,11 +7115,11 @@ packages:
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 1061950
- timestamp: 1764839609607
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda
- sha256: 9fb7f4ff219e3fb5decbd0ee90a950f4078c90a86f5d8d61ca608c913062f9b0
- md5: 265a9d03461da24884ecc8eb58396d57
+ size: 1062274
+ timestamp: 1771378232014
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_18.conda
+ sha256: 91033978ba25e6a60fb86843cf7e1f7dc8ad513f9689f991c9ddabfaf0361e7e
+ md5: c4a6f7989cffb0544bfd9207b6789971
depends:
- libgcc >=15.2.0
constrains:
@@ -6177,21 +7127,21 @@ packages:
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 598291
- timestamp: 1765258993165
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda
- sha256: 5b3e5e4e9270ecfcd48f47e3a68f037f5ab0f529ccb223e8e5d5ac75a58fc687
- md5: 26c46f90d0e727e95c6c9498a33a09f3
+ size: 598634
+ timestamp: 1771378886363
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda
+ sha256: 21337ab58e5e0649d869ab168d4e609b033509de22521de1bfed0c031bfc5110
+ md5: 239c5e9546c38a1e884d69effcf4c882
depends:
- __glibc >=2.17,<3.0.a0
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 603284
- timestamp: 1765256703881
-- conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.conda
- sha256: 2d534c09f92966b885acb3f4a838f7055cea043165a03079a539b06c54e20a49
- md5: d1699ce4fe195a9f61264a1c29b87035
+ 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
@@ -6202,8 +7152,8 @@ packages:
license: BSD-3-Clause
license_family: BSD
purls: []
- size: 2412642
- timestamp: 1765090345611
+ size: 2411241
+ timestamp: 1765104337762
- conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda
sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7
md5: 64571d1dd6cdcfa25d0664a5950fdaa2
@@ -6215,145 +7165,145 @@ packages:
purls: []
size: 696926
timestamp: 1754909290005
-- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda
- sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8
- md5: 1a580f7796c7bf6393fddb8bbbde58dc
+- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda
+ sha256: 755c55ebab181d678c12e49cced893598f2bab22d582fbbf4d8b83c18be207eb
+ md5: c7c83eecbb72d88b940c249af56c8b17
depends:
- __glibc >=2.17,<3.0.a0
- - libgcc >=13
+ - libgcc >=14
constrains:
- - xz 5.8.1.*
+ - xz 5.8.2.*
license: 0BSD
purls: []
- size: 112894
- timestamp: 1749230047870
-- conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda
- sha256: 7e22fd1bdb8bf4c2be93de2d4e718db5c548aa082af47a7430eb23192de6bb36
- md5: 8468beea04b9065b9807fc8b9cdc5894
+ size: 113207
+ timestamp: 1768752626120
+- conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.2-h11316ed_0.conda
+ sha256: 7ab3c98abd3b5d5ec72faa8d9f5d4b50dcee4970ed05339bc381861199dabb41
+ md5: 688a0c3d57fa118b9c97bf7e471ab46c
depends:
- __osx >=10.13
constrains:
- - xz 5.8.1.*
+ - xz 5.8.2.*
license: 0BSD
purls: []
- size: 104826
- timestamp: 1749230155443
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda
- sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285
- md5: d6df911d4564d77c4374b02552cb17d1
+ size: 105482
+ timestamp: 1768753411348
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.2-h8088a28_0.conda
+ sha256: 7bfc7ffb2d6a9629357a70d4eadeadb6f88fa26ebc28f606b1c1e5e5ed99dc7e
+ md5: 009f0d956d7bfb00de86901d16e486c7
depends:
- __osx >=11.0
constrains:
- - xz 5.8.1.*
+ - xz 5.8.2.*
license: 0BSD
purls: []
- size: 92286
- timestamp: 1749230283517
-- conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda
- sha256: 55764956eb9179b98de7cc0e55696f2eff8f7b83fc3ebff5e696ca358bca28cc
- md5: c15148b2e18da456f5108ccb5e411446
+ size: 92242
+ timestamp: 1768752982486
+- conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.2-hfd05255_0.conda
+ sha256: f25bf293f550c8ed2e0c7145eb404324611cfccff37660869d97abf526eb957c
+ md5: ba0bfd4c3cf73f299ffe46ff0eaeb8e3
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:
- - xz 5.8.1.*
+ - xz 5.8.2.*
license: 0BSD
purls: []
- size: 104935
- timestamp: 1749230611612
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda
- sha256: 3aa92d4074d4063f2a162cd8ecb45dccac93e543e565c01a787e16a43501f7ee
- md5: c7e925f37e3b40d893459e625f6a53f1
+ size: 106169
+ timestamp: 1768752763559
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda
+ sha256: fe171ed5cf5959993d43ff72de7596e8ac2853e9021dec0344e583734f1e0843
+ md5: 2c21e66f50753a083cbe6b80f38268fa
depends:
- __glibc >=2.17,<3.0.a0
- - libgcc >=13
+ - libgcc >=14
license: BSD-2-Clause
license_family: BSD
purls: []
- size: 91183
- timestamp: 1748393666725
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda
- sha256: 98299c73c7a93cd4f5ff8bb7f43cd80389f08b5a27a296d806bdef7841cc9b9e
- md5: 18b81186a6adb43f000ad19ed7b70381
+ size: 92400
+ timestamp: 1769482286018
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda
+ sha256: 1096c740109386607938ab9f09a7e9bca06d86770a284777586d6c378b8fb3fd
+ md5: ec88ba8a245855935b871a7324373105
depends:
- __osx >=10.13
license: BSD-2-Clause
license_family: BSD
purls: []
- size: 77667
- timestamp: 1748393757154
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda
- sha256: 0a1875fc1642324ebd6c4ac864604f3f18f57fbcf558a8264f6ced028a3c75b2
- md5: 85ccccb47823dd9f7a99d2c7f530342f
+ size: 79899
+ timestamp: 1769482558610
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda
+ sha256: 1089c7f15d5b62c622625ec6700732ece83be8b705da8c6607f4dabb0c4bd6d2
+ md5: 57c4be259f5e0b99a5983799a228ae55
depends:
- __osx >=11.0
license: BSD-2-Clause
license_family: BSD
purls: []
- size: 71829
- timestamp: 1748393749336
-- conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda
- sha256: fc529fc82c7caf51202cc5cec5bb1c2e8d90edbac6d0a4602c966366efe3c7bf
- md5: 74860100b2029e2523cf480804c76b9b
+ size: 73690
+ timestamp: 1769482560514
+- conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda
+ sha256: 40dcd0b9522a6e0af72a9db0ced619176e7cfdb114855c7a64f278e73f8a7514
+ md5: e4a9fc2bba3b022dad998c78856afe47
depends:
- ucrt >=10.0.20348.0
- - vc >=14.2,<15
- - vc14_runtime >=14.29.30139
+ - vc >=14.3,<15
+ - vc14_runtime >=14.44.35208
license: BSD-2-Clause
license_family: BSD
purls: []
- size: 88657
- timestamp: 1723861474602
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda
- sha256: a4a7dab8db4dc81c736e9a9b42bdfd97b087816e029e221380511960ac46c690
- md5: b499ce4b026493a13774bcf0f4c33849
+ size: 89411
+ timestamp: 1769482314283
+- 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
@@ -6365,116 +7315,105 @@ 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_3.conda
- sha256: dcc626c7103503d1dfc0371687ad553cb948b8ed0249c2a721147bdeb8db4a73
- md5: a18a7f471c517062ee71b843ef95eb8a
+ 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: 4285762
- timestamp: 1761749506256
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.conda
- sha256: 5ef162b2a1390d1495a759734afe2312a358a58441cf8f378be651903646f3b7
- md5: ad1fd565aff83b543d726382c0ab0af2
+ size: 4308797
+ timestamp: 1774472508546
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda
+ sha256: d716847b7deca293d2e49ed1c8ab9e4b9e04b9d780aea49a97c26925b28a7993
+ md5: fd893f6a3002a635b5e50ceb9dd2c0f4
depends:
- __glibc >=2.17,<3.0.a0
+ - icu >=78.2,<79.0a0
- libgcc >=14
- libzlib >=1.3.1,<2.0a0
license: blessing
purls: []
- size: 940686
- timestamp: 1766319628770
-- conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hb99441e_1.conda
- sha256: d62dcce2ecf555864db393fa0fdc0492f9f644c0435f516d0ba4c5f9f934234b
- md5: ec7a2bad1b422f3966e4776442adb05c
+ size: 951405
+ timestamp: 1772818874251
+- conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.52.0-h77d7759_0.conda
+ sha256: f500d1cd50cfcd288d02b8fc3c3b7ecf8de6fec7b86e57ea058def02908e4231
+ md5: d553eb96758e038b04027b30fe314b2d
depends:
- - __osx >=10.13
+ - __osx >=11.0
- libzlib >=1.3.1,<2.0a0
license: blessing
purls: []
- size: 987257
- timestamp: 1766319833399
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda
- sha256: f2c3cbf2ca7d697098964a748fbf19d6e4adcefa23844ec49f0166f1d36af83c
- md5: 8c3951797658e10b610929c3e57e9ad9
+ size: 996526
+ timestamp: 1772819669038
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.52.0-h1ae2325_0.conda
+ sha256: beb0fd5594d6d7c7cd42c992b6bb4d66cbb39d6c94a8234f15956da99a04306c
+ md5: f6233a3fddc35a2ec9f617f79d6f3d71
depends:
- __osx >=11.0
+ - icu >=78.2,<79.0a0
- libzlib >=1.3.1,<2.0a0
license: blessing
purls: []
- size: 905861
- timestamp: 1766319901587
-- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.conda
- sha256: d6d86715a1afe11f626b7509935e9d2e14a4946632c0ac474526e20fc6c55f99
- md5: be65be5f758709fc01b01626152e96b0
+ size: 918420
+ timestamp: 1772819478684
+- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.52.0-hf5d6505_0.conda
+ sha256: 5fccf1e4e4062f8b9a554abf4f9735a98e70f82e2865d0bfdb47b9de94887583
+ md5: 8830689d537fda55f990620680934bb1
depends:
- ucrt >=10.0.20348.0
- vc >=14.3,<15
- vc14_runtime >=14.44.35208
license: blessing
purls: []
- size: 1292859
- timestamp: 1766319616777
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda
- sha256: 813427918316a00c904723f1dfc3da1bbc1974c5cfe1ed1e704c6f4e0798cbc6
- md5: 68f68355000ec3f1d6f26ea13e8f525f
+ size: 1297302
+ timestamp: 1772818899033
+- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda
+ sha256: 78668020064fdaa27e9ab65cd2997e2c837b564ab26ce3bf0e58a2ce1a525c6e
+ md5: 1b08cd684f34175e4514474793d44bcb
depends:
- __glibc >=2.17,<3.0.a0
- - libgcc 15.2.0 he0feb66_16
+ - libgcc 15.2.0 he0feb66_18
constrains:
- - libstdcxx-ng ==15.2.0=*_16
- license: GPL-3.0-only WITH GCC-exception-3.1
- license_family: GPL
- purls: []
- size: 5856456
- timestamp: 1765256838573
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda
- sha256: 81f2f246c7533b41c5e0c274172d607829019621c4a0823b5c0b4a8c7028ee84
- md5: 1b3152694d236cf233b76b8c56bf0eae
- depends:
- - libstdcxx 15.2.0 h934c35e_16
+ - libstdcxx-ng ==15.2.0=*_18
license: GPL-3.0-only WITH GCC-exception-3.1
license_family: GPL
purls: []
- size: 27300
- timestamp: 1765256885128
+ size: 5852330
+ timestamp: 1771378262446
- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda
sha256: 1a7539cfa7df00714e8943e18de0b06cceef6778e420a5ee3a2a145773758aee
md5: db409b7c1720428638e7c0d509d3e1b5
@@ -6538,133 +7477,133 @@ packages:
purls: []
size: 100393
timestamp: 1702724383534
-- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda
- sha256: 8b47d5fb00a6ccc0f495d16787ab5f37a434d51965584d6000966252efecf56d
- md5: 68dc154b8d415176c07b6995bd3a65d9
+- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.2-h5d26750_0.conda
+ sha256: f905eb7046987c336122121759e7f09144729f6898f48cd06df2a945b86998d8
+ md5: 1007e1bfe181a2aee214779ee7f13d30
depends:
- - icu >=78.1,<79.0a0
- libiconv >=1.18,<2.0a0
- - liblzma >=5.8.1,<6.0a0
- - libxml2-16 2.15.1 h3cfd58e_1
+ - 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: 43387
- timestamp: 1766327259710
-- conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.1-h3cfd58e_1.conda
- sha256: a857e941156b7f462063e34e086d212c6ccbc1521ebdf75b9ed66bd90add57dc
- md5: 07d73826fde28e7dbaec52a3297d7d26
+ 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:
- - icu >=78.1,<79.0a0
- libiconv >=1.18,<2.0a0
- - liblzma >=5.8.1,<6.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.1
+ - libxml2 2.15.2
+ - icu <0.0a0
license: MIT
license_family: MIT
purls: []
- size: 518964
- timestamp: 1766327232819
-- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda
- sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4
- md5: edb0dca6bc32e4f4789199455a1dbeb8
+ size: 520078
+ timestamp: 1772704728534
+- 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-21.1.8-h472b3d1_0.conda
- sha256: 2a41885f44cbc1546ff26369924b981efa37a29d20dc5445b64539ba240739e6
- md5: e2d811e9f464dd67398b4ce1f9c7c872
+ 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 >=10.13
+ - __osx >=11.0
constrains:
- - openmp 21.1.8|21.1.8.*
- intel-openmp <0.0a0
+ - openmp 22.1.1|22.1.1.*
license: Apache-2.0 WITH LLVM-exception
license_family: APACHE
purls: []
- size: 311405
- timestamp: 1765965194247
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda
- sha256: 56bcd20a0a44ddd143b6ce605700fdf876bcf5c509adc50bf27e76673407a070
- md5: 206ad2df1b5550526e386087bef543c7
+ 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 21.1.8|21.1.8.*
- intel-openmp <0.0a0
+ - openmp 22.1.1|22.1.1.*
license: Apache-2.0 WITH LLVM-exception
license_family: APACHE
purls: []
- size: 285974
- timestamp: 1765964756583
-- conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda
- sha256: 145c4370abe870f10987efa9fc15a8383f1dab09abbc9ad4ff15a55d45658f7b
- md5: 0d8b425ac862bcf17e4b28802c9351cb
+ 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
- - openmp 21.1.8|21.1.8.*
license: Apache-2.0 WITH LLVM-exception
license_family: APACHE
purls: []
- size: 347566
- timestamp: 1765964942856
+ 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
@@ -6700,6 +7639,11 @@ packages:
- pytest-cov ; extra == 'test'
- lmfit[dev,doc,test] ; extra == 'all'
requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl
+ name: locket
+ version: 1.0.0
+ sha256: b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3
+ requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*'
- pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl
name: mando
version: 0.7.1
@@ -6709,17 +7653,17 @@ packages:
- argparse ; python_full_version < '2.7'
- funcsigs ; python_full_version < '3.3'
- rst2ansi ; extra == 'restructuredtext'
-- pypi: https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl
name: markdown
- version: '3.10'
- sha256: b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c
+ version: 3.10.2
+ sha256: e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36
requires_dist:
- coverage ; extra == 'testing'
- pyyaml ; extra == 'testing'
- mkdocs>=1.6 ; extra == 'docs'
- mkdocs-nature>=0.6 ; extra == 'docs'
- mdx-gh-links>=0.2 ; extra == 'docs'
- - mkdocstrings[python] ; extra == 'docs'
+ - mkdocstrings[python]>=0.28.3 ; extra == 'docs'
- mkdocs-gen-files ; extra == 'docs'
- mkdocs-section-index ; extra == 'docs'
- mkdocs-literate-nav ; extra == 'docs'
@@ -6985,19 +7929,19 @@ packages:
version: 1.3.4
sha256: 70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/fd/1a/31b7cd6e4e7a02df4e076162e9783620777592bea9e4bb036389389af99d/mike-2.1.3-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl
name: mike
- version: 2.1.3
- sha256: d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a
+ version: 2.1.4
+ sha256: 39933e992e155dd70f2297e749a0ed78d8fd7942bc33a3666195d177758a280e
requires_dist:
- - importlib-metadata
- - importlib-resources
- jinja2>=2.7
- mkdocs>=1.0
- pyparsing>=3.0
- pyyaml>=5.1
- pyyaml-env-tag
- verspec
+ - importlib-metadata ; python_full_version < '3.10'
+ - importlib-resources ; python_full_version < '3.10'
- coverage ; extra == 'dev'
- flake8-quotes ; extra == 'dev'
- flake8>=3.0 ; extra == 'dev'
@@ -7049,31 +7993,31 @@ packages:
- pyyaml==5.1 ; extra == 'min-versions'
- watchdog==2.0 ; extra == 'min-versions'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/71/26/4d39d52ea2219604053a4d05b98e90d6a335511cc01806436ec4886b1028/mkdocs_autorefs-1.2.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl
name: mkdocs-autorefs
- version: 1.2.0
- sha256: d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f
+ version: 1.4.4
+ sha256: 834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089
requires_dist:
- markdown>=3.3
- markupsafe>=2.0.1
- mkdocs>=1.1
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl
+ requires_python: '>=3.9'
+- 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.0
- sha256: 2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
+ 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.8'
-- pypi: https://files.pythonhosted.org/packages/08/37/5f1fd5c3f6954b3256f8126275e62af493b96fb6aef6c0dbc4ee326032ad/mkdocs_jupyter-0.25.1-py3-none-any.whl
+ requires_python: '>=3.9'
+- 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
@@ -7088,10 +8032,10 @@ packages:
- mkdocs
- pyyaml
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-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.1
- sha256: 3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c
+ version: 9.7.6
+ sha256: 71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba
requires_dist:
- babel>=2.10
- backrefs>=5.7.post1
@@ -7099,18 +8043,18 @@ 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
- requests>=2.30
- - mkdocs-git-committers-plugin-2>=1.1,<3 ; extra == 'git'
- - mkdocs-git-revision-date-localized-plugin~=1.2,>=1.2.4 ; extra == 'git'
- - cairosvg~=2.6 ; extra == 'imaging'
- - pillow>=10.2,<12.0 ; extra == 'imaging'
- - mkdocs-minify-plugin~=0.7 ; extra == 'recommended'
- - mkdocs-redirects~=1.2 ; extra == 'recommended'
- - mkdocs-rss-plugin~=1.6 ; extra == 'recommended'
+ - mkdocs-git-committers-plugin-2>=1.1 ; extra == 'git'
+ - mkdocs-git-revision-date-localized-plugin>=1.2.4 ; extra == 'git'
+ - cairosvg>=2.6 ; extra == 'imaging'
+ - pillow>=10.2 ; extra == 'imaging'
+ - mkdocs-minify-plugin>=0.7 ; extra == 'recommended'
+ - mkdocs-redirects>=1.2 ; extra == 'recommended'
+ - mkdocs-rss-plugin>=1.6 ; extra == 'recommended'
requires_python: '>=3.8'
- pypi: https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl
name: mkdocs-material-extensions
@@ -7124,39 +8068,36 @@ packages:
requires_dist:
- mkdocs
requires_python: '>=3.5'
-- pypi: https://files.pythonhosted.org/packages/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/04/41/1cf02e3df279d2dd846a1bf235a928254eba9006dd22b4a14caa71aed0f7/mkdocstrings-1.0.3-py3-none-any.whl
name: mkdocstrings
- version: 0.27.0
- sha256: 6ceaa7ea830770959b55a16203ac63da24badd71325b96af950e59fd37366332
+ version: 1.0.3
+ sha256: 0d66d18430c2201dc7fe85134277382baaa15e6b30979f3f3bdbabd6dbdb6046
requires_dist:
- - click>=7.0
- - jinja2>=2.11.1
+ - jinja2>=3.1
- markdown>=3.6
- markupsafe>=1.1
- - mkdocs>=1.4
- - mkdocs-autorefs>=1.2
- - platformdirs>=2.2
+ - mkdocs>=1.6
+ - mkdocs-autorefs>=1.4
- pymdown-extensions>=6.3
- - importlib-metadata>=4.6 ; python_full_version < '3.10'
- - typing-extensions>=4.1 ; python_full_version < '3.10'
- mkdocstrings-crystal>=0.3.4 ; extra == 'crystal'
- mkdocstrings-python-legacy>=0.2.1 ; extra == 'python-legacy'
- - mkdocstrings-python>=0.5.2 ; extra == 'python'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/51/23/d02d86553327296c3bf369d444194ea83410cce8f0e690565264f37f3261/mkdocstrings_python-1.13.0-py3-none-any.whl
+ - mkdocstrings-python>=1.16.2 ; extra == 'python'
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl
name: mkdocstrings-python
- version: 1.13.0
- sha256: b88bbb207bab4086434743849f8e796788b373bd32e7bfefbf8560ac45d88f97
+ version: 2.0.3
+ sha256: 0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12
requires_dist:
- - mkdocstrings>=0.26
- - mkdocs-autorefs>=1.2
- - griffe>=0.49
- requires_python: '>=3.9'
-- conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_455.conda
- sha256: b2b4c84b95210760e4d12319416c60ab66e03674ccdcbd14aeb59f82ebb1318d
- md5: fd05d1e894497b012d05a804232254ed
+ - mkdocstrings>=0.30
+ - mkdocs-autorefs>=1.4
+ - 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 >=21.1.8
+ - llvm-openmp >=22.1.1
- tbb >=2022.3.0
- ucrt >=10.0.20348.0
- vc >=14.3,<15
@@ -7164,8 +8105,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
@@ -7173,6 +8114,15 @@ packages:
requires_dist:
- jinja2
- matplotlib
+- pypi: https://files.pythonhosted.org/packages/12/aa/fb2a0649fdeef5ab7072d221e8f4df164098792c813af6c87e2581cfa860/mpltoolbox-26.2.0-py3-none-any.whl
+ name: mpltoolbox
+ version: 26.2.0
+ sha256: cd2668db4216fc4d7c2ba37974961aa61445f1517527b645b6082930e35ba7f0
+ requires_dist:
+ - matplotlib
+ - ipympl ; extra == 'test'
+ - pytest>=8.0 ; extra == 'test'
+ requires_python: '>=3.11'
- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl
name: mpmath
version: 1.3.0
@@ -7298,68 +8248,68 @@ packages:
- tomli-w ; extra == 'toml'
- pyyaml ; extra == 'yaml'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_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
name: multidict
- version: 6.7.0
- sha256: 14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721
+ version: 6.7.1
+ sha256: 439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl
name: multidict
- version: 6.7.0
- sha256: 95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81
+ version: 6.7.1
+ sha256: 935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl
name: multidict
- version: 6.7.0
- sha256: 30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390
+ version: 6.7.1
+ sha256: 844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.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
name: multidict
- version: 6.7.0
- sha256: 4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6
+ version: 6.7.1
+ sha256: 9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl
name: multidict
- version: 6.7.0
- sha256: 7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159
+ version: 6.7.1
+ sha256: 960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl
name: multidict
- version: 6.7.0
- sha256: 3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd
+ version: 6.7.1
+ sha256: 84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl
name: multidict
- version: 6.7.0
- sha256: 9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32
+ version: 6.7.1
+ sha256: bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-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
name: multidict
- version: 6.7.0
- sha256: 9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca
+ version: 6.7.1
+ sha256: f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855
requires_dist:
- typing-extensions>=4.1.0 ; python_full_version < '3.11'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.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.15.0
- sha256: cbfe21ca19d260d9fd67f995ec75c44592d1f106933b03ddd375df7ac841f9d6
+ version: 2.18.1
+ sha256: a0a8bb80205323851338888ba3a12b4f65d352362c8a94be591244faf36504ad
requires_dist:
- - cudf>=24.10.0 ; extra == 'cudf'
+ - cudf-cu12>=24.10.0 ; extra == 'cudf'
- dask[dataframe]>=2024.8 ; extra == 'dask'
- duckdb>=1.1 ; extra == 'duckdb'
- ibis-framework>=6.0.0 ; extra == 'ibis'
@@ -7372,6 +8322,8 @@ packages:
- pyarrow>=13.0.0 ; extra == 'pyarrow'
- pyspark>=3.5.0 ; extra == 'pyspark'
- pyspark[connect]>=3.5.0 ; extra == 'pyspark-connect'
+ - duckdb>=1.1 ; extra == 'sql'
+ - sqlparse ; extra == 'sql'
- sqlframe>=3.22.0,!=3.39.3 ; extra == 'sqlframe'
requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl
@@ -7412,10 +8364,10 @@ packages:
- testpath ; extra == 'test'
- xmltodict ; extra == 'test'
requires_python: '>=3.10.0'
-- pypi: https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/0d/4b/8d5f796a792f8a25f6925a96032f098789f448571eb92011df1ae59e8ea8/nbconvert-7.17.0-py3-none-any.whl
name: nbconvert
- version: 7.16.6
- sha256: 1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b
+ version: 7.17.0
+ sha256: 4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518
requires_dist:
- beautifulsoup4
- bleach[css]!=5.0.0
@@ -7433,6 +8385,7 @@ packages:
- pygments>=2.4.1
- traitlets>=5.1
- flaky ; extra == 'all'
+ - intersphinx-registry ; extra == 'all'
- ipykernel ; extra == 'all'
- ipython ; extra == 'all'
- ipywidgets>=7.5 ; extra == 'all'
@@ -7442,15 +8395,16 @@ packages:
- pydata-sphinx-theme ; extra == 'all'
- pyqtwebengine>=5.15 ; extra == 'all'
- pytest>=7 ; extra == 'all'
- - sphinx==5.0.2 ; extra == 'all'
+ - sphinx>=5.0.2 ; extra == 'all'
- sphinxcontrib-spelling ; extra == 'all'
- tornado>=6.1 ; extra == 'all'
+ - intersphinx-registry ; extra == 'docs'
- ipykernel ; extra == 'docs'
- ipython ; extra == 'docs'
- myst-parser ; extra == 'docs'
- nbsphinx>=0.2.12 ; extra == 'docs'
- pydata-sphinx-theme ; extra == 'docs'
- - sphinx==5.0.2 ; extra == 'docs'
+ - sphinx>=5.0.2 ; extra == 'docs'
- sphinxcontrib-spelling ; extra == 'docs'
- pyqtwebengine>=5.15 ; extra == 'qtpdf'
- pyqtwebengine>=5.15 ; extra == 'qtpng'
@@ -7460,7 +8414,7 @@ packages:
- ipywidgets>=7.5 ; extra == 'test'
- pytest>=7 ; extra == 'test'
- playwright ; extra == 'webpdf'
- requires_python: '>=3.8'
+ requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl
name: nbformat
version: 5.10.4
@@ -7510,12 +8464,71 @@ packages:
- pyupgrade ; extra == 'toolchain'
- ruff ; extra == 'toolchain'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/12/e5/838eb1004fb9b6f0383c47c0d902eb698fda052e8e64ca48c70a2144a48c/nbstripout-0.8.2-py2.py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/20/16/e777eadfa0c0305878c36fae1d5e6db474fbb15dae202b9ec378809dfb4d/nbstripout-0.9.1-py3-none-any.whl
name: nbstripout
- version: 0.8.2
- sha256: 5f06f9138cb64daed3e91c5359ff0fff85bab4d0db7d72723be1da6f51b890ae
+ version: 0.9.1
+ sha256: ca027ee45742ee77e4f8e9080254f9a707f1161ba11367b82fdf4a29892c759e
requires_dist:
- nbformat
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/fc/39/dfdef4cfbfcf7c80cd144b1b2e262c2e785f18b152eeebe14ecd70ee7455/ncrystal-4.2.12-py3-none-any.whl
+ name: ncrystal
+ version: 4.2.12
+ sha256: 45c414c786cab7e64cf6c69c89e8d49da08f83833b7e27f382366bd5fe7b8582
+ requires_dist:
+ - ncrystal-core==4.2.12
+ - ncrystal-python==4.2.12
+ - spglib>=2.1.0 ; extra == 'composer'
+ - ase>=3.23.0 ; extra == 'cif'
+ - gemmi>=0.6.1 ; extra == 'cif'
+ - spglib>=2.1.0 ; extra == 'cif'
+ - endf-parserpy>=0.14.3 ; extra == 'endf'
+ - matplotlib>=3.6.0 ; extra == 'plot'
+ - ase>=3.23.0 ; extra == 'all'
+ - endf-parserpy>=0.14.3 ; extra == 'all'
+ - gemmi>=0.6.1 ; extra == 'all'
+ - matplotlib>=3.6.0 ; extra == 'all'
+ - spglib>=2.1.0 ; extra == 'all'
+ - pyyaml>=6.0.0 ; extra == 'devel'
+ - ase>=3.23.0 ; extra == 'devel'
+ - cppcheck ; extra == 'devel'
+ - endf-parserpy>=0.14.3 ; extra == 'devel'
+ - gemmi>=0.6.1 ; extra == 'devel'
+ - matplotlib>=3.6.0 ; extra == 'devel'
+ - mpmath>=1.3.0 ; extra == 'devel'
+ - numpy>=1.22 ; extra == 'devel'
+ - pybind11>=2.11.0 ; extra == 'devel'
+ - ruff>=0.8.1 ; extra == 'devel'
+ - simple-build-system>=1.6.0 ; extra == 'devel'
+ - spglib>=2.1.0 ; extra == 'devel'
+ - tomli>=2.0.0 ; python_full_version < '3.11' and extra == 'devel'
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/55/8d/2b26572e909238bb114d50fb0d1b6b54eb6dafa2d83a7264f18146796b0d/ncrystal_core-4.2.12-py3-none-macosx_11_0_arm64.whl
+ name: ncrystal-core
+ version: 4.2.12
+ sha256: d47a1dd3c3348bdc0b7cb8df19ad4c51eeb4befce1279f56a67ca1d773e5d84c
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/7b/51/e13a37a8d924feefb444d7eb42094750ba1bba756cbb8c1f9a523414c4fb/ncrystal_core-4.2.12-py3-none-win_amd64.whl
+ name: ncrystal-core
+ version: 4.2.12
+ sha256: a04c835c20f5ef5e1869282e921e0eae7665d99c48d27dbe4cb78c0cda0a52cc
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/ab/ee/0d9d9218d2081e56828194f83d0eac6292b7182708fd07a62756c66f7194/ncrystal_core-4.2.12-py3-none-macosx_10_9_x86_64.whl
+ name: ncrystal-core
+ version: 4.2.12
+ sha256: 8ccf39338f5745334e88036f3df885008294ad1228e5b3ffc6ff4bfd1b01dd99
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/e3/8b/1f02771d91ceafec996cef7f92f6a24010fedc47fd9404f8e11772d8501c/ncrystal_core-4.2.12-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+ name: ncrystal-core
+ version: 4.2.12
+ sha256: dd622af09c422973c7effc5bbb6f64d7ad5dc020010564362f6c9eebf99f00da
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/78/98/20fdd6825d1876fc54bb098509e736073d20f569b033f2f23bf74aa1df9c/ncrystal_python-4.2.12-py3-none-any.whl
+ name: ncrystal-python
+ version: 4.2.12
+ sha256: 60bb715d9d74bae031de25858d33665a4c407a5ecaa4c5626cb42bcf182400bb
+ requires_dist:
+ - numpy>=1.22
requires_python: '>=3.8'
- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda
sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586
@@ -7550,92 +8563,131 @@ packages:
version: 1.6.0
sha256: 87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c
requires_python: '>=3.5'
+- pypi: https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl
+ name: networkx
+ version: 3.6.1
+ sha256: d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762
+ requires_dist:
+ - asv ; extra == 'benchmarking'
+ - virtualenv ; extra == 'benchmarking'
+ - numpy>=1.25 ; extra == 'default'
+ - scipy>=1.11.2 ; extra == 'default'
+ - matplotlib>=3.8 ; extra == 'default'
+ - pandas>=2.0 ; extra == 'default'
+ - pre-commit>=4.1 ; extra == 'developer'
+ - mypy>=1.15 ; extra == 'developer'
+ - sphinx>=8.0 ; extra == 'doc'
+ - pydata-sphinx-theme>=0.16 ; extra == 'doc'
+ - sphinx-gallery>=0.18 ; extra == 'doc'
+ - numpydoc>=1.8.0 ; extra == 'doc'
+ - pillow>=10 ; extra == 'doc'
+ - texext>=0.6.7 ; extra == 'doc'
+ - myst-nb>=1.1 ; extra == 'doc'
+ - intersphinx-registry ; extra == 'doc'
+ - osmnx>=2.0.0 ; extra == 'example'
+ - momepy>=0.7.2 ; extra == 'example'
+ - contextily>=1.6 ; extra == 'example'
+ - seaborn>=0.13 ; extra == 'example'
+ - cairocffi>=1.7 ; extra == 'example'
+ - igraph>=0.11 ; extra == 'example'
+ - scikit-learn>=1.5 ; extra == 'example'
+ - iplotx>=0.9.0 ; extra == 'example'
+ - lxml>=4.6 ; extra == 'extra'
+ - pygraphviz>=1.14 ; extra == 'extra'
+ - pydot>=3.0.1 ; extra == 'extra'
+ - sympy>=1.10 ; extra == 'extra'
+ - build>=0.10 ; extra == 'release'
+ - twine>=4.0 ; extra == 'release'
+ - wheel>=0.40 ; extra == 'release'
+ - changelist==0.5 ; extra == 'release'
+ - pytest>=7.2 ; extra == 'test'
+ - pytest-cov>=4.0 ; extra == 'test'
+ - pytest-xdist>=3.0 ; extra == 'test'
+ - pytest-mpl ; extra == 'test-extras'
+ - pytest-randomly ; extra == 'test-extras'
+ requires_python: '>=3.11,!=3.14.1'
- pypi: https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl
name: nodeenv
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.2.1-he2c55a7_1.conda
- sha256: 6516f99fe400181ebe27cba29180ca0c7425c15d7392f74220a028ad0e0064a2
- md5: d8005b3a90515c952b51026f6b7d005d
+- 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
- - zstd >=1.5.7,<1.6.0a0
- - c-ares >=1.34.6,<2.0a0
+ - libstdcxx >=14
+ - __glibc >=2.28,<3.0.a0
+ - libnghttp2 >=1.68.1,<2.0a0
- libuv >=1.51.0,<2.0a0
- - libsqlite >=3.51.1,<4.0a0
- - libnghttp2 >=1.67.0,<2.0a0
- - openssl >=3.5.4,<4.0a0
- - libabseil >=20250512.1,<20250513.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*
- - libzlib >=1.3.1,<2.0a0
+ - 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
- - icu >=75.1,<76.0a0
license: MIT
- license_family: MIT
purls: []
- size: 17246248
- timestamp: 1765444698486
-- conda: https://conda.anaconda.org/conda-forge/osx-64/nodejs-25.2.1-h5523da6_1.conda
- sha256: 25ade898cb9e6f26622cc563dab89810f59e898e37ec4ffabd079f9f9a068998
- md5: 18ce8107e5d71b65aaa585c238a9e90d
+ 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:
- - __osx >=11.0
- libcxx >=19
- - libsqlite >=3.51.1,<4.0a0
- - libabseil >=20250512.1,<20250513.0a0
+ - __osx >=11.0
+ - libzlib >=1.3.2,<2.0a0
+ - libnghttp2 >=1.68.1,<2.0a0
+ - libabseil >=20260107.1,<20260108.0a0
- libabseil * cxx17*
- - zstd >=1.5.7,<1.6.0a0
+ - 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.67.0,<2.0a0
- - openssl >=3.5.4,<4.0a0
+ - libsqlite >=3.52.0,<4.0a0
- libuv >=1.51.0,<2.0a0
- c-ares >=1.34.6,<2.0a0
- - icu >=75.1,<76.0a0
- - libzlib >=1.3.1,<2.0a0
+ - openssl >=3.5.5,<4.0a0
+ - zstd >=1.5.7,<1.6.0a0
license: MIT
- license_family: MIT
purls: []
- size: 16923801
- timestamp: 1765444650323
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.2.1-h5230ea7_1.conda
- sha256: acb4a33a096fa89d0ec0eea5d5f19988594d4e5c8d482ac60d2b0365d16dd984
- md5: 0b6dfe96bcfb469afe82885b3fecbd56
+ 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.1,<4.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
- - openssl >=3.5.4,<4.0a0
- - c-ares >=1.34.6,<2.0a0
- - icu >=75.1,<76.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
- - libabseil >=20250512.1,<20250513.0a0
+ - c-ares >=1.34.6,<2.0a0
+ - libabseil >=20260107.1,<20260108.0a0
- libabseil * cxx17*
- - libnghttp2 >=1.67.0,<2.0a0
- - libuv >=1.51.0,<2.0a0
- - libzlib >=1.3.1,<2.0a0
license: MIT
- license_family: MIT
purls: []
- size: 16202237
- timestamp: 1765482731453
-- conda: https://conda.anaconda.org/conda-forge/win-64/nodejs-25.2.1-he453025_1.conda
- sha256: 9742d28cf4a171dc9898bfb3c8512858f1ed46aa3cbc26d8839003d879564beb
- md5: 461d47b472740c68ec0771c8b759868b
+ 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: 30449097
- timestamp: 1765444649904
+ 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
@@ -7647,49 +8699,49 @@ packages:
- pytest-jupyter ; extra == 'test'
- pytest-tornasync ; extra == 'test'
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/2b/09/3c4abbc1dcd8010bf1a611d174c7aa689fc505585ec806111b4406f6f1b1/numpy-2.4.3-cp311-cp311-macosx_14_0_x86_64.whl
name: numpy
- version: 2.3.5
- sha256: 00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b
+ version: 2.4.3
+ sha256: 23b46bb6d8ecb68b58c09944483c135ae5f0e9b8d8858ece5e4ead783771d2a9
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/3a/66/bd096b13a87549683812b53ab211e6d413497f84e794fb3c39191948da97/numpy-2.4.3-cp313-cp313-macosx_14_0_arm64.whl
name: numpy
- version: 2.3.5
- sha256: acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218
+ version: 2.4.3
+ sha256: bb2e3cf95854233799013779216c57e153c1ee67a0bf92138acca0e429aefaee
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/76/1d/edccf27adedb754db7c4511d5eac8b83f004ae948fe2d3509e8b78097d4c/numpy-2.4.3-cp311-cp311-win_amd64.whl
name: numpy
- version: 2.3.5
- sha256: de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10
+ version: 2.4.3
+ sha256: 77e76d932c49a75617c6d13464e41203cd410956614d0a0e999b25e9e8d27eec
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/78/51/9f5d7a41f0b51649ddf2f2320595e15e122a40610b233d51928dd6c92353/numpy-2.4.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
name: numpy
- version: 2.3.5
- sha256: 8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4
+ version: 2.4.3
+ sha256: 715de7f82e192e8cae5a507a347d97ad17598f8e026152ca97233e3666daaa71
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/7e/eb/7daecbea84ec935b7fc732e18f532073064a3816f0932a40a17f3349185f/numpy-2.4.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
name: numpy
- version: 2.3.5
- sha256: aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188
+ version: 2.4.3
+ sha256: d5f51900414fc9204a0e0da158ba2ac52b75656e7dce7e77fb9f84bfa343b4cc
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/a2/2f/687722910b5a5601de2135c891108f51dfc873d8e43c8ed9f4ebb440b4a2/numpy-2.4.3-cp313-cp313-macosx_14_0_x86_64.whl
name: numpy
- version: 2.3.5
- sha256: a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c
+ version: 2.4.3
+ sha256: 7f3408ff897f8ab07a07fbe2823d7aee6ff644c097cc1f90382511fe982f647f
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/b1/3c/88af0040119209b9b5cb59485fa48b76f372c73068dbf9254784b975ac53/numpy-2.4.3-cp313-cp313-win_amd64.whl
name: numpy
- version: 2.3.5
- sha256: d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff
+ version: 2.4.3
+ sha256: 0a60e17a14d640f49146cb38e3f105f571318db7826d9b6fef7e4dce758faecd
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/ef/27/d26c85cbcd86b26e4f125b0668e7a7c0542d19dd7d23ee12e87b550e95b5/numpy-2.4.3-cp311-cp311-macosx_14_0_arm64.whl
name: numpy
- version: 2.3.5
- sha256: 11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017
+ version: 2.4.3
+ sha256: a1988292870c7cb9d0ebb4cc96b4d447513a9644801de54606dc7aabf2b7d920
requires_python: '>=3.11'
-- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda
- sha256: a47271202f4518a484956968335b2521409c8173e123ab381e775c358c67fe6d
- md5: 9ee58d5c534af06558933af3c845a780
+- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda
+ sha256: 44c877f8af015332a5d12f5ff0fb20ca32f896526a7d0cdb30c769df1144fb5c
+ md5: f61eb8cd60ff9057122a3d338b99c00f
depends:
- __glibc >=2.17,<3.0.a0
- ca-certificates
@@ -7697,33 +8749,33 @@ packages:
license: Apache-2.0
license_family: Apache
purls: []
- size: 3165399
- timestamp: 1762839186699
-- conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda
- sha256: 36fe9fb316be22fcfb46d5fa3e2e85eec5ef84f908b7745f68f768917235b2d5
- md5: 3f50cdf9a97d0280655758b735781096
+ size: 3164551
+ timestamp: 1769555830639
+- conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.1-hb6871ef_1.conda
+ sha256: e02e5639b0e4d6d4fcf0f3b082642844fb5a37316f5b0a1126c6271347462e90
+ md5: 30bb8d08b99b9a7600d39efb3559fff0
depends:
- __osx >=10.13
- ca-certificates
license: Apache-2.0
license_family: Apache
purls: []
- size: 2778996
- timestamp: 1762840724922
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda
- sha256: ebe93dafcc09e099782fe3907485d4e1671296bc14f8c383cb6f3dfebb773988
- md5: b34dc4172653c13dcf453862f251af2b
+ size: 2777136
+ timestamp: 1769557662405
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda
+ sha256: 361f5c5e60052abc12bdd1b50d7a1a43e6a6653aab99a2263bf2288d709dcf67
+ md5: f4f6ad63f98f64191c3e77c5f5f29d76
depends:
- __osx >=11.0
- ca-certificates
license: Apache-2.0
license_family: Apache
purls: []
- size: 3108371
- timestamp: 1762839712322
-- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda
- sha256: 6d72d6f766293d4f2aa60c28c244c8efed6946c430814175f959ffe8cab899b3
- md5: 84f8fb4afd1157f59098f618cd2437e4
+ size: 3104268
+ timestamp: 1769556384749
+- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.1-hf411b9b_1.conda
+ sha256: 53a5ad2e5553b8157a91bb8aa375f78c5958f77cb80e9d2ce59471ea8e5c0bd6
+ md5: eb585509b815415bc964b2c7e11c7eb3
depends:
- ca-certificates
- ucrt >=10.0.20348.0
@@ -7732,8 +8784,8 @@ packages:
license: Apache-2.0
license_family: Apache
purls: []
- size: 9440812
- timestamp: 1762841722179
+ size: 9343023
+ timestamp: 1769557547888
- pypi: https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl
name: overrides
version: 7.7.0
@@ -7741,10 +8793,10 @@ packages:
requires_dist:
- typing ; python_full_version < '3.5'
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl
name: packaging
- version: '25.0'
- sha256: 29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484
+ version: '26.0'
+ sha256: b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529
requires_python: '>=3.8'
- pypi: https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl
name: paginate
@@ -7754,754 +8806,758 @@ packages:
- pytest ; extra == 'dev'
- tox ; extra == 'dev'
- black ; extra == 'lint'
-- pypi: https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/0b/48/aad6ec4f8d007534c091e9a7172b3ec1b1ee6d99a9cbb936b5eab6c6cf58/pandas-3.0.1-cp313-cp313-macosx_10_13_x86_64.whl
name: pandas
- version: 2.3.3
- sha256: 318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac
+ version: 3.0.1
+ sha256: 5272627187b5d9c20e55d27caf5f2cd23e286aba25cadf73c8590e432e2b7262
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/1f/67/af63f83cd6ca603a00fe8530c10a60f0879265b8be00b5930e8e78c5b30b/pandas-3.0.1-cp311-cp311-win_amd64.whl
name: pandas
- version: 2.3.3
- sha256: bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8
+ version: 3.0.1
+ sha256: 84f0904a69e7365f79a0c77d3cdfccbfb05bf87847e3a51a41e1426b0edb9c79
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/2e/7c/870c7e7daec2a6c7ff2ac9e33b23317230d4e4e954b35112759ea4a924a7/pandas-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
name: pandas
- version: 2.3.3
- sha256: f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee
+ version: 3.0.1
+ sha256: 830994d7e1f31dd7e790045235605ab61cff6c94defc774547e8b7fdfbff3dc7
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/a8/14/5990826f779f79148ae9d3a2c39593dc04d61d5d90541e71b5749f35af95/pandas-3.0.1-cp313-cp313-macosx_11_0_arm64.whl
name: pandas
- version: 2.3.3
- sha256: f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c
+ version: 3.0.1
+ sha256: 661e0f665932af88c7877f31da0dc743fe9c8f2524bdffe23d24fdcb67ef9d56
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/c1/27/90683c7122febeefe84a56f2cde86a9f05f68d53885cebcc473298dfc33e/pandas-3.0.1-cp311-cp311-macosx_11_0_arm64.whl
name: pandas
- version: 2.3.3
- sha256: 8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45
+ version: 3.0.1
+ sha256: 24ba315ba3d6e5806063ac6eb717504e499ce30bd8c236d8693a5fd3f084c796
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/d6/7d/216a1588b65a7aa5f4535570418a599d943c85afb1d95b0876fc00aa1468/pandas-3.0.1-cp313-cp313-win_amd64.whl
name: pandas
- version: 2.3.3
- sha256: b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b
+ version: 3.0.1
+ sha256: 9fea306c783e28884c29057a1d9baa11a349bbf99538ec1da44c8476563d1b25
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/f2/85/ab6d04733a7d6ff32bfc8382bf1b07078228f5d6ebec5266b91bfc5c4ff7/pandas-3.0.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
name: pandas
- version: 2.3.3
- sha256: 602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523
+ version: 3.0.1
+ sha256: 1ff8cf1d2896e34343197685f432450ec99a85ba8d90cce2030c5eee2ef98791
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/ff/07/c7087e003ceee9b9a82539b40414ec557aa795b584a1a346e89180853d79/pandas-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl
name: pandas
- version: 2.3.3
- sha256: 56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713
+ version: 3.0.1
+ sha256: de09668c1bf3b925c07e5762291602f0d789eca1b3a781f99c1c78f6cac0e7ea
requires_dist:
- - numpy>=1.22.4 ; python_full_version < '3.11'
- - numpy>=1.23.2 ; python_full_version == '3.11.*'
- - numpy>=1.26.0 ; python_full_version >= '3.12'
+ - numpy>=1.26.0 ; python_full_version < '3.14'
+ - numpy>=2.3.3 ; python_full_version >= '3.14'
- python-dateutil>=2.8.2
- - pytz>=2020.1
- - tzdata>=2022.7
- - hypothesis>=6.46.1 ; extra == 'test'
- - pytest>=7.3.2 ; extra == 'test'
- - pytest-xdist>=2.2.0 ; extra == 'test'
- - pyarrow>=10.0.1 ; extra == 'pyarrow'
- - bottleneck>=1.3.6 ; extra == 'performance'
- - numba>=0.56.4 ; extra == 'performance'
- - numexpr>=2.8.4 ; extra == 'performance'
- - scipy>=1.10.0 ; extra == 'computation'
- - xarray>=2022.12.0 ; extra == 'computation'
- - fsspec>=2022.11.0 ; extra == 'fss'
- - s3fs>=2022.11.0 ; extra == 'aws'
- - gcsfs>=2022.11.0 ; extra == 'gcp'
- - pandas-gbq>=0.19.0 ; extra == 'gcp'
+ - tzdata ; sys_platform == 'win32'
+ - tzdata ; sys_platform == 'emscripten'
+ - hypothesis>=6.116.0 ; extra == 'test'
+ - pytest>=8.3.4 ; extra == 'test'
+ - pytest-xdist>=3.6.1 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'pyarrow'
+ - bottleneck>=1.4.2 ; extra == 'performance'
+ - numba>=0.60.0 ; extra == 'performance'
+ - numexpr>=2.10.2 ; extra == 'performance'
+ - scipy>=1.14.1 ; extra == 'computation'
+ - xarray>=2024.10.0 ; extra == 'computation'
+ - fsspec>=2024.10.0 ; extra == 'fss'
+ - s3fs>=2024.10.0 ; extra == 'aws'
+ - gcsfs>=2024.10.0 ; extra == 'gcp'
- odfpy>=1.4.1 ; extra == 'excel'
- - openpyxl>=3.1.0 ; extra == 'excel'
- - python-calamine>=0.1.7 ; extra == 'excel'
+ - openpyxl>=3.1.5 ; extra == 'excel'
+ - python-calamine>=0.3.0 ; extra == 'excel'
- pyxlsb>=1.0.10 ; extra == 'excel'
- xlrd>=2.0.1 ; extra == 'excel'
- - xlsxwriter>=3.0.5 ; extra == 'excel'
- - pyarrow>=10.0.1 ; extra == 'parquet'
- - pyarrow>=10.0.1 ; extra == 'feather'
- - tables>=3.8.0 ; extra == 'hdf5'
- - pyreadstat>=1.2.0 ; extra == 'spss'
- - sqlalchemy>=2.0.0 ; extra == 'postgresql'
- - psycopg2>=2.9.6 ; extra == 'postgresql'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql'
- - sqlalchemy>=2.0.0 ; extra == 'mysql'
- - pymysql>=1.0.2 ; extra == 'mysql'
- - sqlalchemy>=2.0.0 ; extra == 'sql-other'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other'
- - beautifulsoup4>=4.11.2 ; extra == 'html'
+ - xlsxwriter>=3.2.0 ; extra == 'excel'
+ - pyarrow>=13.0.0 ; extra == 'parquet'
+ - pyarrow>=13.0.0 ; extra == 'feather'
+ - pyiceberg>=0.8.1 ; extra == 'iceberg'
+ - tables>=3.10.1 ; extra == 'hdf5'
+ - pyreadstat>=1.2.8 ; extra == 'spss'
+ - sqlalchemy>=2.0.36 ; extra == 'postgresql'
+ - psycopg2>=2.9.10 ; extra == 'postgresql'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql'
+ - sqlalchemy>=2.0.36 ; extra == 'mysql'
+ - pymysql>=1.1.1 ; extra == 'mysql'
+ - sqlalchemy>=2.0.36 ; extra == 'sql-other'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other'
+ - beautifulsoup4>=4.12.3 ; extra == 'html'
- html5lib>=1.1 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'html'
- - lxml>=4.9.2 ; extra == 'xml'
- - matplotlib>=3.6.3 ; extra == 'plot'
- - jinja2>=3.1.2 ; extra == 'output-formatting'
+ - lxml>=5.3.0 ; extra == 'html'
+ - lxml>=5.3.0 ; extra == 'xml'
+ - matplotlib>=3.9.3 ; extra == 'plot'
+ - jinja2>=3.1.5 ; extra == 'output-formatting'
- tabulate>=0.9.0 ; extra == 'output-formatting'
- pyqt5>=5.15.9 ; extra == 'clipboard'
- - qtpy>=2.3.0 ; extra == 'clipboard'
- - zstandard>=0.19.0 ; extra == 'compression'
- - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard'
- - adbc-driver-postgresql>=0.8.0 ; extra == 'all'
- - adbc-driver-sqlite>=0.8.0 ; extra == 'all'
- - beautifulsoup4>=4.11.2 ; extra == 'all'
- - bottleneck>=1.3.6 ; extra == 'all'
- - dataframe-api-compat>=0.1.7 ; extra == 'all'
- - fastparquet>=2022.12.0 ; extra == 'all'
- - fsspec>=2022.11.0 ; extra == 'all'
- - gcsfs>=2022.11.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'clipboard'
+ - zstandard>=0.23.0 ; extra == 'compression'
+ - pytz>=2024.2 ; extra == 'timezone'
+ - adbc-driver-postgresql>=1.2.0 ; extra == 'all'
+ - adbc-driver-sqlite>=1.2.0 ; extra == 'all'
+ - beautifulsoup4>=4.12.3 ; extra == 'all'
+ - bottleneck>=1.4.2 ; extra == 'all'
+ - fastparquet>=2024.11.0 ; extra == 'all'
+ - fsspec>=2024.10.0 ; extra == 'all'
+ - gcsfs>=2024.10.0 ; extra == 'all'
- html5lib>=1.1 ; extra == 'all'
- - hypothesis>=6.46.1 ; extra == 'all'
- - jinja2>=3.1.2 ; extra == 'all'
- - lxml>=4.9.2 ; extra == 'all'
- - matplotlib>=3.6.3 ; extra == 'all'
- - numba>=0.56.4 ; extra == 'all'
- - numexpr>=2.8.4 ; extra == 'all'
+ - hypothesis>=6.116.0 ; extra == 'all'
+ - jinja2>=3.1.5 ; extra == 'all'
+ - lxml>=5.3.0 ; extra == 'all'
+ - matplotlib>=3.9.3 ; extra == 'all'
+ - numba>=0.60.0 ; extra == 'all'
+ - numexpr>=2.10.2 ; extra == 'all'
- odfpy>=1.4.1 ; extra == 'all'
- - openpyxl>=3.1.0 ; extra == 'all'
- - pandas-gbq>=0.19.0 ; extra == 'all'
- - psycopg2>=2.9.6 ; extra == 'all'
- - pyarrow>=10.0.1 ; extra == 'all'
- - pymysql>=1.0.2 ; extra == 'all'
+ - openpyxl>=3.1.5 ; extra == 'all'
+ - psycopg2>=2.9.10 ; extra == 'all'
+ - pyarrow>=13.0.0 ; extra == 'all'
+ - pyiceberg>=0.8.1 ; extra == 'all'
+ - pymysql>=1.1.1 ; extra == 'all'
- pyqt5>=5.15.9 ; extra == 'all'
- - pyreadstat>=1.2.0 ; extra == 'all'
- - pytest>=7.3.2 ; extra == 'all'
- - pytest-xdist>=2.2.0 ; extra == 'all'
- - python-calamine>=0.1.7 ; extra == 'all'
+ - pyreadstat>=1.2.8 ; extra == 'all'
+ - pytest>=8.3.4 ; extra == 'all'
+ - pytest-xdist>=3.6.1 ; extra == 'all'
+ - python-calamine>=0.3.0 ; extra == 'all'
+ - pytz>=2024.2 ; extra == 'all'
- pyxlsb>=1.0.10 ; extra == 'all'
- - qtpy>=2.3.0 ; extra == 'all'
- - scipy>=1.10.0 ; extra == 'all'
- - s3fs>=2022.11.0 ; extra == 'all'
- - sqlalchemy>=2.0.0 ; extra == 'all'
- - tables>=3.8.0 ; extra == 'all'
+ - qtpy>=2.4.2 ; extra == 'all'
+ - scipy>=1.14.1 ; extra == 'all'
+ - s3fs>=2024.10.0 ; extra == 'all'
+ - sqlalchemy>=2.0.36 ; extra == 'all'
+ - tables>=3.10.1 ; extra == 'all'
- tabulate>=0.9.0 ; extra == 'all'
- - xarray>=2022.12.0 ; extra == 'all'
+ - xarray>=2024.10.0 ; extra == 'all'
- xlrd>=2.0.1 ; extra == 'all'
- - xlsxwriter>=3.0.5 ; extra == 'all'
- - zstandard>=0.19.0 ; extra == 'all'
- requires_python: '>=3.9'
+ - xlsxwriter>=3.2.0 ; extra == 'all'
+ - zstandard>=0.23.0 ; extra == 'all'
+ requires_python: '>=3.11'
- pypi: https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl
name: pandocfilters
version: 1.5.1
sha256: 93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc
requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*'
-- pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl
name: parso
- version: 0.8.5
- sha256: 646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887
+ version: 0.8.6
+ sha256: 2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff
requires_dist:
- pytest ; extra == 'testing'
- docopt ; extra == 'testing'
- flake8==5.0.4 ; extra == 'qa'
- - mypy==0.971 ; extra == 'qa'
+ - zuban==0.5.1 ; extra == 'qa'
- types-setuptools==67.2.0.1 ; extra == 'qa'
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/05/bb/39e6768529454cc2b57e1e2fa0a0a18ff64397a16303270e215a3e03285f/pathspec-1.0.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/71/e7/40fb618334dcdf7c5a316c0e7343c5cd82d3d866edc100d98e29bc945ecd/partd-1.4.2-py3-none-any.whl
+ name: partd
+ version: 1.4.2
+ sha256: 978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f
+ requires_dist:
+ - locket
+ - toolz
+ - numpy>=1.20.0 ; extra == 'complete'
+ - pandas>=1.3 ; extra == 'complete'
+ - pyzmq ; extra == 'complete'
+ - blosc ; extra == 'complete'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl
name: pathspec
- version: 1.0.0
- sha256: 1373719036e64a2b9de3b8ddd9e30afb082a915619f07265ed76d9ae507800ae
+ version: 1.0.4
+ sha256: fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723
requires_dist:
- hyperscan>=0.7 ; extra == 'hyperscan'
- typing-extensions>=4 ; extra == 'optional'
@@ -8515,10 +9571,10 @@ packages:
sha256: 7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523
requires_dist:
- ptyprocess>=0.5
-- pypi: https://files.pythonhosted.org/packages/01/9a/632e58ec89a32738cabfd9ec418f0e9898a2b4719afc581f07c04a05e3c9/pillow-12.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl
name: pillow
- version: 12.1.0
- sha256: 6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc
+ version: 12.1.1
+ sha256: 6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8547,10 +9603,10 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/1c/34/583420a1b55e715937a85bd48c5c0991598247a1fd2eb5423188e765ea02/pillow-12.1.0-cp313-cp313-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/2b/46/5da1ec4a5171ee7bf1a0efa064aba70ba3d6e0788ce3f5acd1375d23c8c0/pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl
name: pillow
- version: 12.1.0
- sha256: db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac
+ version: 12.1.1
+ sha256: e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8579,10 +9635,10 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/43/06/7264c0597e676104cc22ca73ee48f752767cd4b1fe084662620b17e10120/pillow-12.1.0-cp311-cp311-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/31/03/bef822e4f2d8f9d7448c133d0a18185d3cce3e70472774fffefe8b0ed562/pillow-12.1.1-cp311-cp311-win_amd64.whl
name: pillow
- version: 12.1.0
- sha256: b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0
+ version: 12.1.1
+ sha256: fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8611,10 +9667,10 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/43/c4/bf8328039de6cc22182c3ef007a2abfbbdab153661c0a9aa78af8d706391/pillow-12.1.0-cp311-cp311-macosx_10_10_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl
name: pillow
- version: 12.1.0
- sha256: a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3
+ version: 12.1.1
+ sha256: 344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8643,10 +9699,10 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/4f/bf/28ab865de622e14b747f0cd7877510848252d950e43002e224fb1c9ababf/pillow-12.1.0-cp313-cp313-macosx_10_13_x86_64.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
name: pillow
- version: 12.1.0
- sha256: 983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587
+ version: 12.1.1
+ sha256: 47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8675,10 +9731,10 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/5c/1f/8e66ab9be3aaf1435bc03edd1ebdf58ffcd17f7349c1d970cafe87af27d9/pillow-12.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/78/93/a29e9bc02d1cf557a834da780ceccd54e02421627200696fcf805ebdc3fb/pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl
name: pillow
- version: 12.1.0
- sha256: 1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0
+ version: 12.1.1
+ sha256: 365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8707,10 +9763,10 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/6c/af/b1d7e301c4cd26cd45d4af884d9ee9b6fab893b0ad2450d4746d74a6968c/pillow-12.1.0-cp311-cp311-win_amd64.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
name: pillow
- version: 12.1.0
- sha256: 806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75
+ version: 12.1.1
+ sha256: 597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8739,10 +9795,10 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/e5/07/74a9d941fa45c90a0d9465098fe1ec85de3e2afbdc15cc4766622d516056/pillow-12.1.0-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl
name: pillow
- version: 12.1.0
- sha256: 461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a
+ version: 12.1.1
+ sha256: 8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2
requires_dist:
- furo ; extra == 'docs'
- olefile ; extra == 'docs'
@@ -8771,11 +9827,6 @@ packages:
- trove-classifiers>=2024.10.12 ; extra == 'tests'
- defusedxml ; extra == 'xmp'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl
- name: pip
- version: '25.3'
- sha256: 9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd
- 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
@@ -8788,26 +9839,46 @@ packages:
- returns>=0.23
- tomli>=2 ; python_full_version < '3.11'
requires_python: '>=3.10,<4.0'
-- pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl
name: platformdirs
- version: 4.5.1
- sha256: d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31
- requires_dist:
- - furo>=2025.9.25 ; extra == 'docs'
- - proselint>=0.14 ; extra == 'docs'
- - sphinx-autodoc-typehints>=3.2 ; extra == 'docs'
- - sphinx>=8.2.3 ; extra == 'docs'
- - appdirs==1.4.4 ; extra == 'test'
- - covdefaults>=2.3 ; extra == 'test'
- - pytest-cov>=7 ; extra == 'test'
- - pytest-mock>=3.15.1 ; extra == 'test'
- - pytest>=8.4.2 ; extra == 'test'
- - mypy>=1.18.2 ; extra == 'type'
+ version: 4.9.4
+ sha256: 68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.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.1
+ sha256: 56531f2f71fa4f7f33c172312d2423d969deb9b9dd29b2524ad3ed7e33d220eb
+ requires_dist:
+ - lazy-loader>=0.4
+ - matplotlib>=3.8
+ - scipp>=25.5.0 ; extra == 'scipp'
+ - scipp>=25.5.0 ; extra == 'all'
+ - ipympl>0.8.4 ; extra == 'all'
+ - pythreejs>=2.4.1 ; extra == 'all'
+ - mpltoolbox>=24.6.0 ; extra == 'all'
+ - ipywidgets>=8.1.0 ; extra == 'all'
+ - graphviz>=0.20.3 ; extra == 'all'
+ - graphviz>=0.20.3 ; extra == 'test'
+ - h5py>=3.12 ; extra == 'test'
+ - ipympl>=0.8.4 ; extra == 'test'
+ - ipywidgets>=8.1.0 ; extra == 'test'
+ - ipykernel>=6.26,<7 ; extra == 'test'
+ - mpltoolbox>=24.6.0 ; extra == 'test'
+ - pandas>=2.2.2 ; extra == 'test'
+ - plotly>=5.15.0 ; extra == 'test'
+ - pooch>=1.5 ; extra == 'test'
+ - pyarrow>=13.0.0 ; extra == 'test'
+ - pytest>=8.0 ; extra == 'test'
+ - pythreejs>=2.4.1 ; extra == 'test'
+ - scipp>=25.5.0 ; extra == 'test'
+ - scipy>=1.10.0 ; extra == 'test'
+ - xarray>=2024.5.0 ; extra == 'test'
+ - anywidget>=0.9.0 ; extra == 'test'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl
name: plotly
- version: 6.5.0
- sha256: 5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a
+ version: 6.6.0
+ sha256: 8d6daf0f87412e0c0bfe72e809d615217ab57cc715899a1e5145135a7800d1d0
requires_dist:
- narwhals>=1.15.1
- packaging
@@ -8855,14 +9926,22 @@ 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'
sha256: 096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce
-- pypi: https://files.pythonhosted.org/packages/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/2a/2d/d4bf65e47cea8ff2c794a600c4fd1273a7902f268757c531e0ee9f18aa58/pooch-1.9.0-py3-none-any.whl
name: pooch
- version: 1.8.2
- sha256: 3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47
+ version: 1.9.0
+ sha256: f265597baa9f760d25ceb29d0beb8186c243d6607b0f60b83ecf14078dbc703b
requires_dist:
- platformdirs>=2.5.0
- packaging>=20.0
@@ -8870,7 +9949,9 @@ packages:
- tqdm>=4.41.0,<5.0.0 ; extra == 'progress'
- paramiko>=2.7.0 ; extra == 'sftp'
- xxhash>=1.4.3 ; extra == 'xxhash'
- requires_python: '>=3.7'
+ - pytest-httpserver ; extra == 'test'
+ - pytest-localftpserver ; extra == 'test'
+ requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl
name: pre-commit
version: 4.5.1
@@ -8892,12 +9973,14 @@ packages:
- pytest-cov ; extra == 'tests'
- pytest-lazy-fixtures ; extra == 'tests'
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl
name: prometheus-client
- version: 0.23.1
- sha256: dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99
+ version: 0.24.1
+ sha256: 150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055
requires_dist:
- twisted ; extra == 'twisted'
+ - aiohttp ; extra == 'aiohttp'
+ - django ; extra == 'django'
requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl
name: prompt-toolkit
@@ -8946,10 +10029,10 @@ packages:
version: 0.4.1
sha256: 381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/29/f8/40e01c350ad9a2b3cb4e6adbcc8a83b17ee50dd5792102b6142385937db5/psutil-7.2.1-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl
name: psutil
- version: 7.2.1
- sha256: 5e38404ca2bb30ed7267a46c02f06ff842e92da3bb8c5bfdadbd35a5722314d8
+ version: 7.2.2
+ sha256: 1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979
requires_dist:
- psleak ; extra == 'dev'
- pytest ; extra == 'dev'
@@ -8976,25 +10059,30 @@ packages:
- virtualenv ; extra == 'dev'
- vulture ; extra == 'dev'
- wheel ; extra == 'dev'
+ - colorama ; os_name == 'nt' and extra == 'dev'
+ - pyreadline3 ; os_name == 'nt' and extra == 'dev'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
- psleak ; extra == 'test'
- pytest ; extra == 'test'
- pytest-instafail ; extra == 'test'
- pytest-xdist ; extra == 'test'
- setuptools ; extra == 'test'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/34/68/d9317542e3f2b180c4306e3f45d3c922d7e86d8ce39f941bb9e2e9d8599e/psutil-7.2.1-cp37-abi3-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl
name: psutil
- version: 7.2.1
- sha256: b1b0671619343aa71c20ff9767eced0483e4fc9e1f489d50923738caf6a03c17
+ version: 7.2.2
+ sha256: eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988
requires_dist:
- psleak ; extra == 'dev'
- pytest ; extra == 'dev'
- pytest-instafail ; extra == 'dev'
- pytest-xdist ; extra == 'dev'
- setuptools ; extra == 'dev'
- - pywin32 ; extra == 'dev'
- - wheel ; extra == 'dev'
- - wmi ; extra == 'dev'
- abi3audit ; extra == 'dev'
- black ; extra == 'dev'
- check-manifest ; extra == 'dev'
@@ -9014,21 +10102,25 @@ packages:
- validate-pyproject[all] ; extra == 'dev'
- virtualenv ; extra == 'dev'
- vulture ; extra == 'dev'
- - colorama ; extra == 'dev'
- - pyreadline3 ; extra == 'dev'
+ - wheel ; extra == 'dev'
+ - colorama ; os_name == 'nt' and extra == 'dev'
+ - pyreadline3 ; os_name == 'nt' and extra == 'dev'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
- psleak ; extra == 'test'
- pytest ; extra == 'test'
- pytest-instafail ; extra == 'test'
- pytest-xdist ; extra == 'test'
- setuptools ; extra == 'test'
- - pywin32 ; extra == 'test'
- - wheel ; extra == 'test'
- - wmi ; extra == 'test'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/c5/2c/78e4a789306a92ade5000da4f5de3255202c534acdadc3aac7b5458fadef/psutil-7.2.1-cp36-abi3-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl
name: psutil
- version: 7.2.1
- sha256: 05cc68dbb8c174828624062e73078e7e35406f4ca2d0866c272c2410d8ef06d1
+ version: 7.2.2
+ sha256: 076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9
requires_dist:
- psleak ; extra == 'dev'
- pytest ; extra == 'dev'
@@ -9055,16 +10147,24 @@ packages:
- virtualenv ; extra == 'dev'
- vulture ; extra == 'dev'
- wheel ; extra == 'dev'
+ - colorama ; os_name == 'nt' and extra == 'dev'
+ - pyreadline3 ; os_name == 'nt' and extra == 'dev'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
- psleak ; extra == 'test'
- pytest ; extra == 'test'
- pytest-instafail ; extra == 'test'
- pytest-xdist ; extra == 'test'
- setuptools ; extra == 'test'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
requires_python: '>=3.6'
-- pypi: https://files.pythonhosted.org/packages/c5/cf/5180eb8c8bdf6a503c6919f1da28328bd1e6b3b1b5b9d5b01ae64f019616/psutil-7.2.1-cp36-abi3-macosx_10_9_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl
name: psutil
- version: 7.2.1
- sha256: b2e953fcfaedcfbc952b44744f22d16575d3aa78eb4f51ae74165b4e96e55f42
+ version: 7.2.2
+ sha256: ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486
requires_dist:
- psleak ; extra == 'dev'
- pytest ; extra == 'dev'
@@ -9091,11 +10191,19 @@ packages:
- virtualenv ; extra == 'dev'
- vulture ; extra == 'dev'
- wheel ; extra == 'dev'
+ - colorama ; os_name == 'nt' and extra == 'dev'
+ - pyreadline3 ; os_name == 'nt' and extra == 'dev'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev'
- psleak ; extra == 'test'
- pytest ; extra == 'test'
- pytest-instafail ; extra == 'test'
- pytest-xdist ; extra == 'test'
- setuptools ; extra == 'test'
+ - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
+ - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test'
requires_python: '>=3.6'
- pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
name: ptyprocess
@@ -9112,10 +10220,10 @@ packages:
version: 1.11.0
sha256: 607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378
requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*'
-- pypi: https://files.pythonhosted.org/packages/0b/a6/e2e8535d8146bce05de6e0ecf1099a7e2887d840ae2a7b3a09385543fd02/py3dmol-2.5.3-py2.py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/25/7d/cea3531f77df694ac7f169378250d85f19f69b09a5f4fa45f650837ae7cc/py3dmol-2.5.4-py2.py3-none-any.whl
name: py3dmol
- version: 2.5.3
- sha256: 5c1c9ee40bda82b91978e75f3c144be5b90cdf472e765bcef4890db76cc8f843
+ version: 2.5.4
+ sha256: 32806726b5310524a2b5bfee320737f7feef635cafc945c991062806daa9e43a
requires_dist:
- ipython ; extra == 'ipython'
- pypi: https://files.pythonhosted.org/packages/5c/a5/a8c7562ec39f2647245b52ea4aeb13b5b125b3f48c0c152e9ebce7047a0a/pycifrw-5.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
@@ -9191,11 +10299,89 @@ packages:
version: 2.14.0
sha256: dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl
name: pycparser
- version: '2.23'
- sha256: e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934
- requires_python: '>=3.8'
+ version: '3.0'
+ sha256: b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl
+ name: pydantic
+ version: 2.12.5
+ sha256: e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d
+ requires_dist:
+ - annotated-types>=0.6.0
+ - pydantic-core==2.41.5
+ - typing-extensions>=4.14.1
+ - typing-inspection>=0.4.2
+ - email-validator>=2.0.0 ; extra == 'email'
+ - tzdata ; python_full_version >= '3.9' and sys_platform == 'win32' and extra == 'timezone'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl
+ name: pydantic-core
+ version: 2.41.5
+ sha256: 76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe
+ requires_dist:
+ - typing-extensions>=4.14.1
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl
+ name: pydantic-core
+ version: 2.41.5
+ sha256: 7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b
+ requires_dist:
+ - typing-extensions>=4.14.1
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl
+ name: pydantic-core
+ version: 2.41.5
+ sha256: 941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9
+ requires_dist:
+ - typing-extensions>=4.14.1
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl
+ name: pydantic-core
+ version: 2.41.5
+ sha256: 112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34
+ requires_dist:
+ - typing-extensions>=4.14.1
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl
+ name: pydantic-core
+ version: 2.41.5
+ sha256: 79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11
+ requires_dist:
+ - typing-extensions>=4.14.1
+ requires_python: '>=3.9'
+- 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
+ name: pydantic-core
+ version: 2.41.5
+ sha256: f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b
+ requires_dist:
+ - typing-extensions>=4.14.1
+ requires_python: '>=3.9'
+- 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
+ name: pydantic-core
+ version: 2.41.5
+ sha256: 406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586
+ requires_dist:
+ - typing-extensions>=4.14.1
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl
+ name: pydantic-core
+ version: 2.41.5
+ sha256: a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6
+ 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
@@ -9203,19 +10389,19 @@ packages:
requires_dist:
- colorama>=0.4.6 ; extra == 'windows-terminal'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl
name: pymdown-extensions
- version: '10.20'
- sha256: ea9e62add865da80a271d00bfa1c0fa085b20d133fb3fc97afdc88e682f60b2f
+ version: '10.21'
+ sha256: 91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f
requires_dist:
- markdown>=3.6
- pyyaml
- pygments>=2.19.1 ; extra == 'extra'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/8b/40/2614036cdd416452f5bf98ec037f38a1afb17f327cb8e6b652d4729e0af8/pyparsing-3.3.1-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl
name: pyparsing
- version: 3.3.1
- sha256: 023b5e7e5520ad96642e2c6db4cb683d3970bd640cdf7115049a6e9c3682df82
+ version: 3.3.2
+ sha256: 850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d
requires_dist:
- railroad-diagrams ; extra == 'diagrams'
- jinja2 ; extra == 'diagrams'
@@ -9245,10 +10431,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
@@ -9268,38 +10454,37 @@ packages:
- psutil>=3.0 ; extra == 'psutil'
- setproctitle ; extra == 'setproctitle'
requires_python: '>=3.9'
-- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.14-hd63d673_2_cpython.conda
- build_number: 2
- sha256: 5b872f7747891e50e990a96d2b235236a5c66cc9f8c9dcb7149aee674ea8145a
- md5: c4202a55b4486314fbb8c11bc43a29a0
+- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda
+ sha256: bf6a32c69889d38482436a786bea32276756cedf0e9805cc856ffd088e8d00f0
+ md5: a5ebcefec0c12a333bcd6d7bf3bddc1f
depends:
- __glibc >=2.17,<3.0.a0
- bzip2 >=1.0.8,<2.0a0
- ld_impl_linux-64 >=2.36.1
- - libexpat >=2.7.1,<3.0a0
+ - libexpat >=2.7.4,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- libgcc >=14
- - liblzma >=5.8.1,<6.0a0
+ - liblzma >=5.8.2,<6.0a0
- libnsl >=2.0.1,<2.1.0a0
- - libsqlite >=3.50.4,<4.0a0
- - libuuid >=2.41.2,<3.0a0
+ - libsqlite >=3.51.2,<4.0a0
+ - libuuid >=2.41.3,<3.0a0
- libxcrypt >=4.4.36
- libzlib >=1.3.1,<2.0a0
- ncurses >=6.5,<7.0a0
- - openssl >=3.5.4,<4.0a0
- - readline >=8.2,<9.0a0
+ - openssl >=3.5.5,<4.0a0
+ - readline >=8.3,<9.0a0
- tk >=8.6.13,<8.7.0a0
- tzdata
constrains:
- python_abi 3.11.* *_cp311
license: Python-2.0
purls: []
- size: 30874708
- timestamp: 1761174520369
-- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.11-hc97d973_100_cp313.conda
+ size: 30949404
+ timestamp: 1772730362552
+- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.12-hc97d973_100_cp313.conda
build_number: 100
- sha256: 9cf014cf28e93ee242bacfbf664e8b45ae06e50b04291e640abeaeb0cba0364c
- md5: 0cbb0010f1d8ecb64a428a8d4214609e
+ sha256: 8a08fe5b7cb5a28aa44e2994d18dbf77f443956990753a4ca8173153ffb6eb56
+ md5: 4c875ed0e78c2d407ec55eadffb8cf3d
depends:
- __glibc >=2.17,<3.0.a0
- bzip2 >=1.0.8,<2.0a0
@@ -9307,128 +10492,125 @@ packages:
- libexpat >=2.7.3,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- libgcc >=14
- - liblzma >=5.8.1,<6.0a0
+ - liblzma >=5.8.2,<6.0a0
- libmpdec >=4.0.0,<5.0a0
- - libsqlite >=3.51.1,<4.0a0
- - libuuid >=2.41.2,<3.0a0
+ - libsqlite >=3.51.2,<4.0a0
+ - libuuid >=2.41.3,<3.0a0
- libzlib >=1.3.1,<2.0a0
- ncurses >=6.5,<7.0a0
- - openssl >=3.5.4,<4.0a0
+ - openssl >=3.5.5,<4.0a0
- python_abi 3.13.* *_cp313
- - readline >=8.2,<9.0a0
+ - readline >=8.3,<9.0a0
- tk >=8.6.13,<8.7.0a0
- tzdata
license: Python-2.0
purls: []
- size: 37226336
- timestamp: 1765021889577
+ size: 37364553
+ timestamp: 1770272309861
python_site_packages_path: lib/python3.13/site-packages
-- conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.14-h74c2667_2_cpython.conda
- build_number: 2
- sha256: 0a17479efb8df514c3777c015ffe430d38a3a59c01dc46358e87d7ff459c9aeb
- md5: 37ac5f13a245f08746e0d658b245d670
+- conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda
+ sha256: e02e12cd8d391f18bb3bf91d07e16b993592ec0d76ee37cf639390b766e0e687
+ md5: 93b802a91de90b2c17b808608726bf45
depends:
- - __osx >=10.13
+ - __osx >=11.0
- bzip2 >=1.0.8,<2.0a0
- - libexpat >=2.7.1,<3.0a0
+ - libexpat >=2.7.4,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- - liblzma >=5.8.1,<6.0a0
- - libsqlite >=3.50.4,<4.0a0
+ - liblzma >=5.8.2,<6.0a0
+ - libsqlite >=3.51.2,<4.0a0
- libzlib >=1.3.1,<2.0a0
- ncurses >=6.5,<7.0a0
- - openssl >=3.5.4,<4.0a0
- - readline >=8.2,<9.0a0
+ - openssl >=3.5.5,<4.0a0
+ - readline >=8.3,<9.0a0
- tk >=8.6.13,<8.7.0a0
- tzdata
constrains:
- python_abi 3.11.* *_cp311
license: Python-2.0
purls: []
- size: 15697126
- timestamp: 1761174493171
-- conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.11-h17c18a5_100_cp313.conda
+ size: 15664115
+ timestamp: 1772730794934
+- conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.12-h894a449_100_cp313.conda
build_number: 100
- sha256: 58e23beaf3174a809c785900477c37df9f88993b5a3ccd0d76d57d6688a1be37
- md5: 6ffffd784fe1126b73329e29c80ddf53
+ sha256: 9548dcf58cf6045aa4aa1f2f3fa6110115ca616a8d5fa142a24081d2b9d91291
+ md5: 99b1fa1fe8a8ab58224969f4568aadca
depends:
- __osx >=10.13
- bzip2 >=1.0.8,<2.0a0
- libexpat >=2.7.3,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- - liblzma >=5.8.1,<6.0a0
+ - liblzma >=5.8.2,<6.0a0
- libmpdec >=4.0.0,<5.0a0
- - libsqlite >=3.51.1,<4.0a0
+ - libsqlite >=3.51.2,<4.0a0
- libzlib >=1.3.1,<2.0a0
- ncurses >=6.5,<7.0a0
- - openssl >=3.5.4,<4.0a0
+ - openssl >=3.5.5,<4.0a0
- python_abi 3.13.* *_cp313
- - readline >=8.2,<9.0a0
+ - readline >=8.3,<9.0a0
- tk >=8.6.13,<8.7.0a0
- tzdata
license: Python-2.0
purls: []
- size: 17360881
- timestamp: 1765022591905
+ size: 17570178
+ timestamp: 1770272361922
python_site_packages_path: lib/python3.13/site-packages
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.14-h18782d2_2_cpython.conda
- build_number: 2
- sha256: 64a2bc6be8582fae75f1f2da7bdc49afd81c2793f65bb843fc37f53c99734063
- md5: da948e6cd735249ab4cfbb3fdede785e
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda
+ sha256: 9a846065863925b2562126a5c6fecd7a972e84aaa4de9e686ad3715ca506acfa
+ md5: 49c7d96c58b969585cf09fb01d74e08e
depends:
- __osx >=11.0
- bzip2 >=1.0.8,<2.0a0
- - libexpat >=2.7.1,<3.0a0
+ - libexpat >=2.7.4,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- - liblzma >=5.8.1,<6.0a0
- - libsqlite >=3.50.4,<4.0a0
+ - liblzma >=5.8.2,<6.0a0
+ - libsqlite >=3.51.2,<4.0a0
- libzlib >=1.3.1,<2.0a0
- ncurses >=6.5,<7.0a0
- - openssl >=3.5.4,<4.0a0
- - readline >=8.2,<9.0a0
+ - openssl >=3.5.5,<4.0a0
+ - readline >=8.3,<9.0a0
- tk >=8.6.13,<8.7.0a0
- tzdata
constrains:
- python_abi 3.11.* *_cp311
license: Python-2.0
purls: []
- size: 14788204
- timestamp: 1761174033541
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.11-hfc2f54d_100_cp313.conda
+ size: 14753109
+ timestamp: 1772730203101
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.12-h20e6be0_100_cp313.conda
build_number: 100
- sha256: c476f4e9b6d97c46b496b442878924868a54e5727251549ebfc82027aa52af68
- md5: 18a8c69608151098a8fb75eea64cc266
+ sha256: 9a4f16a64def0853f0a7b6a7beb40d498fd6b09bee10b90c3d6069b664156817
+ md5: 179c0f5ae4f22bc3be567298ed0b17b9
depends:
- __osx >=11.0
- bzip2 >=1.0.8,<2.0a0
- libexpat >=2.7.3,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- - liblzma >=5.8.1,<6.0a0
+ - liblzma >=5.8.2,<6.0a0
- libmpdec >=4.0.0,<5.0a0
- - libsqlite >=3.51.1,<4.0a0
+ - libsqlite >=3.51.2,<4.0a0
- libzlib >=1.3.1,<2.0a0
- ncurses >=6.5,<7.0a0
- - openssl >=3.5.4,<4.0a0
+ - openssl >=3.5.5,<4.0a0
- python_abi 3.13.* *_cp313
- - readline >=8.2,<9.0a0
+ - readline >=8.3,<9.0a0
- tk >=8.6.13,<8.7.0a0
- tzdata
license: Python-2.0
purls: []
- size: 12920650
- timestamp: 1765020887340
+ size: 12770674
+ timestamp: 1770272314517
python_site_packages_path: lib/python3.13/site-packages
-- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.14-h0159041_2_cpython.conda
- build_number: 2
- sha256: d5f455472597aefcdde1bc39bca313fcb40bf084f3ad987da0441f2a2ec242e4
- md5: 02a9ba5950d8b78e6c9862d6ba7a5045
+- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda
+ sha256: a1f1031088ce69bc99c82b95980c1f54e16cbd5c21f042e9c1ea25745a8fc813
+ md5: d09dbf470b41bca48cbe6a78ba1e009b
depends:
- bzip2 >=1.0.8,<2.0a0
- - libexpat >=2.7.1,<3.0a0
+ - libexpat >=2.7.4,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- - liblzma >=5.8.1,<6.0a0
- - libsqlite >=3.50.4,<4.0a0
+ - liblzma >=5.8.2,<6.0a0
+ - libsqlite >=3.51.2,<4.0a0
- libzlib >=1.3.1,<2.0a0
- - openssl >=3.5.4,<4.0a0
+ - openssl >=3.5.5,<4.0a0
- tk >=8.6.13,<8.7.0a0
- tzdata
- ucrt >=10.0.20348.0
@@ -9438,21 +10620,21 @@ packages:
- python_abi 3.11.* *_cp311
license: Python-2.0
purls: []
- size: 18514691
- timestamp: 1761172844103
-- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.11-h09917c8_100_cp313.conda
+ size: 18416208
+ timestamp: 1772728847666
+- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.12-h09917c8_100_cp313.conda
build_number: 100
- sha256: 0ee0402368783e1fad10025719530499c517a3dbbdfbe18351841d9b7aef1d6a
- md5: 9e4c9a7ee9c4ab5b3778ab73e583283e
+ sha256: da70aec20ff5a5ae18bbba9fdd1e18190b419605cafaafb3bdad8becf11ce94d
+ md5: 4440c24966d0aa0c8f1e1d5006dac2d6
depends:
- bzip2 >=1.0.8,<2.0a0
- libexpat >=2.7.3,<3.0a0
- libffi >=3.5.2,<3.6.0a0
- - liblzma >=5.8.1,<6.0a0
+ - liblzma >=5.8.2,<6.0a0
- libmpdec >=4.0.0,<5.0a0
- - libsqlite >=3.51.1,<4.0a0
+ - libsqlite >=3.51.2,<4.0a0
- libzlib >=1.3.1,<2.0a0
- - openssl >=3.5.4,<4.0a0
+ - openssl >=3.5.5,<4.0a0
- python_abi 3.13.* *_cp313
- tk >=8.6.13,<8.7.0a0
- tzdata
@@ -9461,8 +10643,8 @@ packages:
- vc14_runtime >=14.44.35208
license: Python-2.0
purls: []
- size: 16617922
- timestamp: 1765019627175
+ size: 16535316
+ timestamp: 1770270322707
python_site_packages_path: Lib/site-packages
- pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl
name: python-dateutil
@@ -9471,17 +10653,35 @@ packages:
requires_dist:
- six>=1.5
requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*'
-- pypi: https://files.pythonhosted.org/packages/50/74/c655a6eda0fd188d490c14142a0f0380655ac7099604e1fbf8fa1a97f0a1/python_engineio-4.13.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.1
+ sha256: b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502
+ requires_dist:
+ - filelock>=3.15.4
+ - platformdirs>=4.3.6,<5
+ - furo>=2025.12.19 ; extra == 'docs'
+ - sphinx-autodoc-typehints>=3.6.3 ; extra == 'docs'
+ - sphinx>=9.1 ; extra == 'docs'
+ - sphinxcontrib-mermaid>=2 ; extra == 'docs'
+ - covdefaults>=2.3 ; extra == 'testing'
+ - coverage>=7.5.4 ; extra == 'testing'
+ - pytest-mock>=3.14 ; extra == 'testing'
+ - pytest>=8.3.5 ; extra == 'testing'
+ - setuptools>=75.1 ; extra == 'testing'
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl
name: python-engineio
- version: 4.13.0
- sha256: 57b94eac094fa07b050c6da59f48b12250ab1cd920765f4849963e3d89ad9de3
+ version: 4.13.1
+ sha256: f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399
requires_dist:
- simple-websocket>=0.10.0
- requests>=2.21.0 ; extra == 'client'
- websocket-client>=0.54.0 ; extra == 'client'
- - aiohttp>=3.4 ; extra == 'asyncio-client'
+ - aiohttp>=3.11 ; extra == 'asyncio-client'
- tox ; extra == 'dev'
- sphinx ; extra == 'docs'
+ - furo ; extra == 'docs'
requires_python: '>=3.8'
- pypi: https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl
name: python-json-logger
@@ -9509,10 +10709,10 @@ packages:
- mkdocs-literate-nav ; extra == 'dev'
- mike ; extra == 'dev'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/28/d2/2ccc2b69a187b80fda3152745670cfba936704f296a9fa54c6c8ac694d12/python_socketio-5.16.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl
name: python-socketio
- version: 5.16.0
- sha256: d95802961e15c7bd54ecf884c6e7644f81be8460f0a02ee66b473df58088ee8a
+ version: 5.16.1
+ sha256: a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35
requires_dist:
- bidict>=0.21.0
- python-engineio>=4.11.0
@@ -9521,6 +10721,7 @@ packages:
- aiohttp>=3.4 ; extra == 'asyncio-client'
- tox ; extra == 'dev'
- sphinx ; extra == 'docs'
+ - furo ; extra == 'docs'
requires_python: '>=3.8'
- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda
build_number: 8
@@ -9533,19 +10734,44 @@ packages:
purls: []
size: 7002
timestamp: 1752805902938
-- pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl
- name: pytz
- version: '2025.2'
- sha256: 5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00
-- pypi: https://files.pythonhosted.org/packages/a6/a1/409c1651c9f874d598c10f51ff586c416625601df4bca315d08baec4c3e3/pywinpty-3.0.2-cp311-cp311-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/d8/8b/e2bbeb42068f0c48899e8eddd34902afc0f7429d4d2a152d2dc2670dc661/pythreejs-2.4.2-py3-none-any.whl
+ name: pythreejs
+ version: 2.4.2
+ sha256: 8418807163ad91f4df53b58c4e991b26214852a1236f28f1afeaadf99d095818
+ requires_dist:
+ - ipywidgets>=7.2.1
+ - ipydatawidgets>=1.1.1
+ - numpy
+ - traitlets
+ - sphinx>=1.5 ; extra == 'docs'
+ - nbsphinx>=0.2.13 ; extra == 'docs'
+ - nbsphinx-link ; extra == 'docs'
+ - sphinx-rtd-theme ; extra == 'docs'
+ - scipy ; extra == 'examples'
+ - matplotlib ; extra == 'examples'
+ - scikit-image ; extra == 'examples'
+ - ipywebrtc ; extra == 'examples'
+ - nbval ; extra == 'test'
+ - 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.2
- sha256: 327790d70e4c841ebd9d0f295a780177149aeb405bca44c7115a3de5c2054b23
+ version: 3.0.3
+ sha256: dff25a9a6435f527d7c65608a7e62783fc12076e7d44487a4911ee91be5a8ac8
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/fc/19/b757fe28008236a4a713e813283721b8a40aa60cd7d3f83549f2e25a3155/pywinpty-3.0.2-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl
name: pywinpty
- version: 3.0.2
- sha256: 18f78b81e4cfee6aabe7ea8688441d30247b73e52cd9657138015c5f4ee13a51
+ version: 3.0.3
+ sha256: 9c91dbb026050c77bdcef964e63a4f10f01a639113c4d3658332614544c467ab
requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl
name: pyyaml
@@ -9636,6 +10862,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
@@ -9688,18 +10921,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
@@ -9730,10 +10969,10 @@ packages:
- lark>=1.2.2
- pytest>=8.3.5 ; extra == 'testing'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl
name: rich
- version: 14.2.0
- sha256: 76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd
+ version: 14.3.3
+ sha256: 793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d
requires_dist:
- ipywidgets>=7.5.1,<9 ; extra == 'jupyter'
- markdown-it-py>=2.2.0
@@ -9779,32 +11018,296 @@ packages:
version: 0.30.0
sha256: e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8
requires_python: '>=3.10'
-- pypi: https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl
name: ruff
- version: 0.14.10
- sha256: d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d
+ version: 0.15.8
+ sha256: 04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_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.14.10
- sha256: 59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f
+ version: 0.15.8
+ sha256: 6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-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
name: ruff
- version: 0.14.10
- sha256: 674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f
+ version: 0.15.8
+ sha256: d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.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.14.10
- sha256: 466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154
+ version: 0.15.8
+ sha256: 0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/af/46/661159ad844034ba8b3f4e0516215c41e4ee17db4213d13a82227670764f/sciline-25.11.1-py3-none-any.whl
+ name: sciline
+ version: 25.11.1
+ sha256: 13c378287b8157e819b9b67d7e973c65bc6bdc545a3602d18204c365b0c336f9
+ requires_dist:
+ - cyclebane>=24.6.0
+ - pytest ; extra == 'test'
+ - pytest-randomly>=3 ; extra == 'test'
+ - dask ; extra == 'test'
+ - graphviz ; extra == 'test'
+ - jsonschema ; extra == 'test'
+ - numpy ; extra == 'test'
+ - pandas ; extra == 'test'
+ - pydantic ; extra == 'test'
+ - rich ; extra == 'test'
+ - rich ; extra == 'progress'
+ requires_python: '>=3.11'
+- 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: 26.3.1
+ sha256: 993706e7c31f0317be2db5f528f9142ba67b2e52d7af174fcad195f702e1d6c7
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/37/fd/22621d3ee9e3ee87ef4c89b63bba55b265ab85039b3c1ba88ed2380a24c1/scipp-26.3.1-cp313-cp313-win_amd64.whl
+ name: scipp
+ version: 26.3.1
+ sha256: 03c6dbf8936a2ed62587c5abe8ab5266a5098834a0709321ce799bd1328eb3e6
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/60/54/5011adb56853caabfd90686c2e543d1e3c76a8ef2755809b7e12e3f3583b/scipp-26.3.1-cp311-cp311-macosx_14_0_arm64.whl
+ name: scipp
+ version: 26.3.1
+ sha256: 67d275fc83b062053df9aa7ce3af4d2205109c2bc3ab22467bcd73ceb0a83df2
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/79/fe/b14d806894cf05178f1e77d0d619f071db50cf698bc654c54f9241223bcf/scipp-26.3.1-cp313-cp313-macosx_14_0_arm64.whl
+ name: scipp
+ version: 26.3.1
+ sha256: 8dfe8adedb5cba05acaaea15e3b6fe1820ac2f497e87c1e581ba4be9d82c53bb
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/81/21/4962b1daddf0422e56c5ed4c41bea1ccb6d2a9ab72b795196835a20969c7/scipp-26.3.1-cp311-cp311-macosx_14_0_x86_64.whl
+ name: scipp
+ version: 26.3.1
+ sha256: 7c90e78fcba1d272df059fc01350c9e18f017aec26369b03def723a3702d763d
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- 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: 26.3.1
+ sha256: ab5859a24b3150b588dd2c67e68b0c7f07c9444eae501f3b6326d6b4a34cbf10
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/e2/69/1dcb8e967f62759578938db5b29792b82ea8939a2d712e79491fa3e1cf0a/scipp-26.3.1-cp313-cp313-macosx_14_0_x86_64.whl
+ name: scipp
+ version: 26.3.1
+ sha256: 4c9c8632ba24ce74bd98430a1376310fa5b3fcd2c3467a7e6a484ebb091e915f
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/e6/0d/8882a4c7a5ebe59a46b709e82411d9c730d67250d41a2e11bc4bcd4d431d/scipp-26.3.1-cp311-cp311-win_amd64.whl
+ name: scipp
+ version: 26.3.1
+ sha256: 37877cf07b4f54f224d5465c265d6a1e591d605d0c23dd350a4b48d95c26ab7b
+ requires_dist:
+ - numpy>=2
+ - pytest ; extra == 'test'
+ - matplotlib ; extra == 'test'
+ - beautifulsoup4 ; extra == 'test'
+ - ipython ; extra == 'test'
+ - h5py ; extra == 'extra'
+ - scipy>=1.7.0 ; extra == 'extra'
+ - graphviz ; extra == 'extra'
+ - pooch ; extra == 'extra'
+ - plopp ; extra == 'extra'
+ - matplotlib ; extra == 'extra'
+ - scipp[extra] ; extra == 'all'
+ - ipympl ; extra == 'all'
+ - ipython ; extra == 'all'
+ - ipywidgets ; extra == 'all'
+ - jupyterlab ; extra == 'all'
+ - jupyterlab-widgets ; extra == 'all'
+ - jupyter-nbextensions-configurator ; extra == 'all'
+ - nodejs ; extra == 'all'
+ - pythreejs ; extra == 'all'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/fb/46/e50b38629e9e3f4a1dd55fb36d8b8abd1d59768c31151c1c8ed696f7b865/scippneutron-26.3.0-py3-none-any.whl
+ name: scippneutron
+ version: 26.3.0
+ sha256: 6bc9e36f68059bb792460cc897e6247236289f170134a953ed9fee8578872dd7
+ requires_dist:
+ - python-dateutil>=2.8
+ - email-validator>=2
+ - h5py>=3.12
+ - lazy-loader>=0.4
+ - mpltoolbox>=24.6.0
+ - numpy>=1.20
+ - plopp>=24.9.1
+ - pydantic>=2
+ - scipp>=24.7.0
+ - scippnexus>=23.11.0
+ - scipy>=1.7.0
+ - scipp[all]>=23.7.0 ; extra == 'all'
+ - pooch>=1.5 ; extra == 'all'
+ - hypothesis>=6.100 ; extra == 'test'
+ - ipympl>0.9.0 ; extra == 'test'
+ - ipykernel>6.30 ; extra == 'test'
+ - pace-neutrons>=0.3 ; extra == 'test'
+ - pooch>=1.5 ; extra == 'test'
+ - psutil>=5.0 ; extra == 'test'
+ - pytest>=7.0 ; extra == 'test'
+ - pytest-xdist>=3.0 ; extra == 'test'
+ - pythreejs>=2.4.1 ; extra == 'test'
+ - sciline>=25.1.0 ; extra == 'test'
+ requires_python: '>=3.11'
+- pypi: https://files.pythonhosted.org/packages/5c/01/6cb4d63c6b6933be4b7945b2f64638336420f04ea71ca5b9a7539c008bc5/scippnexus-26.1.1-py3-none-any.whl
+ name: scippnexus
+ version: 26.1.1
+ sha256: 899a0a5e71291b7809d902c17b6c74addf5a805397eabcec557491ff74eead12
+ requires_dist:
+ - scipp>=24.2.0
+ - scipy>=1.10.0
+ - h5py>=3.12
+ - pytest>=7.0 ; extra == 'test'
+ requires_python: '>=3.11'
+- 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
name: scipy
- version: 1.16.3
- sha256: 7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88
+ version: 1.17.1
+ sha256: 43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -9833,22 +11336,22 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl
name: scipy
- version: 1.16.3
- sha256: 16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d
+ version: 1.17.1
+ sha256: 37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -9877,22 +11380,22 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl
name: scipy
- version: 1.16.3
- sha256: d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c
+ version: 1.17.1
+ sha256: 7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -9921,22 +11424,22 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl
name: scipy
- version: 1.16.3
- sha256: 8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511
+ version: 1.17.1
+ sha256: a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -9965,22 +11468,22 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl
name: scipy
- version: 1.16.3
- sha256: 40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97
+ version: 1.17.1
+ sha256: d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -10009,22 +11512,22 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl
name: scipy
- version: 1.16.3
- sha256: 0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2
+ version: 1.17.1
+ sha256: 4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -10053,22 +11556,22 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl
name: scipy
- version: 1.16.3
- sha256: 062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304
+ version: 1.17.1
+ sha256: 766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -10097,22 +11600,22 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.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
name: scipy
- version: 1.16.3
- sha256: 53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78
+ version: 1.17.1
+ sha256: 581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464
requires_dist:
- - numpy>=1.25.2,<2.6
+ - numpy>=1.26.4,<2.7
- pytest>=8.0.0 ; extra == 'test'
- pytest-cov ; extra == 'test'
- pytest-timeout ; extra == 'test'
@@ -10141,30 +11644,29 @@ packages:
- jupyterlite-sphinx>=0.19.1 ; extra == 'doc'
- jupyterlite-pyodide-kernel ; extra == 'doc'
- linkify-it-py ; extra == 'doc'
+ - tabulate ; extra == 'doc'
+ - click<8.3.0 ; extra == 'dev'
+ - spin ; extra == 'dev'
- mypy==1.10.0 ; extra == 'dev'
- typing-extensions ; extra == 'dev'
- types-psutil ; extra == 'dev'
- pycodestyle ; extra == 'dev'
- - ruff>=0.0.292 ; extra == 'dev'
+ - ruff>=0.12.0 ; extra == 'dev'
- cython-lint>=0.12.2 ; extra == 'dev'
- - rich-click ; extra == 'dev'
- - doit>=0.36.0 ; extra == 'dev'
- - pydevtool ; extra == 'dev'
requires_python: '>=3.11'
-- pypi: https://files.pythonhosted.org/packages/1b/5a/f2f2e5eda25579f754acd83399c522ee03d6acbe001dfe53c8a1ec928b44/send2trash-2.0.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl
name: send2trash
- version: 2.0.0
- sha256: e70d5ce41dbb890882cc78bc25d137478330b39a391e756fadf82e34da4d85b8
- requires_dist:
- - pywin32 ; sys_platform == 'win32' and extra == 'win32'
- - pyobjc-framework-cocoa ; sys_platform == 'darwin' and extra == 'objc'
- - pywin32 ; sys_platform == 'win32' and extra == 'nativelib'
- - pyobjc-framework-cocoa ; sys_platform == 'darwin' and extra == 'nativelib'
- requires_python: '!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*'
-- pypi: https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl
+ version: 2.1.0
+ sha256: 0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c
+ requires_dist:
+ - pytest>=8 ; extra == 'test'
+ - pywin32>=305 ; sys_platform == 'win32' and extra == 'nativelib'
+ - pyobjc>=9.0 ; sys_platform == 'darwin' and extra == 'nativelib'
+ requires_python: '>=3.8'
+- pypi: https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl
name: setuptools
- version: 80.9.0
- sha256: 062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922
+ version: 82.0.1
+ sha256: a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb
requires_dist:
- pytest>=6,!=8.1.* ; extra == 'test'
- virtualenv>=13.0.0 ; extra == 'test'
@@ -10205,16 +11707,15 @@ packages:
- importlib-metadata>=6 ; python_full_version < '3.10' and extra == 'core'
- tomli>=2.0.1 ; python_full_version < '3.11' and extra == 'core'
- wheel>=0.43.0 ; extra == 'core'
- - platformdirs>=4.2.2 ; extra == 'core'
- jaraco-functools>=4 ; extra == 'core'
- more-itertools ; extra == 'core'
- pytest-checkdocs>=2.4 ; extra == 'check'
- pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check'
- - ruff>=0.8.0 ; sys_platform != 'cygwin' and extra == 'check'
+ - ruff>=0.13.0 ; sys_platform != 'cygwin' and extra == 'check'
- pytest-cov ; extra == 'cover'
- pytest-enabler>=2.2 ; extra == 'enabler'
- pytest-mypy ; extra == 'type'
- - mypy==1.14.* ; extra == 'type'
+ - mypy==1.18.* ; extra == 'type'
- importlib-metadata>=7.0.2 ; python_full_version < '3.10' and extra == 'type'
- jaraco-develop>=7.21 ; sys_platform != 'cygwin' and extra == 'type'
requires_python: '>=3.9'
@@ -10240,15 +11741,288 @@ packages:
version: 1.17.0
sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274
requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*'
-- pypi: https://files.pythonhosted.org/packages/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl
+- 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.1
- sha256: a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434
+ 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
+ sha256: ff1632524f6ac0031423474e48d6b69f4932ecb7eb4446a501f59619e2b5cbc9
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/1b/a5/174d33068d4383df4be9ee1ea9251f17820a622f3be744ca2ab7334818ee/spglib-2.6.0-cp313-cp313-macosx_10_13_x86_64.whl
+ name: spglib
+ version: 2.6.0
+ sha256: d7872819b6448024ecf7dfccac45147d8cac839bdfff6763b92784aae63bd139
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/25/a8/d89e1bde525baba10eb8d0be79a5bbaf56c59a47b32bb954866d96a228e3/spglib-2.6.0-cp311-cp311-win_amd64.whl
+ name: spglib
+ version: 2.6.0
+ sha256: 9a72daeefcabf62ca88eff77adacf5b2b9d91a17c4f93d61d86f635476de8400
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/2d/47/634fe8323c6c2bfa86e10eb41ebfe410db5e6231aa1727a31ce4f002480f/spglib-2.6.0-cp313-cp313-macosx_11_0_arm64.whl
+ name: spglib
+ version: 2.6.0
+ sha256: 12db7a0d6ad84c55e61eda67590a438edeb48e57ffd5df868cd931b57fb8c630
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/53/01/1c0485ae02e645bc517bf5d5a6ca674f62c97e247890b954cbfe85c64dae/spglib-2.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
+ name: spglib
+ version: 2.6.0
+ sha256: cc9d856d7cc936dc73b6b303aaf8b2fb4230a8527659373450c6e1139cbb2a5c
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/55/41/591cd1e94254c20f00bb1f32c0b1a6de68c03d54e6daf78dd7b146d0b3fc/spglib-2.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
+ name: spglib
+ version: 2.6.0
+ sha256: ff6b2f21226f7ece7758eb1d320907168018aa0a30a57c2b0a24cbf8f6860211
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
+ requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/6c/44/30888e2a5b2fa2e6df18606b442cb8b126b0bea5a2f1ec4a2a82538ffecf/spglib-2.6.0-cp311-cp311-macosx_10_9_x86_64.whl
+ name: spglib
+ version: 2.6.0
+ sha256: c22d87849e1086cbe88399c08c93b4e7babec92c1db49f15ef8b081339b67e25
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/0e/50/80a8d080ac7d3d321e5e5d420c9a522b0aa770ec7013ea91f9a8b7d36e4a/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/f6/ca/270d463f6c34f539bb55acdab14099c092d3be28c8af64d61399aa07610c/spglib-2.6.0-cp311-cp311-macosx_11_0_arm64.whl
+ name: spglib
+ version: 2.6.0
+ sha256: 02d2e730a3b2cb43e318944493d0c288592b0e2ddbab0d222a548312659ee22a
+ requires_dist:
+ - numpy>=1.20,<3
+ - importlib-resources ; python_full_version < '3.10'
+ - typing-extensions>=4.9.0 ; python_full_version < '3.13'
+ - pytest ; extra == 'test'
+ - pyyaml ; extra == 'test'
+ - sphinx>=7.0 ; extra == 'docs'
+ - sphinxcontrib-bibtex>=2.5 ; extra == 'docs'
+ - sphinx-book-theme ; extra == 'docs'
+ - sphinx-autodoc-typehints ; extra == 'docs'
+ - myst-parser>=2.0 ; extra == 'docs'
+ - linkify-it-py ; extra == 'docs'
+ - sphinx-tippy ; extra == 'docs'
+ - spglib[test] ; extra == 'test-cov'
+ - pytest-cov ; extra == 'test-cov'
+ - spglib[test] ; extra == 'test-benchmark'
+ - pytest-benchmark ; extra == 'test-benchmark'
+ - spglib[test] ; extra == 'dev'
+ - pre-commit ; extra == 'dev'
+ - spglib[docs] ; extra == 'doc'
+ - spglib[test] ; extra == 'testing'
+ requires_python: '>=3.9'
+- 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
+ name: sqlalchemy
+ version: 2.0.48
+ sha256: 6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7
+ requires_dist:
+ - importlib-metadata ; python_full_version < '3.8'
+ - greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
+ - typing-extensions>=4.6.0
+ - greenlet>=1 ; extra == 'asyncio'
+ - mypy>=0.910 ; extra == 'mypy'
+ - pyodbc ; extra == 'mssql'
+ - pymssql ; extra == 'mssql-pymssql'
+ - pyodbc ; extra == 'mssql-pyodbc'
+ - mysqlclient>=1.4.0 ; extra == 'mysql'
+ - mysql-connector-python ; extra == 'mysql-connector'
+ - mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10 ; extra == 'mariadb-connector'
+ - cx-oracle>=8 ; extra == 'oracle'
+ - oracledb>=1.0.1 ; extra == 'oracle-oracledb'
+ - psycopg2>=2.7 ; extra == 'postgresql'
+ - pg8000>=1.29.1 ; extra == 'postgresql-pg8000'
+ - greenlet>=1 ; extra == 'postgresql-asyncpg'
+ - asyncpg ; extra == 'postgresql-asyncpg'
+ - psycopg2-binary ; extra == 'postgresql-psycopg2binary'
+ - psycopg2cffi ; extra == 'postgresql-psycopg2cffi'
+ - psycopg>=3.0.7 ; extra == 'postgresql-psycopg'
+ - psycopg[binary]>=3.0.7 ; extra == 'postgresql-psycopgbinary'
+ - pymysql ; extra == 'pymysql'
+ - greenlet>=1 ; extra == 'aiomysql'
+ - aiomysql>=0.2.0 ; extra == 'aiomysql'
+ - greenlet>=1 ; extra == 'aioodbc'
+ - aioodbc ; extra == 'aioodbc'
+ - greenlet>=1 ; extra == 'asyncmy'
+ - asyncmy>=0.2.3,!=0.2.4,!=0.2.6 ; extra == 'asyncmy'
+ - greenlet>=1 ; extra == 'aiosqlite'
+ - aiosqlite ; extra == 'aiosqlite'
+ - typing-extensions!=3.10.0.1 ; extra == 'aiosqlite'
+ - sqlcipher3-binary ; extra == 'sqlcipher'
+ requires_python: '>=3.7'
+- pypi: https://files.pythonhosted.org/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl
name: sqlalchemy
- version: 2.0.45
- sha256: 672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e
+ version: 2.0.48
+ sha256: a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096
requires_dist:
- importlib-metadata ; python_full_version < '3.8'
- greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
@@ -10283,10 +12057,10 @@ packages:
- typing-extensions!=3.10.0.1 ; extra == 'aiosqlite'
- sqlcipher3-binary ; extra == 'sqlcipher'
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/a2/1c/769552a9d840065137272ebe86ffbb0bc92b0f1e0a68ee5266a225f8cd7b/sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.whl
+- pypi: https://files.pythonhosted.org/packages/58/d5/dd767277f6feef12d05651538f280277e661698f617fa4d086cce6055416/sqlalchemy-2.0.48-cp311-cp311-win_amd64.whl
name: sqlalchemy
- version: 2.0.45
- sha256: 2e90a344c644a4fa871eb01809c32096487928bd2038bf10f3e4515cb688cc56
+ version: 2.0.48
+ sha256: 583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c
requires_dist:
- importlib-metadata ; python_full_version < '3.8'
- greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
@@ -10321,10 +12095,10 @@ packages:
- typing-extensions!=3.10.0.1 ; extra == 'aiosqlite'
- sqlcipher3-binary ; extra == 'sqlcipher'
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/bc/fa/09d0a11fe9f15c7fa5c7f0dd26be3d235b0c0cbf2f9544f43bc42efc8a24/sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl
name: sqlalchemy
- version: 2.0.45
- sha256: a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2
+ version: 2.0.48
+ sha256: d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2
requires_dist:
- importlib-metadata ; python_full_version < '3.8'
- greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
@@ -10359,10 +12133,10 @@ packages:
- typing-extensions!=3.10.0.1 ; extra == 'aiosqlite'
- sqlcipher3-binary ; extra == 'sqlcipher'
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl
name: sqlalchemy
- version: 2.0.45
- sha256: 5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0
+ version: 2.0.48
+ sha256: e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4
requires_dist:
- importlib-metadata ; python_full_version < '3.8'
- greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
@@ -10397,10 +12171,10 @@ packages:
- typing-extensions!=3.10.0.1 ; extra == 'aiosqlite'
- sqlcipher3-binary ; extra == 'sqlcipher'
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/c0/c5/d17113020b2d43073412aeca09b60d2009442420372123b8d49cc253f8b8/sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl
+- pypi: https://files.pythonhosted.org/packages/d7/6d/b8b78b5b80f3c3ab3f7fa90faa195ec3401f6d884b60221260fd4d51864c/sqlalchemy-2.0.48-cp311-cp311-macosx_11_0_arm64.whl
name: sqlalchemy
- version: 2.0.45
- sha256: afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee
+ version: 2.0.48
+ sha256: 1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc
requires_dist:
- importlib-metadata ; python_full_version < '3.8'
- greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
@@ -10435,10 +12209,10 @@ packages:
- typing-extensions!=3.10.0.1 ; extra == 'aiosqlite'
- sqlcipher3-binary ; extra == 'sqlcipher'
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/f6/2b/60ce3ee7a5ae172bfcd419ce23259bb874d2cddd44f67c5df3760a1e22f9/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_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
name: sqlalchemy
- version: 2.0.45
- sha256: 12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac
+ version: 2.0.48
+ sha256: b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed
requires_dist:
- importlib-metadata ; python_full_version < '3.8'
- greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'
@@ -10495,26 +12269,26 @@ packages:
- pytest>=7.1.0 ; extra == 'dev'
- hypothesis>=6.70.0 ; extra == 'dev'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl
name: tabulate
- version: 0.9.0
- sha256: 024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f
+ version: 0.10.0
+ sha256: f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3
requires_dist:
- wcwidth ; extra == 'widechars'
- requires_python: '>=3.7'
-- conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda
- sha256: c31cac57913a699745d124cdc016a63e31c5749f16f60b3202414d071fc50573
- md5: 17c38aaf14c640b85c4617ccb59c1146
+ 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.1,<2.12.2.0a0
+ - 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: 155714
- timestamp: 1762510341121
+ 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
@@ -10543,118 +12317,135 @@ packages:
- pytest ; extra == 'test'
- ruff ; extra == 'test'
requires_python: '>=3.8'
-- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda
- sha256: 1544760538a40bcd8ace2b1d8ebe3eb5807ac268641f8acdc18c69c5ebfeaf64
- md5: 86bc20552bf46075e3d92b67f089172d
+- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda
+ sha256: cafeec44494f842ffeca27e9c8b0c27ed714f93ac77ddadc6aaf726b5554ebac
+ md5: cffd3bdd58090148f4cfcd831f4b26ab
depends:
- __glibc >=2.17,<3.0.a0
- - libgcc >=13
+ - libgcc >=14
- libzlib >=1.3.1,<2.0a0
constrains:
- xorg-libx11 >=1.8.12,<2.0a0
license: TCL
license_family: BSD
purls: []
- size: 3284905
- timestamp: 1763054914403
-- conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_3.conda
- sha256: 0d0b6cef83fec41bc0eb4f3b761c4621b7adfb14378051a8177bd9bb73d26779
- md5: bd9f1de651dbd80b51281c694827f78f
+ size: 3301196
+ timestamp: 1769460227866
+- conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda
+ sha256: 7f0d9c320288532873e2d8486c331ec6d87919c9028208d3f6ac91dc8f99a67b
+ md5: 6e6efb7463f8cef69dbcb4c2205bf60e
depends:
- __osx >=10.13
- libzlib >=1.3.1,<2.0a0
license: TCL
license_family: BSD
purls: []
- size: 3262702
- timestamp: 1763055085507
-- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda
- sha256: ad0c67cb03c163a109820dc9ecf77faf6ec7150e942d1e8bb13e5d39dc058ab7
- md5: a73d54a5abba6543cb2f0af1bfbd6851
+ size: 3282953
+ timestamp: 1769460532442
+- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda
+ sha256: 799cab4b6cde62f91f750149995d149bc9db525ec12595e8a1d91b9317f038b3
+ md5: a9d86bc62f39b94c4661716624eb21b0
depends:
- __osx >=11.0
- libzlib >=1.3.1,<2.0a0
license: TCL
license_family: BSD
purls: []
- size: 3125484
- timestamp: 1763055028377
-- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda
- sha256: 4581f4ffb432fefa1ac4f85c5682cc27014bcd66e7beaa0ee330e927a7858790
- md5: 7cb36e506a7dba4817970f8adb6396f9
+ size: 3127137
+ timestamp: 1769460817696
+- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda
+ sha256: 0e79810fae28f3b69fe7391b0d43f5474d6bd91d451d5f2bde02f55ae481d5e3
+ md5: 0481bfd9814bf525bd4b3ee4b51494c4
depends:
- ucrt >=10.0.20348.0
- - vc >=14.2,<15
- - vc14_runtime >=14.29.30139
+ - vc >=14.3,<15
+ - vc14_runtime >=14.44.35208
license: TCL
license_family: BSD
purls: []
- size: 3472313
- timestamp: 1763055164278
+ size: 3526350
+ timestamp: 1769460339384
+- pypi: https://files.pythonhosted.org/packages/f5/57/2a154a69d6642860300bf8eb205d13131104991f2b1065bbb9075ac5c32e/tof-26.3.0-py3-none-any.whl
+ name: tof
+ version: 26.3.0
+ sha256: e89783a072b05fdb53d9e76fbf919dc8935e75e118fdaf17ca5cc33727ef002b
+ requires_dist:
+ - plopp>=23.10.0
+ - pooch>=1.5.0
+ - scipp>=25.1.0
+ - lazy-loader>=0.3
+ - pytest>=8.0 ; extra == 'test'
+ - scippneutron>=24.12.0 ; extra == 'test'
+ requires_python: '>=3.11'
- pypi: https://files.pythonhosted.org/packages/33/f0/3fe8c6e69135a845f4106f2ff8b6805638d4e85c264e70114e8126689587/tokenize_rt-6.2.0-py2.py3-none-any.whl
name: tokenize-rt
version: 6.2.0
sha256: a152bf4f249c847a66497a4a95f63376ed68ac6abf092a2f7cfb29d044ecff44
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.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.3.0
- sha256: 4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be
+ version: 2.4.1
+ sha256: 36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-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.3.0
- sha256: 883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba
+ version: 2.4.1
+ sha256: f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.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.3.0
- sha256: 5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b
+ version: 2.4.1
+ sha256: eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl
name: tomli
- version: 2.3.0
- sha256: a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441
+ version: 2.4.1
+ sha256: 5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.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.3.0
- sha256: 0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999
+ version: 2.4.1
+ sha256: 5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl
name: tomli
- version: 2.3.0
- sha256: 88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45
+ version: 2.4.1
+ sha256: 8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.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.3.0
- sha256: 4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf
+ version: 2.4.1
+ sha256: 4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.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.3.0
- sha256: be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae
+ version: 2.4.1
+ sha256: f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30
requires_python: '>=3.8'
-- 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/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl
+ name: toolz
+ version: 1.1.0
+ sha256: 15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8
+ requires_python: '>=3.9'
+- 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
@@ -10671,33 +12462,50 @@ packages:
- pytest-mypy-testing ; extra == 'test'
- pytest>=7.0,<8.2 ; extra == 'test'
requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/4f/7e/bc19996fa86cad8801e8ffe6f1bba5836ca0160df76d0410d27432193712/trove_classifiers-2025.12.1.14-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/8d/c0/fdf9d3ee103ce66a55f0532835ad5e154226c5222423c6636ba049dc42fc/traittypes-0.2.3-py2.py3-none-any.whl
+ name: traittypes
+ version: 0.2.3
+ sha256: 49016082ce740d6556d9bb4672ee2d899cd14f9365f17cbb79d5d96b47096d4e
+ requires_dist:
+ - traitlets>=4.2.2
+ - numpy ; extra == 'test'
+ - pandas ; extra == 'test'
+ - xarray ; extra == 'test'
+ - pytest ; extra == 'test'
+- pypi: https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl
name: trove-classifiers
- version: 2025.12.1.14
- sha256: a8206978ede95937b9959c3aff3eb258bbf7b07dff391ddd4ea7e61f316635ab
-- pypi: https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl
+ version: 2026.1.14.14
+ sha256: 1f9553927f18d0513d8e5ff80ab8980b8202ce37ecae0e3274ed2ef11880e74d
+- pypi: https://files.pythonhosted.org/packages/91/88/b55b3117287a8540b76dbdd87733808d4d01c8067a3b339408c250bb3600/typeguard-4.5.1-py3-none-any.whl
name: typeguard
- version: 4.4.4
- sha256: b5f562281b6bfa1f5492470464730ef001646128b180769880468bd84b68b09e
+ version: 4.5.1
+ sha256: 44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40
requires_dist:
- importlib-metadata>=3.6 ; python_full_version < '3.10'
- typing-extensions>=4.14.0
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl
name: typer
- version: 0.21.1
- sha256: 7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01
+ version: 0.24.1
+ sha256: 112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e
requires_dist:
- - click>=8.0.0
- - typing-extensions>=3.7.4.3
+ - click>=8.2.1
- shellingham>=1.3.0
- - rich>=10.11.0
- requires_python: '>=3.9'
+ - rich>=12.3.0
+ - annotated-doc>=0.0.2
+ requires_python: '>=3.10'
- pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl
name: typing-extensions
version: 4.15.0
sha256: f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl
+ name: typing-inspection
+ version: 0.4.2
+ sha256: 4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7
+ requires_dist:
+ - typing-extensions>=4.12.0
+ requires_python: '>=3.9'
- pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl
name: tzdata
version: '2025.3'
@@ -10735,10 +12543,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
@@ -10765,10 +12569,10 @@ packages:
- flake8-use-fstring ; extra == 'dev'
- pep8-naming ; extra == 'dev'
requires_python: '>=3.7'
-- pypi: https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl
name: urllib3
- version: 2.6.2
- sha256: ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd
+ version: 2.6.3
+ sha256: bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
requires_dist:
- brotli>=1.2.0 ; platform_python_implementation == 'CPython' and extra == 'brotli'
- brotlicffi>=1.2.0.0 ; platform_python_implementation != 'CPython' and extra == 'brotli'
@@ -10776,30 +12580,10 @@ 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/32/49/9e3e19ba756c4a5e6acb4ea74336d3035f7959254fbb05f5eb77bff067ed/uv-0.9.22-py3-none-win_amd64.whl
- name: uv
- version: 0.9.22
- sha256: 9c238525272506845fe07c0b9088c5e33fcd738e1f49ef49dc3c8112096d2e3a
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/40/15/63fb7a6908db2f03716c4a50aea7e27a7440fe6a09854282c401139afaf7/uv-0.9.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- name: uv
- version: 0.9.22
- sha256: 1f45e1e0f26dd47fa01eb421c54cfd39de10fd52ac0a9d7ae45b92fce7d92b0b
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/5e/68/bb76c97c284ce7fb8efa868994c2510588faa7075e60d8865d1373e54b7b/uv-0.9.22-py3-none-macosx_10_12_x86_64.whl
- name: uv
- version: 0.9.22
- sha256: b78f2605d65c4925631d891dec99b677b05f50c774dedc6ef8968039a5bcfdb0
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/af/49/7230b1d56aeaee0eefd346a70f582463f11fb7036d2d020bcf68053bd994/uv-0.9.22-py3-none-macosx_11_0_arm64.whl
- name: uv
- version: 0.9.22
- sha256: 2a4155cf7d0231d0adae94257ee10d70c57c2f592207536ddd55d924590a8c15
- requires_python: '>=3.8'
-- pypi: https://files.pythonhosted.org/packages/a4/39/6983dd79f01aaa4c75d9ffa550fa393f0c4c28f7ccd6956e4188c62cefbc/validate_pyproject-0.24.1-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/b7/ee/e9c95cda829131f71a8dff5ce0406059fd16e591c074414e31ada19ba7c3/validate_pyproject-0.25-py3-none-any.whl
name: validate-pyproject
- version: 0.24.1
- sha256: b7b05fa9117204c9c4606ab317acd095b18d1bfc78fb7dc8cc06f77d0582ca2d
+ version: '0.25'
+ sha256: f9d05e2686beff82f9ea954f582306b036ced3d3feb258c1110f2c2a495b1981
requires_dist:
- fastjsonschema>=2.16.2,<=3
- packaging>=24.2 ; extra == 'all'
@@ -10873,35 +12657,18 @@ packages:
- mypy ; extra == 'test'
- pretend ; extra == 'test'
- pytest ; extra == 'test'
-- pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl
name: virtualenv
- version: 20.35.4
- sha256: c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b
+ version: 21.2.0
+ sha256: 1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f
requires_dist:
- distlib>=0.3.7,<1
- - filelock>=3.12.2,<4
+ - filelock>=3.24.2,<4 ; python_full_version >= '3.10'
+ - filelock>=3.16.1,<=3.19.1 ; python_full_version < '3.10'
- importlib-metadata>=6.6 ; python_full_version < '3.8'
- platformdirs>=3.9.1,<5
+ - python-discovery>=1
- typing-extensions>=4.13.2 ; python_full_version < '3.11'
- - furo>=2023.7.26 ; extra == 'docs'
- - proselint>=0.13 ; extra == 'docs'
- - sphinx>=7.1.2,!=7.3 ; extra == 'docs'
- - sphinx-argparse>=0.4 ; extra == 'docs'
- - sphinxcontrib-towncrier>=0.2.1a0 ; extra == 'docs'
- - towncrier>=23.6 ; extra == 'docs'
- - covdefaults>=2.3 ; extra == 'test'
- - coverage-enable-subprocess>=1 ; extra == 'test'
- - coverage>=7.2.7 ; extra == 'test'
- - flaky>=3.7 ; extra == 'test'
- - packaging>=23.1 ; extra == 'test'
- - pytest-env>=0.8.2 ; extra == 'test'
- - pytest-freezer>=0.4.8 ; (python_full_version >= '3.13' and platform_python_implementation == 'CPython' and sys_platform == 'win32' and extra == 'test') or (platform_python_implementation == 'GraalVM' and extra == 'test') or (platform_python_implementation == 'PyPy' and extra == 'test')
- - pytest-mock>=3.11.1 ; extra == 'test'
- - pytest-randomly>=3.12 ; extra == 'test'
- - pytest-timeout>=2.1 ; extra == 'test'
- - pytest>=7.4 ; extra == 'test'
- - setuptools>=68 ; extra == 'test'
- - time-machine>=2.10 ; platform_python_implementation == 'CPython' and extra == 'test'
requires_python: '>=3.8'
- pypi: https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl
name: watchdog
@@ -10945,11 +12712,11 @@ packages:
requires_dist:
- pyyaml>=3.10 ; extra == 'watchmedo'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl
+- pypi: https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl
name: wcwidth
- version: 0.2.14
- sha256: a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1
- requires_python: '>=3.6'
+ version: 0.6.0
+ sha256: 1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad
+ requires_python: '>=3.8'
- pypi: https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl
name: webcolors
version: 25.10.0
@@ -10972,6 +12739,11 @@ packages:
- sphinx-rtd-theme>=1.1.0 ; extra == 'docs'
- myst-parser>=2.0.0 ; extra == 'docs'
requires_python: '>=3.9'
+- pypi: https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl
+ name: widgetsnbextension
+ version: 4.0.15
+ sha256: 8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366
+ requires_python: '>=3.7'
- pypi: https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl
name: wsproto
version: 1.3.2
@@ -10996,78 +12768,78 @@ packages:
- coverage ; extra == 'test'
- xraydb[dev,doc,test] ; extra == 'all'
requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl
+- pypi: https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl
name: yarl
- version: 1.22.0
- sha256: 01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a
+ version: 1.23.0
+ sha256: cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
name: yarl
- version: 1.22.0
- sha256: bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b
+ version: 1.23.0
+ sha256: 34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl
name: yarl
- version: 1.22.0
- sha256: 22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c
+ version: 1.23.0
+ sha256: 4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl
name: yarl
- version: 1.22.0
- sha256: 4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d
+ version: 1.23.0
+ sha256: baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl
name: yarl
- version: 1.22.0
- sha256: 669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6
+ version: 1.23.0
+ sha256: bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
name: yarl
- version: 1.22.0
- sha256: 47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d
+ version: 1.23.0
+ sha256: 99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl
name: yarl
- version: 1.22.0
- sha256: 078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b
+ version: 1.23.0
+ sha256: 7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
-- pypi: https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl
+ requires_python: '>=3.10'
+- pypi: https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl
name: yarl
- version: 1.22.0
- sha256: 792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028
+ version: 1.23.0
+ sha256: dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432
requires_dist:
- idna>=2.0
- multidict>=4.0
- propcache>=0.2.1
- requires_python: '>=3.9'
+ requires_python: '>=3.10'
- pypi: https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl
name: zipp
version: 3.23.0
diff --git a/pixi.toml b/pixi.toml
index 04f71c03..37952beb 100644
--- a/pixi.toml
+++ b/pixi.toml
@@ -5,7 +5,7 @@
# Platform-independent
[activation.env]
-PYTHONIOENCODING = "utf-8"
+PYTHONIOENCODING = 'utf-8'
# Platform-specific
@@ -27,12 +27,25 @@ PYTHONPATH = "${PIXI_PROJECT_ROOT}/src;%PYTHONPATH%"
###########
[workspace]
+
# Supported platforms for the lock file (pixi.lock)
platforms = ['win-64', 'linux-64', 'osx-64', 'osx-arm64']
# Channels for fetching packages
channels = ['conda-forge']
+#####################
+# SYSTEM REQUIREMENTS
+#####################
+
+[system-requirements]
+
+# Set minimum supported version for macOS to be 14.0 to ensure packages
+# like `skipp` that only have wheels for macOS 14.0+ (macosx_14_0_arm64)
+# are used instead of building from source. This is a workaround for
+# Pixi, see https://github.com/prefix-dev/pixi/issues/5667
+macos = '14.0'
+
##########
# FEATURES
##########
@@ -40,33 +53,20 @@ channels = ['conda-forge']
# 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 diffpy.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
+#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
##############
@@ -75,13 +75,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
@@ -89,59 +88,59 @@ 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'
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'
+notebook-tests = 'python -m pytest --nbmake docs/docs/tutorials/ --nbmake-timeout=1200 --color=yes -n auto -v'
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',
-] }
+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'
+
+check = 'pre-commit run --hook-stage manual --all-files'
+
+##########
+# 🛠️ Fixes
+##########
-### 🛠️ 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'
+param-docstring-fix = 'python tools/param_consistency.py src/ --fix'
+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/'
+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 = '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',
] }
-## 🧮 Code Complexity
+####################
+# 🧮 Code Complexity
+####################
+
complexity-check = 'radon cc -s src/'
complexity-check-json = 'radon cc -s -j src/'
maintainability-check = 'radon mi src/'
@@ -149,62 +148,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',
+ 'unit-tests-coverage',
+ 'integration-tests-coverage',
+] }
-cov = { depends-on = ['docstring-coverage', 'integration-tests-coverage'] }
+########################
+# 📓 Notebook Management
+########################
-## 📓 Notebook Management
-notebook-convert = 'jupytext tutorials/*.py --from py:percent --to ipynb'
-notebook-strip = 'nbstripout tutorials/*.ipynb'
+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=1200 --overwrite --color=yes -n auto -v'
notebook-prepare = { depends-on = [
- 'notebook-convert',
+ #'notebook-convert',
'notebook-strip',
- 'notebook-tweak',
+ #'notebook-tweak',
+] }
+
+########################
+# 📚 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',
] }
-## 📚 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',
+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/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
+####################################
+
+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',
+ #'pre-commit-setup',
] }
-wheel = { depends-on = ['npm-config', 'prettier-install'] }
-
-## 🔗 Main Package Shortcut
+##########################
+# 🔗 Main Package Shortcut
+##########################
easydiffraction = 'python -m easydiffraction'
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/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 fb0118e3..88036948 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,9 +21,9 @@ classifiers = [
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
]
-requires-python = '>=3.11,<3.14'
+requires-python = '>=3.11'
dependencies = [
- 'essdiffraction', # ESS Diffraction library
+ 'essdiffraction', # ESS-specific diffraction library
'numpy', # Numerical computing library
'colorama', # Color terminal output
'tabulate', # Pretty-print tabular data for terminal output
@@ -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
+ 'format-docstring', # 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 = 65 # 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,112 +191,201 @@ fail_under = 65 # 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 = ['tests', 'tmp']
exclude = [
'tmp',
- 'tests/unit',
- 'tests/integration/fitting',
- 'tests/integration/scipp-analysis/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
-# Enable new rules that are not yet stable, like DOC
-preview = true
+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
+
+[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
+ '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
+ '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 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', # 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
]
-# Temporarily increase McCabe complexity limit to 19 to allow
-# refactoring in smaller steps.
-[tool.ruff.lint.mccabe]
-max-complexity = 19 # default is 10
+# 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
+ '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/
+ # Temporary:
+ 'ARG001',
+ 'ARG002',
+ 'ARG004',
+ 'ARG005',
+ 'B011',
+ 'B017',
+ 'B018',
+ 'E501',
+ 'E741',
+ 'F841',
+ 'I001',
+ 'N801',
+ 'N805',
+ 'N812',
+ 'PLC',
+ 'PLE',
+ 'PLR',
+ 'PLW',
+ 'SIM117',
+ 'W505',
+]
+'docs/**' = [
+ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/
+ 'T201', # https://docs.astral.sh/ruff/rules/print/
+ # Temporary:
+ 'ANN',
+ 'D',
+ 'W',
+]
+
+# 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
-# 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.
+# Use 72 characters for docstrings.
+max-line-length = 99 # See also `line-length` in [tool.ruff]
+max-doc-length = 72
[tool.ruff.lint.pydocstyle]
-convention = 'google'
+convention = 'numpy'
-[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)
+#############################
+# Configuration for pydoclint
+#############################
+
+# '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]
+#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
+allow-init-docstring = true
+
+####################################
+# Configuration for format-docstring
+####################################
+
+# 'format-docstring' -- Code formatter for docstrings
+# 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
+verbose = 'default'
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/src/easydiffraction/__init__.py b/src/easydiffraction/__init__.py
index 259c931d..10308402 100644
--- a/src/easydiffraction/__init__.py
+++ b/src/easydiffraction/__init__.py
@@ -1,30 +1,17 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2025 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
-from easydiffraction.experiments.experiment.factory import ExperimentFactory
+from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory
+from easydiffraction.datablocks.structure.item.factory import StructureFactory
+from easydiffraction.io.ascii import extract_data_paths_from_dir
+from easydiffraction.io.ascii import extract_data_paths_from_zip
+from easydiffraction.io.ascii import extract_metadata
from easydiffraction.project.project import Project
-from easydiffraction.sample_models.sample_model.factory import SampleModelFactory
from easydiffraction.utils.logging import Logger
from easydiffraction.utils.logging import console
from easydiffraction.utils.logging import log
from easydiffraction.utils.utils import download_all_tutorials
from easydiffraction.utils.utils import download_data
from easydiffraction.utils.utils import download_tutorial
-from easydiffraction.utils.utils import get_value_from_xye_header
from easydiffraction.utils.utils import list_tutorials
from easydiffraction.utils.utils import show_version
-
-__all__ = [
- 'Project',
- 'ExperimentFactory',
- 'SampleModelFactory',
- 'download_data',
- 'download_tutorial',
- 'download_all_tutorials',
- 'list_tutorials',
- 'get_value_from_xye_header',
- 'show_version',
- 'Logger',
- 'log',
- 'console',
-]
diff --git a/src/easydiffraction/__main__.py b/src/easydiffraction/__main__.py
index c850b1fa..1a058dd6 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
@@ -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/__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 3532290e..c87fc545 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
@@ -7,18 +7,20 @@
import pandas as pd
-from easydiffraction.analysis.calculators.factory import CalculatorFactory
-from easydiffraction.analysis.categories.aliases import Aliases
-from easydiffraction.analysis.categories.constraints import Constraints
+from easydiffraction.analysis.categories.aliases.factory import AliasesFactory
+from easydiffraction.analysis.categories.constraints.factory import ConstraintsFactory
+from easydiffraction.analysis.categories.fit_mode import FitModeEnum
+from easydiffraction.analysis.categories.fit_mode import FitModeFactory
from easydiffraction.analysis.categories.joint_fit_experiments import JointFitExperiments
from easydiffraction.analysis.fitting import Fitter
from easydiffraction.analysis.minimizers.factory import MinimizerFactory
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.singletons import ConstraintsHandler
+from easydiffraction.core.singleton import ConstraintsHandler
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import Parameter
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.collection import Experiments
from easydiffraction.display.tables import TableRenderer
-from easydiffraction.experiments.experiments import Experiments
+from easydiffraction.utils.enums import VerbosityEnum
from easydiffraction.utils.logging import console
from easydiffraction.utils.logging import log
from easydiffraction.utils.utils import render_cif
@@ -26,53 +28,194 @@
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
- computations across the project's sample models and experiments.
+ computations across the project's structures and experiments.
+ """
- Typical usage:
+ def __init__(self, project: object) -> None:
+ """
+ Create a new Analysis instance bound to a project.
- - Display or filter parameters to fit.
- - Select a calculator/minimizer implementation.
- - Calculate patterns and run single or joint fits.
+ Parameters
+ ----------
+ project : object
+ The project that owns models and experiments.
+ """
+ self.project = project
+ self._aliases_type: str = AliasesFactory.default_tag()
+ self.aliases = AliasesFactory.create(self._aliases_type)
+ self._constraints_type: str = ConstraintsFactory.default_tag()
+ self.constraints = ConstraintsFactory.create(self._constraints_type)
+ self.constraints_handler = ConstraintsHandler.get()
+ self._fit_mode_type: str = FitModeFactory.default_tag()
+ self._fit_mode = FitModeFactory.create(self._fit_mode_type)
+ self._joint_fit_experiments = JointFitExperiments()
+ self.fitter = Fitter('lmfit')
+ self.fit_results = None
+ self._parameter_snapshots: dict[str, dict[str, dict]] = {}
+
+ def help(self) -> None:
+ """Print a summary of analysis properties and methods."""
+ from easydiffraction.core.guard import GuardedBase
+
+ console.paragraph("Help for 'Analysis'")
+
+ cls = type(self)
+
+ # Auto-discover properties from MRO
+ seen_props: dict = {}
+ for base in cls.mro():
+ for key, attr in base.__dict__.items():
+ if key.startswith('_') or not isinstance(attr, property):
+ continue
+ if key not in seen_props:
+ seen_props[key] = attr
+
+ prop_rows = []
+ for i, key in enumerate(sorted(seen_props), 1):
+ prop = seen_props[key]
+ writable = '✓' if prop.fset else '✗'
+ doc = GuardedBase._first_sentence(prop.fget.__doc__ if prop.fget else None)
+ prop_rows.append([str(i), key, writable, doc])
+
+ if prop_rows:
+ console.paragraph('Properties')
+ render_table(
+ columns_headers=['#', 'Name', 'Writable', 'Description'],
+ columns_alignment=['right', 'left', 'center', 'left'],
+ columns_data=prop_rows,
+ )
- 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.
- """
+ # Auto-discover methods from MRO
+ seen_methods: set = set()
+ methods_list: list = []
+ for base in cls.mro():
+ for key, attr in base.__dict__.items():
+ if key.startswith('_') or key in seen_methods:
+ continue
+ if isinstance(attr, property):
+ continue
+ raw = attr
+ if isinstance(raw, (staticmethod, classmethod)):
+ raw = raw.__func__
+ if callable(raw):
+ seen_methods.add(key)
+ methods_list.append((key, raw))
+
+ method_rows = []
+ for i, (key, method) in enumerate(sorted(methods_list), 1):
+ doc = GuardedBase._first_sentence(getattr(method, '__doc__', None))
+ method_rows.append([str(i), f'{key}()', doc])
+
+ if method_rows:
+ console.paragraph('Methods')
+ render_table(
+ columns_headers=['#', 'Name', 'Description'],
+ columns_alignment=['right', 'left', 'left'],
+ columns_data=method_rows,
+ )
- _calculator = CalculatorFactory.create_calculator('cryspy')
+ # ------------------------------------------------------------------
+ # Aliases (switchable-category pattern)
+ # ------------------------------------------------------------------
- def __init__(self, project) -> None:
- """Create a new Analysis instance bound to a project.
+ @property
+ def aliases_type(self) -> str:
+ """Tag of the active aliases collection type."""
+ return self._aliases_type
- Args:
- project: The project that owns models and experiments.
+ @aliases_type.setter
+ def aliases_type(self, new_type: str) -> None:
"""
- self.project = project
- self.aliases = Aliases()
- self.constraints = Constraints()
- self.constraints_handler = ConstraintsHandler.get()
- self.calculator = Analysis._calculator # Default calculator shared by project
- self._calculator_key: str = 'cryspy' # Added to track the current calculator
- self._fit_mode: str = 'single'
- self.fitter = Fitter('lmfit (leastsq)')
+ Switch to a different aliases collection type.
+
+ Parameters
+ ----------
+ new_type : str
+ Aliases tag (e.g. ``'default'``).
+ """
+ supported_tags = AliasesFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported aliases type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_aliases_types()'",
+ )
+ return
+ self.aliases = AliasesFactory.create(new_type)
+ self._aliases_type = new_type
+ console.paragraph('Aliases type changed to')
+ console.print(new_type)
+
+ def show_supported_aliases_types(self) -> None:
+ """Print a table of supported aliases collection types."""
+ AliasesFactory.show_supported()
+
+ def show_current_aliases_type(self) -> None:
+ """Print the currently used aliases collection type."""
+ console.paragraph('Current aliases type')
+ console.print(self._aliases_type)
+
+ # ------------------------------------------------------------------
+ # Constraints (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def constraints_type(self) -> str:
+ """Tag of the active constraints collection type."""
+ return self._constraints_type
+
+ @constraints_type.setter
+ def constraints_type(self, new_type: str) -> None:
+ """
+ Switch to a different constraints collection type.
+
+ Parameters
+ ----------
+ new_type : str
+ Constraints tag (e.g. ``'default'``).
+ """
+ supported_tags = ConstraintsFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported constraints type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_constraints_types()'",
+ )
+ return
+ self.constraints = ConstraintsFactory.create(new_type)
+ self._constraints_type = new_type
+ console.paragraph('Constraints type changed to')
+ console.print(new_type)
+
+ def show_supported_constraints_types(self) -> None:
+ """Print a table of supported constraints collection types."""
+ ConstraintsFactory.show_supported()
+
+ def show_current_constraints_type(self) -> None:
+ """Print the currently used constraints collection type."""
+ console.paragraph('Current constraints type')
+ console.print(self._constraints_type)
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[Union[NumericDescriptor, Parameter]]
+ List of DescriptorFloat or Parameter objects.
- Returns:
+ Returns
+ -------
+ pd.DataFrame
A pandas DataFrame containing parameter information.
"""
records = []
@@ -108,13 +251,11 @@ def _get_params_as_dataframe(
return df
def show_all_params(self) -> None:
- """Print a table with all parameters for sample models and
- experiments.
- """
- sample_models_params = self.project.sample_models.parameters
+ """Print all parameters for structures and experiments."""
+ structures_params = self.project.structures.parameters
experiments_params = self.project.experiments.parameters
- if not sample_models_params and not experiments_params:
+ if not structures_params and not experiments_params:
log.warning('No parameters found.')
return
@@ -129,8 +270,8 @@ def show_all_params(self) -> None:
'fittable',
]
- console.paragraph('All parameters for all sample models (🧩 data blocks)')
- df = self._get_params_as_dataframe(sample_models_params)
+ console.paragraph('All parameters for all structures (🧩 data blocks)')
+ df = self._get_params_as_dataframe(structures_params)
filtered_df = df[filtered_headers]
tabler.render(filtered_df)
@@ -140,13 +281,11 @@ 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.
- """
- sample_models_params = self.project.sample_models.fittable_parameters
+ """Print all fittable parameters."""
+ structures_params = self.project.structures.fittable_parameters
experiments_params = self.project.experiments.fittable_parameters
- if not sample_models_params and not experiments_params:
+ if not structures_params and not experiments_params:
log.warning('No fittable parameters found.')
return
@@ -163,8 +302,8 @@ def show_fittable_params(self) -> None:
'free',
]
- console.paragraph('Fittable parameters for all sample models (🧩 data blocks)')
- df = self._get_params_as_dataframe(sample_models_params)
+ console.paragraph('Fittable parameters for all structures (🧩 data blocks)')
+ df = self._get_params_as_dataframe(structures_params)
filtered_df = df[filtered_headers]
tabler.render(filtered_df)
@@ -174,12 +313,10 @@ 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.
- """
- sample_models_params = self.project.sample_models.free_parameters
+ """Print only currently free (varying) parameters."""
+ structures_params = self.project.structures.free_parameters
experiments_params = self.project.experiments.free_parameters
- free_params = sample_models_params + experiments_params
+ free_params = structures_params + experiments_params
if not free_params:
log.warning('No free parameters found.')
@@ -200,23 +337,23 @@ def show_free_params(self) -> None:
]
console.paragraph(
- 'Free parameters for both sample models (🧩 data blocks) '
- 'and experiments (🔬 data blocks)'
+ 'Free parameters for both structures (🧩 data blocks) and experiments (🔬 data blocks)'
)
df = self._get_params_as_dataframe(free_params)
filtered_df = df[filtered_headers]
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.
"""
- sample_models_params = self.project.sample_models.parameters
+ structures_params = self.project.structures.parameters
experiments_params = self.project.experiments.parameters
all_params = {
- 'sample_models': sample_models_params,
+ 'structures': structures_params,
'experiments': experiments_params,
}
@@ -272,15 +409,16 @@ 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.
"""
- sample_models_params = self.project.sample_models.parameters
+ structures_params = self.project.structures.parameters
experiments_params = self.project.experiments.parameters
all_params = {
- 'sample_models': sample_models_params,
+ 'structures': structures_params,
'experiments': experiments_params,
}
@@ -328,40 +466,6 @@ def show_parameter_cif_uids(self) -> None:
columns_data=columns_data,
)
- def show_current_calculator(self) -> None:
- """Print the name of the currently selected calculator
- engine.
- """
- console.paragraph('Current calculator')
- console.print(self.current_calculator)
-
- @staticmethod
- def show_supported_calculators() -> None:
- """Print a table of available calculator backends on this
- system.
- """
- CalculatorFactory.show_supported_calculators()
-
- @property
- def current_calculator(self) -> str:
- """The key/name of the active calculator backend."""
- return self._calculator_key
-
- @current_calculator.setter
- def current_calculator(self, calculator_name: str) -> None:
- """Switch to a different calculator backend.
-
- Args:
- calculator_name: Calculator key to use (e.g. 'cryspy').
- """
- calculator = CalculatorFactory.create_calculator(calculator_name)
- if calculator is None:
- return
- self.calculator = calculator
- self._calculator_key = calculator_name
- console.paragraph('Current calculator changed to')
- console.print(self.current_calculator)
-
def show_current_minimizer(self) -> None:
"""Print the name of the currently selected minimizer."""
console.paragraph('Current minimizer')
@@ -369,10 +473,8 @@ def show_current_minimizer(self) -> None:
@staticmethod
def show_available_minimizers() -> None:
- """Print a table of available minimizer drivers on this
- system.
- """
- MinimizerFactory.show_available_minimizers()
+ """Print available minimizer drivers on this system."""
+ MinimizerFactory.show_supported()
@property
def current_minimizer(self) -> Optional[str]:
@@ -381,114 +483,92 @@ 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 (leastsq)'.
+ Parameters
+ ----------
+ selection : str
+ Minimizer selection string, e.g. 'lmfit'.
"""
self.fitter = Fitter(selection)
console.paragraph('Current minimizer changed to')
console.print(self.current_minimizer)
+ # ------------------------------------------------------------------
+ # Fit mode (switchable-category pattern)
+ # ------------------------------------------------------------------
+
@property
- def fit_mode(self) -> str:
- """Current fitting strategy: either 'single' or 'joint'."""
+ def fit_mode(self) -> object:
+ """Fit-mode category item holding the active strategy."""
return self._fit_mode
- @fit_mode.setter
- def fit_mode(self, strategy: str) -> None:
- """Set the fitting strategy.
-
- When set to 'joint', all experiments get default weights and
- are used together in a single optimization.
-
- Args:
- strategy: Either 'single' or 'joint'.
+ @property
+ def fit_mode_type(self) -> str:
+ """Tag of the active fit-mode category type."""
+ return self._fit_mode_type
- Raises:
- ValueError: If an unsupported strategy value is
- provided.
+ @fit_mode_type.setter
+ def fit_mode_type(self, new_type: str) -> None:
"""
- if strategy not in ['single', 'joint']:
- raise ValueError("Fit mode must be either 'single' or 'joint'")
- self._fit_mode = strategy
- if strategy == 'joint' and not hasattr(self, 'joint_fit_experiments'):
- # Pre-populate all experiments with weight 0.5
- self.joint_fit_experiments = JointFitExperiments()
- for id in self.project.experiments.names:
- self.joint_fit_experiments.add(id=id, weight=0.5)
- console.paragraph('Current fit mode changed to')
- console.print(self._fit_mode)
-
- def show_available_fit_modes(self) -> None:
- """Print all supported fitting strategies and their
- descriptions.
+ Switch to a different fit-mode category type.
+
+ Parameters
+ ----------
+ new_type : str
+ Fit-mode tag (e.g. ``'default'``).
"""
- strategies = [
- {
- 'Strategy': 'single',
- 'Description': 'Independent fitting of each experiment; no shared parameters',
- },
- {
- 'Strategy': 'joint',
- 'Description': 'Simultaneous fitting of all experiments; '
- 'some parameters are shared',
- },
- ]
+ supported_tags = FitModeFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported fit-mode type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_fit_mode_types()'",
+ )
+ return
+ self._fit_mode = FitModeFactory.create(new_type)
+ self._fit_mode_type = new_type
+ console.paragraph('Fit-mode type changed to')
+ console.print(new_type)
- columns_headers = ['Strategy', 'Description']
- columns_alignment = ['left', 'left']
- columns_data = []
- for item in strategies:
- strategy = item['Strategy']
- description = item['Description']
- columns_data.append([strategy, description])
+ def show_supported_fit_mode_types(self) -> None:
+ """Print a table of supported fit-mode category types."""
+ FitModeFactory.show_supported()
- console.paragraph('Available fit modes')
- render_table(
- columns_headers=columns_headers,
- columns_alignment=columns_alignment,
- columns_data=columns_data,
- )
+ def show_current_fit_mode_type(self) -> None:
+ """Print the currently used fit-mode category type."""
+ console.paragraph('Current fit-mode type')
+ console.print(self._fit_mode_type)
+
+ # ------------------------------------------------------------------
+ # Joint-fit experiments (category)
+ # ------------------------------------------------------------------
- def show_current_fit_mode(self) -> None:
- """Print the currently active fitting strategy."""
- console.paragraph('Current fit mode')
- console.print(self.fit_mode)
+ @property
+ def joint_fit_experiments(self) -> object:
+ """Per-experiment weight collection for joint fitting."""
+ return self._joint_fit_experiments
def show_constraints(self) -> None:
"""Print a table of all user-defined symbolic constraints."""
- constraints_dict = dict(self.constraints)
-
if not self.constraints._items:
log.warning('No constraints defined.')
return
rows = []
- for constraint in constraints_dict.values():
- row = {
- 'lhs_alias': constraint.lhs_alias.value,
- 'rhs_expr': constraint.rhs_expr.value,
- 'full expression': f'{constraint.lhs_alias.value} = {constraint.rhs_expr.value}',
- }
- rows.append(row)
-
- headers = ['lhs_alias', 'rhs_expr', 'full expression']
- alignments = ['left', 'left', 'left']
- rows = [[row[header] for header in headers] for row in rows]
+ for constraint in self.constraints:
+ rows.append([constraint.expression.value])
console.paragraph('User defined constraints')
render_table(
- columns_headers=headers,
- columns_alignment=alignments,
+ columns_headers=['expression'],
+ columns_alignment=['left'],
columns_data=rows,
)
- def apply_constraints(self):
- """Apply the currently defined constraints to the active
- project.
- """
+ def apply_constraints(self) -> None:
+ """Apply currently defined constraints to the project."""
if not self.constraints._items:
log.warning('No constraints defined.')
return
@@ -497,31 +577,41 @@ def apply_constraints(self):
self.constraints_handler.set_constraints(self.constraints)
self.constraints_handler.apply()
- def fit(self):
- """Execute fitting using the selected mode, calculator and
- minimizer.
+ def fit(self, verbosity: str | None = None) -> None:
+ """
+ Execute fitting for all experiments.
This method performs the optimization but does not display
results automatically. Call :meth:`show_fit_results` after
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``).
-
- Example::
-
- project.analysis.fit()
- project.analysis.show_fit_results() # Display results
+ programmatically (e.g.,
+ ``analysis.fit_results.reduced_chi_square``).
+
+ Parameters
+ ----------
+ verbosity : str | None, default=None
+ Console output verbosity: ``'full'`` for detailed per-
+ experiment progress, ``'short'`` for a
+ one-row-per-experiment summary table, or ``'silent'`` for no
+ output. When ``None``, uses ``project.verbosity``.
+
+ Raises
+ ------
+ NotImplementedError
+ If the fit mode is not ``'single'`` or ``'joint'``.
"""
- sample_models = self.project.sample_models
- if not sample_models:
- log.warning('No sample models found in the project. Cannot run fit.')
+ verb = VerbosityEnum(verbosity if verbosity is not None else self.project.verbosity)
+
+ structures = self.project.structures
+ if not structures:
+ log.warning('No structures found in the project. Cannot run fit.')
return
experiments = self.project.experiments
@@ -530,23 +620,58 @@ def fit(self):
return
# Run the fitting process
- if self.fit_mode == 'joint':
- console.paragraph(
- f"Using all experiments 🔬 {experiments.names} for '{self.fit_mode}' fitting"
- )
+ mode = FitModeEnum(self._fit_mode.mode.value)
+ if mode is FitModeEnum.JOINT:
+ # Auto-populate joint_fit_experiments if empty
+ if not len(self._joint_fit_experiments):
+ for id in experiments.names:
+ self._joint_fit_experiments.create(id=id, weight=0.5)
+ if verb is not VerbosityEnum.SILENT:
+ console.paragraph(
+ f"Using all experiments 🔬 {experiments.names} for '{mode.value}' fitting"
+ )
self.fitter.fit(
- sample_models,
+ structures,
experiments,
- weights=self.joint_fit_experiments,
+ weights=self._joint_fit_experiments,
analysis=self,
+ verbosity=verb,
)
- elif self.fit_mode == 'single':
- # TODO: Find a better way without creating dummy
- # experiments?
- for expt_name in experiments.names:
+
+ # After fitting, get the results
+ self.fit_results = self.fitter.results
+
+ elif mode is FitModeEnum.SINGLE:
+ expt_names = experiments.names
+ num_expts = len(expt_names)
+
+ # Short mode: print header and create display handle once
+ short_headers = ['experiment', 'χ²', 'iterations', 'status']
+ short_alignments = ['left', 'right', 'right', 'center']
+ short_rows: list[list[str]] = []
+ short_display_handle: object | None = None
+ if verb is VerbosityEnum.SHORT:
+ from easydiffraction.analysis.fit_helpers.tracking import _make_display_handle
+
+ first = expt_names[0]
+ last = expt_names[-1]
+ minimizer_name = self.fitter.selection
console.paragraph(
- f"Using experiment 🔬 '{expt_name}' for '{self.fit_mode}' fitting"
+ f"Using {num_expts} experiments 🔬 from '{first}' to "
+ f"'{last}' for '{mode.value}' fitting"
)
+ console.print(f"🚀 Starting fit process with '{minimizer_name}'...")
+ console.print('📈 Goodness-of-fit (reduced χ²) per experiment:')
+ short_display_handle = _make_display_handle()
+
+ # TODO: Find a better way without creating dummy
+ # experiments?
+ for _idx, expt_name in enumerate(expt_names, start=1):
+ if verb is VerbosityEnum.FULL:
+ console.paragraph(
+ f"Using experiment 🔬 '{expt_name}' for '{mode.value}' fitting"
+ )
+
experiment = experiments[expt_name]
dummy_experiments = Experiments() # TODO: Find a better name
@@ -555,20 +680,64 @@ def fit(self):
# parameters can be resolved correctly during fitting.
object.__setattr__(dummy_experiments, '_parent', self.project)
- dummy_experiments._add(experiment)
+ dummy_experiments.add(experiment)
self.fitter.fit(
- sample_models,
+ structures,
dummy_experiments,
analysis=self,
+ verbosity=verb,
)
+
+ # After fitting, snapshot parameter values before
+ # they get overwritten by the next experiment's fit
+ results = self.fitter.results
+ snapshot: dict[str, dict] = {}
+ for param in results.parameters:
+ snapshot[param.unique_name] = {
+ 'value': param.value,
+ 'uncertainty': param.uncertainty,
+ 'units': param.units,
+ }
+ self._parameter_snapshots[expt_name] = snapshot
+ self.fit_results = results
+
+ # Short mode: append one summary row and update in-place
+ if verb is VerbosityEnum.SHORT:
+ chi2_str = (
+ f'{results.reduced_chi_square:.2f}'
+ if results.reduced_chi_square is not None
+ else '—'
+ )
+ iters = str(self.fitter.minimizer.tracker.best_iteration or 0)
+ status = '✅' if results.success else '❌'
+ short_rows.append([expt_name, chi2_str, iters, status])
+ render_table(
+ columns_headers=short_headers,
+ columns_alignment=short_alignments,
+ columns_data=short_rows,
+ display_handle=short_display_handle,
+ )
+
+ # Short mode: close the display handle
+ if short_display_handle is not None and hasattr(short_display_handle, 'close'):
+ from contextlib import suppress
+
+ with suppress(Exception):
+ short_display_handle.close()
+
else:
- raise NotImplementedError(f'Fit mode {self.fit_mode} not implemented yet.')
+ raise NotImplementedError(f'Fit mode {mode.value} not implemented yet.')
- # After fitting, get the results
- self.fit_results = self.fitter.results
+ # After fitting, save the project
+ # TODO: Consider saving individual data during sequential
+ # (single) fitting, instead of waiting until the end and save
+ # only the last one
+ if self.project.info.path is not None:
+ self.project.save()
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
@@ -579,26 +748,28 @@ 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:
+ if self.fit_results is None:
log.warning('No fit results available. Run fit() first.')
return
- sample_models = self.project.sample_models
+ structures = self.project.structures
experiments = self.project.experiments
- self.fitter._process_fit_results(sample_models, experiments)
+ self.fitter._process_fit_results(structures, experiments)
- def _update_categories(self, called_by_minimizer=False) -> None:
- """Update all categories owned by Analysis.
+ 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.
- Args:
- called_by_minimizer: Whether this is called during fitting.
+ Parameters
+ ----------
+ called_by_minimizer : bool, default=False
+ Whether this is called during fitting.
"""
# Apply constraints to sync dependent parameters
if self.constraints._items:
@@ -610,10 +781,13 @@ 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):
- """Serialize the analysis section to a CIF string.
+ def as_cif(self) -> str:
+ """
+ Serialize the analysis section to a CIF string.
- Returns:
+ Returns
+ -------
+ str
The analysis section represented as a CIF document string.
"""
from easydiffraction.io.cif.serialize import analysis_to_cif
@@ -622,9 +796,7 @@ def as_cif(self):
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/__init__.py b/src/easydiffraction/analysis/calculators/__init__.py
index 429f2648..38bd1aa0 100644
--- a/src/easydiffraction/analysis/calculators/__init__.py
+++ b/src/easydiffraction/analysis/calculators/__init__.py
@@ -1,2 +1,6 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.analysis.calculators.crysfml import CrysfmlCalculator
+from easydiffraction.analysis.calculators.cryspy import CryspyCalculator
+from easydiffraction.analysis.calculators.pdffit import PdffitCalculator
diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py
index 80c86a36..bd667ac8 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
@@ -6,9 +6,9 @@
import numpy as np
-from easydiffraction.experiments.experiment.base import ExperimentBase
-from easydiffraction.sample_models.sample_model.base import SampleModelBase
-from easydiffraction.sample_models.sample_models import SampleModels
+from easydiffraction.datablocks.experiment.item.base import ExperimentBase
+from easydiffraction.datablocks.structure.collection import Structures
+from easydiffraction.datablocks.structure.item.base import Structure
class CalculatorBase(ABC):
@@ -17,41 +17,48 @@ class CalculatorBase(ABC):
@property
@abstractmethod
def name(self) -> str:
+ """Short identifier of the calculation engine."""
pass
@property
@abstractmethod
def engine_imported(self) -> bool:
+ """True if the underlying calculation library is available."""
pass
@abstractmethod
def calculate_structure_factors(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
experiment: ExperimentBase,
+ called_by_minimizer: bool,
) -> None:
- """Calculate structure factors for a single sample model and
- experiment.
- """
+ """Calculate structure factors for one experiment."""
pass
@abstractmethod
def calculate_pattern(
self,
- sample_model: SampleModels, # TODO: SampleModelBase?
+ structure: Structures, # TODO: Structure?
experiment: ExperimentBase,
called_by_minimizer: bool,
) -> np.ndarray:
- """Calculate the diffraction pattern for a single sample model
- and experiment.
+ """
+ Calculate diffraction pattern for one structure-experiment pair.
- Args:
- sample_model: The sample model object.
- experiment: The experiment object.
- called_by_minimizer: Whether the calculation is called by a
- minimizer.
+ 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:
+ 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 babd3e08..34624e2b 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
@@ -9,10 +9,12 @@
import numpy as np
from easydiffraction.analysis.calculators.base import CalculatorBase
-from easydiffraction.experiments.experiment.base import ExperimentBase
-from easydiffraction.experiments.experiments import Experiments
-from easydiffraction.sample_models.sample_model.base import SampleModelBase
-from easydiffraction.sample_models.sample_models import SampleModels
+from easydiffraction.analysis.calculators.factory import CalculatorFactory
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.collection import Experiments
+from easydiffraction.datablocks.experiment.item.base import ExperimentBase
+from easydiffraction.datablocks.structure.collection import Structures
+from easydiffraction.datablocks.structure.item.base import Structure
try:
from pycrysfml import cfml_py_utilities
@@ -27,53 +29,71 @@
cfml_py_utilities = None
+@CalculatorFactory.register
class CrysfmlCalculator(CalculatorBase):
"""Wrapper for Crysfml library."""
+ type_info = TypeInfo(
+ tag='crysfml',
+ description='CrysFML library for crystallographic calculations',
+ )
engine_imported: bool = cfml_py_utilities is not None
@property
def name(self) -> str:
+ """Short identifier of this calculator engine."""
return 'crysfml'
def calculate_structure_factors(
self,
- sample_models: SampleModels,
+ structures: Structures,
experiments: Experiments,
) -> None:
- """Call Crysfml to calculate structure factors.
-
- Args:
- sample_models: The sample models to calculate structure
- factors for.
- experiments: The experiments associated with the sample
- models.
+ """
+ Call Crysfml to calculate structure factors.
+
+ Parameters
+ ----------
+ structures : Structures
+ The structures to calculate structure factors for.
+ 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.')
def calculate_pattern(
self,
- sample_model: SampleModels,
+ structure: Structures,
experiment: ExperimentBase,
called_by_minimizer: bool = False,
) -> Union[np.ndarray, List[float]]:
- """Calculates the diffraction pattern using Crysfml for the
- given sample model and experiment.
-
- Args:
- sample_model: The sample model to calculate the pattern for.
- experiment: The experiment associated with the sample model.
- called_by_minimizer: Whether the calculation is called by a
- minimizer.
-
- Returns:
+ """
+ Calculate the diffraction pattern using Crysfml.
+
+ Parameters
+ ----------
+ structure : Structures
+ The structure to calculate the pattern for.
+ experiment : ExperimentBase
+ The experiment associated with the structure.
+ 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.
+ list of floats.
"""
# Intentionally unused, required by public API/signature
del called_by_minimizer
- crysfml_dict = self._crysfml_dict(sample_model, experiment)
+ crysfml_dict = self._crysfml_dict(structure, experiment)
try:
_, y = cfml_py_utilities.cw_powder_pattern_from_dict(crysfml_dict)
y = self._adjust_pattern_length(y, len(experiment.data.x))
@@ -87,13 +107,19 @@ def _adjust_pattern_length(
pattern: List[float],
target_length: int,
) -> 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.
-
- Returns:
+ """
+ Adjust the pattern length to match the target length.
+
+ Parameters
+ ----------
+ pattern : List[float]
+ The pattern to adjust.
+ target_length : int
+ The desired length of the pattern.
+
+ Returns
+ -------
+ List[float]
The adjusted pattern.
"""
# TODO: Check the origin of this discrepancy coming from
@@ -104,53 +130,62 @@ def _adjust_pattern_length(
def _crysfml_dict(
self,
- sample_model: SampleModels,
+ structure: Structures,
experiment: ExperimentBase,
- ) -> Dict[str, Union[ExperimentBase, SampleModelBase]]:
- """Converts the sample model and experiment into a dictionary
- format for Crysfml.
-
- Args:
- sample_model: The sample model to convert.
- experiment: The experiment to convert.
-
- Returns:
- A dictionary representation of the sample model and
- experiment.
+ ) -> Dict[str, Union[ExperimentBase, Structure]]:
"""
- sample_model_dict = self._convert_sample_model_to_dict(sample_model)
+ Convert structure and experiment into a Crysfml dictionary.
+
+ Parameters
+ ----------
+ structure : Structures
+ The structure to convert.
+ 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)
experiment_dict = self._convert_experiment_to_dict(experiment)
return {
- 'phases': [sample_model_dict],
+ 'phases': [structure_dict],
'experiments': [experiment_dict],
}
- def _convert_sample_model_to_dict(
+ def _convert_structure_to_dict(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
) -> Dict[str, Any]:
- """Converts a sample model into a dictionary format.
+ """
+ Convert a structure into a dictionary format.
- Args:
- sample_model: The sample model to convert.
+ Parameters
+ ----------
+ structure : Structure
+ The structure to convert.
- Returns:
- A dictionary representation of the sample model.
+ Returns
+ -------
+ Dict[str, Any]
+ A dictionary representation of the structure.
"""
- sample_model_dict = {
- sample_model.name: {
- '_space_group_name_H-M_alt': sample_model.space_group.name_h_m.value,
- '_cell_length_a': sample_model.cell.length_a.value,
- '_cell_length_b': sample_model.cell.length_b.value,
- '_cell_length_c': sample_model.cell.length_c.value,
- '_cell_angle_alpha': sample_model.cell.angle_alpha.value,
- '_cell_angle_beta': sample_model.cell.angle_beta.value,
- '_cell_angle_gamma': sample_model.cell.angle_gamma.value,
+ structure_dict = {
+ structure.name: {
+ '_space_group_name_H-M_alt': structure.space_group.name_h_m.value,
+ '_cell_length_a': structure.cell.length_a.value,
+ '_cell_length_b': structure.cell.length_b.value,
+ '_cell_length_c': structure.cell.length_c.value,
+ '_cell_angle_alpha': structure.cell.angle_alpha.value,
+ '_cell_angle_beta': structure.cell.angle_beta.value,
+ '_cell_angle_gamma': structure.cell.angle_gamma.value,
'_atom_site': [],
}
}
- for atom in sample_model.atom_sites:
+ for atom in structure.atom_sites:
atom_site = {
'_label': atom.label.value,
'_type_symbol': atom.type_symbol.value,
@@ -161,20 +196,25 @@ def _convert_sample_model_to_dict(
'_adp_type': 'Biso', # Assuming Biso for simplicity
'_B_iso_or_equiv': atom.b_iso.value,
}
- sample_model_dict[sample_model.name]['_atom_site'].append(atom_site)
+ structure_dict[structure.name]['_atom_site'].append(atom_site)
- return sample_model_dict
+ return structure_dict
def _convert_experiment_to_dict(
self,
experiment: ExperimentBase,
) -> Dict[str, Any]:
- """Converts an experiment into a dictionary format.
+ """
+ Convert an experiment into a dictionary format.
- Args:
- experiment: The experiment to convert.
+ Parameters
+ ----------
+ experiment : ExperimentBase
+ The experiment to convert.
- Returns:
+ 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 18e7858d..b6a0e4d6 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
@@ -12,9 +12,12 @@
import numpy as np
from easydiffraction.analysis.calculators.base import CalculatorBase
-from easydiffraction.experiments.experiment.base import ExperimentBase
-from easydiffraction.experiments.experiment.enums import BeamModeEnum
-from easydiffraction.sample_models.sample_model.base import SampleModelBase
+from easydiffraction.analysis.calculators.factory import CalculatorFactory
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.item.base import ExperimentBase
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.structure.item.base import Structure
try:
import cryspy
@@ -30,17 +33,24 @@
cryspy = None
+@CalculatorFactory.register
class CryspyCalculator(CalculatorBase):
- """Cryspy-based diffraction calculator.
+ """
+ Cryspy-based diffraction calculator.
Converts EasyDiffraction models into Cryspy objects and computes
patterns.
"""
+ type_info = TypeInfo(
+ tag='cryspy',
+ description='CrysPy library for crystallographic calculations',
+ )
engine_imported: bool = cryspy is not None
@property
def name(self) -> str:
+ """Short identifier of this calculator engine."""
return 'cryspy'
def __init__(self) -> None:
@@ -49,55 +59,102 @@ def __init__(self) -> None:
def calculate_structure_factors(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
experiment: ExperimentBase,
+ called_by_minimizer: bool = False,
) -> None:
- """Raises a NotImplementedError as HKL calculation is not
- implemented.
-
- Args:
- sample_model: The sample model to calculate structure
- factors for.
- experiment: The experiment associated with the sample
- models.
"""
- raise NotImplementedError('HKL calculation is not implemented for CryspyCalculator.')
+ Raise NotImplementedError as HKL calculation is not implemented.
+
+ Parameters
+ ----------
+ structure : Structure
+ The structure to calculate structure factors for.
+ experiment : ExperimentBase
+ The experiment associated with the sample models.
+ called_by_minimizer : bool, default=False
+ Whether the calculation is called by a minimizer.
+ """
+ combined_name = f'{structure.name}_{experiment.name}'
+
+ if called_by_minimizer:
+ if self._cryspy_dicts and combined_name in self._cryspy_dicts:
+ cryspy_dict = self._recreate_cryspy_dict(structure, experiment)
+ else:
+ cryspy_obj = self._recreate_cryspy_obj(structure, experiment)
+ cryspy_dict = cryspy_obj.get_dictionary()
+ else:
+ cryspy_obj = self._recreate_cryspy_obj(structure, experiment)
+ cryspy_dict = cryspy_obj.get_dictionary()
+
+ self._cryspy_dicts[combined_name] = copy.deepcopy(cryspy_dict)
+
+ cryspy_in_out_dict: Dict[str, Any] = {}
+
+ # Calculate the pattern using Cryspy
+ # TODO: Redirect stderr to suppress Cryspy warnings.
+ # This is a temporary solution to avoid cluttering the output.
+ # E.g. cryspy/A_functions_base/powder_diffraction_tof.py:106:
+ # RuntimeWarning: overflow encountered in exp
+ # Remove this when Cryspy is updated to handle warnings better.
+ with contextlib.redirect_stderr(io.StringIO()):
+ rhochi_calc_chi_sq_by_dictionary(
+ cryspy_dict,
+ dict_in_out=cryspy_in_out_dict,
+ flag_use_precalculated_data=False,
+ flag_calc_analytical_derivatives=False,
+ )
+
+ cryspy_block_name = f'diffrn_{experiment.name}'
+
+ try:
+ y_calc = cryspy_in_out_dict[cryspy_block_name]['intensity_calc']
+ stol = cryspy_in_out_dict[cryspy_block_name]['sthovl']
+ except KeyError:
+ print(f'[CryspyCalculator] Error: No calculated data for {cryspy_block_name}')
+ return [], []
+
+ return stol, y_calc
def calculate_pattern(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
experiment: ExperimentBase,
called_by_minimizer: bool = False,
) -> Union[np.ndarray, List[float]]:
- """Calculates the diffraction pattern using Cryspy for the given
- sample model 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:
- sample_model: The sample model to calculate the pattern for.
- experiment: The experiment associated with the sample model.
- called_by_minimizer: Whether the calculation is called by a
- minimizer.
-
- Returns:
+ """
+ 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
+ other cases, we are modifying the existing cryspy_dict This
+ allows significantly speeding up the calculation
+
+ Parameters
+ ----------
+ structure : Structure
+ The structure to calculate the pattern for.
+ experiment : ExperimentBase
+ The experiment associated with the structure.
+ 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.
+ list of floats.
"""
- combined_name = f'{sample_model.name}_{experiment.name}'
+ combined_name = f'{structure.name}_{experiment.name}'
if called_by_minimizer:
if self._cryspy_dicts and combined_name in self._cryspy_dicts:
- cryspy_dict = self._recreate_cryspy_dict(sample_model, experiment)
+ cryspy_dict = self._recreate_cryspy_dict(structure, experiment)
else:
- cryspy_obj = self._recreate_cryspy_obj(sample_model, experiment)
+ cryspy_obj = self._recreate_cryspy_obj(structure, experiment)
cryspy_dict = cryspy_obj.get_dictionary()
else:
- cryspy_obj = self._recreate_cryspy_obj(sample_model, experiment)
+ cryspy_obj = self._recreate_cryspy_obj(structure, experiment)
cryspy_dict = cryspy_obj.get_dictionary()
self._cryspy_dicts[combined_name] = copy.deepcopy(cryspy_dict)
@@ -141,126 +198,153 @@ def calculate_pattern(
def _recreate_cryspy_dict(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
experiment: ExperimentBase,
) -> Dict[str, Any]:
- """Recreates the Cryspy dictionary for the given sample model
- and experiment.
-
- Args:
- sample_model: The sample model to update.
- experiment: The experiment to update.
-
- Returns:
+ """
+ Recreate the Cryspy dictionary for structure and experiment.
+
+ Parameters
+ ----------
+ structure : Structure
+ The structure to update.
+ experiment : ExperimentBase
+ The experiment to update.
+
+ Returns
+ -------
+ Dict[str, Any]
The updated Cryspy dictionary.
"""
- combined_name = f'{sample_model.name}_{experiment.name}'
+ combined_name = f'{structure.name}_{experiment.name}'
cryspy_dict = copy.deepcopy(self._cryspy_dicts[combined_name])
- cryspy_model_id = f'crystal_{sample_model.name}'
+ cryspy_model_id = f'crystal_{structure.name}'
cryspy_model_dict = cryspy_dict[cryspy_model_id]
- # Update sample model parameters
+ ################################
+ # Update structure parameters
+ ################################
# Cell
cryspy_cell = cryspy_model_dict['unit_cell_parameters']
- cryspy_cell[0] = sample_model.cell.length_a.value
- cryspy_cell[1] = sample_model.cell.length_b.value
- cryspy_cell[2] = sample_model.cell.length_c.value
- cryspy_cell[3] = np.deg2rad(sample_model.cell.angle_alpha.value)
- cryspy_cell[4] = np.deg2rad(sample_model.cell.angle_beta.value)
- cryspy_cell[5] = np.deg2rad(sample_model.cell.angle_gamma.value)
+ cryspy_cell[0] = structure.cell.length_a.value
+ cryspy_cell[1] = structure.cell.length_b.value
+ cryspy_cell[2] = structure.cell.length_c.value
+ cryspy_cell[3] = np.deg2rad(structure.cell.angle_alpha.value)
+ cryspy_cell[4] = np.deg2rad(structure.cell.angle_beta.value)
+ cryspy_cell[5] = np.deg2rad(structure.cell.angle_gamma.value)
# Atomic coordinates
cryspy_xyz = cryspy_model_dict['atom_fract_xyz']
- for idx, atom_site in enumerate(sample_model.atom_sites):
+ for idx, atom_site in enumerate(structure.atom_sites):
cryspy_xyz[0][idx] = atom_site.fract_x.value
cryspy_xyz[1][idx] = atom_site.fract_y.value
cryspy_xyz[2][idx] = atom_site.fract_z.value
# Atomic occupancies
cryspy_occ = cryspy_model_dict['atom_occupancy']
- for idx, atom_site in enumerate(sample_model.atom_sites):
+ for idx, atom_site in enumerate(structure.atom_sites):
cryspy_occ[idx] = atom_site.occupancy.value
# Atomic ADPs - Biso only for now
cryspy_biso = cryspy_model_dict['atom_b_iso']
- for idx, atom_site in enumerate(sample_model.atom_sites):
+ for idx, atom_site in enumerate(structure.atom_sites):
cryspy_biso[idx] = atom_site.b_iso.value
+ ##############################
# Update experiment parameters
-
- if experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
- cryspy_expt_name = f'pd_{experiment.name}'
+ ##############################
+
+ if experiment.type.sample_form.value == SampleFormEnum.POWDER:
+ if experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ cryspy_expt_name = f'pd_{experiment.name}'
+ cryspy_expt_dict = cryspy_dict[cryspy_expt_name]
+
+ # Instrument
+ cryspy_expt_dict['offset_ttheta'][0] = np.deg2rad(
+ experiment.instrument.calib_twotheta_offset.value
+ )
+ cryspy_expt_dict['wavelength'][0] = experiment.instrument.setup_wavelength.value
+
+ # Peak
+ cryspy_resolution = cryspy_expt_dict['resolution_parameters']
+ cryspy_resolution[0] = experiment.peak.broad_gauss_u.value
+ cryspy_resolution[1] = experiment.peak.broad_gauss_v.value
+ cryspy_resolution[2] = experiment.peak.broad_gauss_w.value
+ cryspy_resolution[3] = experiment.peak.broad_lorentz_x.value
+ cryspy_resolution[4] = experiment.peak.broad_lorentz_y.value
+
+ elif experiment.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
+ cryspy_expt_name = f'tof_{experiment.name}'
+ cryspy_expt_dict = cryspy_dict[cryspy_expt_name]
+
+ # Instrument
+ cryspy_expt_dict['zero'][0] = experiment.instrument.calib_d_to_tof_offset.value
+ cryspy_expt_dict['dtt1'][0] = experiment.instrument.calib_d_to_tof_linear.value
+ cryspy_expt_dict['dtt2'][0] = experiment.instrument.calib_d_to_tof_quad.value
+ cryspy_expt_dict['ttheta_bank'] = np.deg2rad(
+ experiment.instrument.setup_twotheta_bank.value
+ )
+
+ # Peak
+ cryspy_sigma = cryspy_expt_dict['profile_sigmas']
+ cryspy_sigma[0] = experiment.peak.broad_gauss_sigma_0.value
+ cryspy_sigma[1] = experiment.peak.broad_gauss_sigma_1.value
+ cryspy_sigma[2] = experiment.peak.broad_gauss_sigma_2.value
+
+ cryspy_beta = cryspy_expt_dict['profile_betas']
+ cryspy_beta[0] = experiment.peak.broad_mix_beta_0.value
+ cryspy_beta[1] = experiment.peak.broad_mix_beta_1.value
+
+ cryspy_alpha = cryspy_expt_dict['profile_alphas']
+ cryspy_alpha[0] = experiment.peak.asym_alpha_0.value
+ cryspy_alpha[1] = experiment.peak.asym_alpha_1.value
+
+ if experiment.type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL:
+ cryspy_expt_name = f'diffrn_{experiment.name}'
cryspy_expt_dict = cryspy_dict[cryspy_expt_name]
# Instrument
- cryspy_expt_dict['offset_ttheta'][0] = np.deg2rad(
- experiment.instrument.calib_twotheta_offset.value
- )
- cryspy_expt_dict['wavelength'][0] = experiment.instrument.setup_wavelength.value
-
- # Peak
- cryspy_resolution = cryspy_expt_dict['resolution_parameters']
- cryspy_resolution[0] = experiment.peak.broad_gauss_u.value
- cryspy_resolution[1] = experiment.peak.broad_gauss_v.value
- cryspy_resolution[2] = experiment.peak.broad_gauss_w.value
- cryspy_resolution[3] = experiment.peak.broad_lorentz_x.value
- cryspy_resolution[4] = experiment.peak.broad_lorentz_y.value
-
- elif experiment.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
- cryspy_expt_name = f'tof_{experiment.name}'
- cryspy_expt_dict = cryspy_dict[cryspy_expt_name]
-
- # Instrument
- cryspy_expt_dict['zero'][0] = experiment.instrument.calib_d_to_tof_offset.value
- cryspy_expt_dict['dtt1'][0] = experiment.instrument.calib_d_to_tof_linear.value
- cryspy_expt_dict['dtt2'][0] = experiment.instrument.calib_d_to_tof_quad.value
- cryspy_expt_dict['ttheta_bank'] = np.deg2rad(
- experiment.instrument.setup_twotheta_bank.value
- )
-
- # Peak
- cryspy_sigma = cryspy_expt_dict['profile_sigmas']
- cryspy_sigma[0] = experiment.peak.broad_gauss_sigma_0.value
- cryspy_sigma[1] = experiment.peak.broad_gauss_sigma_1.value
- cryspy_sigma[2] = experiment.peak.broad_gauss_sigma_2.value
+ if experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ cryspy_expt_dict['wavelength'][0] = experiment.instrument.setup_wavelength.value
- cryspy_beta = cryspy_expt_dict['profile_betas']
- cryspy_beta[0] = experiment.peak.broad_mix_beta_0.value
- cryspy_beta[1] = experiment.peak.broad_mix_beta_1.value
-
- cryspy_alpha = cryspy_expt_dict['profile_alphas']
- cryspy_alpha[0] = experiment.peak.asym_alpha_0.value
- cryspy_alpha[1] = experiment.peak.asym_alpha_1.value
+ # Extinction
+ cryspy_expt_dict['extinction_radius'][0] = experiment.extinction.radius.value
+ cryspy_expt_dict['extinction_mosaicity'][0] = experiment.extinction.mosaicity.value
return cryspy_dict
def _recreate_cryspy_obj(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
experiment: ExperimentBase,
- ) -> Any:
- """Recreates the Cryspy object for the given sample model and
- experiment.
-
- Args:
- sample_model: The sample model to recreate.
- experiment: The experiment to recreate.
-
- Returns:
+ ) -> object:
+ """
+ Recreate the Cryspy object for structure and experiment.
+
+ Parameters
+ ----------
+ structure : Structure
+ The structure to recreate.
+ experiment : ExperimentBase
+ The experiment to recreate.
+
+ Returns
+ -------
+ object
The recreated Cryspy object.
"""
cryspy_obj = str_to_globaln('')
- cryspy_sample_model_cif = self._convert_sample_model_to_cryspy_cif(sample_model)
- cryspy_sample_model_obj = str_to_globaln(cryspy_sample_model_cif)
- cryspy_obj.add_items(cryspy_sample_model_obj.items)
+ cryspy_structure_cif = self._convert_structure_to_cryspy_cif(structure)
+ cryspy_structure_obj = str_to_globaln(cryspy_structure_cif)
+ cryspy_obj.add_items(cryspy_structure_obj.items)
# Add single experiment to cryspy_obj
cryspy_experiment_cif = self._convert_experiment_to_cryspy_cif(
experiment,
- linked_phase=sample_model,
+ linked_structure=structure,
)
cryspy_experiment_obj = str_to_globaln(cryspy_experiment_cif)
@@ -268,41 +352,55 @@ def _recreate_cryspy_obj(
return cryspy_obj
- def _convert_sample_model_to_cryspy_cif(
+ def _convert_structure_to_cryspy_cif(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
) -> str:
- """Converts a sample model to a Cryspy CIF string.
+ """
+ Convert a structure to a Cryspy CIF string.
- Args:
- sample_model: The sample model to convert.
+ Parameters
+ ----------
+ structure : Structure
+ The structure to convert.
- Returns:
- The Cryspy CIF string representation of the sample model.
+ Returns
+ -------
+ str
+ The Cryspy CIF string representation of the structure.
"""
- return sample_model.as_cif
+ return structure.as_cif
def _convert_experiment_to_cryspy_cif(
self,
experiment: ExperimentBase,
- linked_phase: Any,
+ linked_structure: object,
) -> str:
- """Converts an experiment to a Cryspy CIF string.
-
- Args:
- experiment: The experiment to convert.
- linked_phase: The linked phase associated with the
- experiment.
-
- Returns:
+ """
+ Convert an experiment to a Cryspy CIF string.
+
+ Parameters
+ ----------
+ experiment : ExperimentBase
+ The experiment to convert.
+ linked_structure : object
+ The structure linked to the experiment.
+
+ Returns
+ -------
+ str
The Cryspy CIF string representation of the experiment.
"""
+ # Try to get experiment attributes
expt_type = getattr(experiment, 'type', None)
instrument = getattr(experiment, 'instrument', None)
peak = getattr(experiment, 'peak', None)
+ extinction = getattr(experiment, 'extinction', None)
+ # Add experiment datablock name
cif_lines = [f'data_{experiment.name}']
+ # Add experiment type attribute dat
if expt_type is not None:
cif_lines.append('')
radiation_probe = expt_type.radiation_probe.value
@@ -310,22 +408,39 @@ def _convert_experiment_to_cryspy_cif(
radiation_probe = radiation_probe.replace('xray', 'X-rays')
cif_lines.append(f'_setup_radiation {radiation_probe}')
+ # Add instrument attribute data
if instrument:
# Restrict to only attributes relevant for the beam mode to
# avoid probing non-existent guarded attributes (which
# triggers diagnostics).
if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
- instrument_mapping = {
- 'setup_wavelength': '_setup_wavelength',
- 'calib_twotheta_offset': '_setup_offset_2theta',
- }
+ if expt_type.sample_form.value == SampleFormEnum.POWDER:
+ instrument_mapping = {
+ 'setup_wavelength': '_setup_wavelength',
+ 'calib_twotheta_offset': '_setup_offset_2theta',
+ }
+ elif expt_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL:
+ instrument_mapping = {
+ 'setup_wavelength': '_setup_wavelength',
+ }
+ # Add dummy 0.0 value for _setup_field required by
+ # Cryspy
+ cif_lines.append('')
+ cif_lines.append('_setup_field 0.0')
elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
- instrument_mapping = {
- 'setup_twotheta_bank': '_tof_parameters_2theta_bank',
- 'calib_d_to_tof_offset': '_tof_parameters_Zero',
- 'calib_d_to_tof_linear': '_tof_parameters_Dtt1',
- 'calib_d_to_tof_quad': '_tof_parameters_dtt2',
- }
+ if expt_type.sample_form.value == SampleFormEnum.POWDER:
+ instrument_mapping = {
+ 'setup_twotheta_bank': '_tof_parameters_2theta_bank',
+ 'calib_d_to_tof_offset': '_tof_parameters_Zero',
+ 'calib_d_to_tof_linear': '_tof_parameters_Dtt1',
+ 'calib_d_to_tof_quad': '_tof_parameters_dtt2',
+ }
+ elif expt_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL:
+ instrument_mapping = {} # TODO: Check this mapping!
+ # Add dummy 0.0 value for _setup_field required by
+ # Cryspy
+ cif_lines.append('')
+ cif_lines.append('_setup_field 0.0')
cif_lines.append('')
for local_attr_name, engine_key_name in instrument_mapping.items():
# attr_obj = instrument.__dict__.get(local_attr_name)
@@ -333,6 +448,7 @@ def _convert_experiment_to_cryspy_cif(
if attr_obj is not None:
cif_lines.append(f'{engine_key_name} {attr_obj.value}')
+ # Add peak attribute data
if peak:
if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
peak_mapping = {
@@ -360,57 +476,140 @@ def _convert_experiment_to_cryspy_cif(
if attr_obj is not None:
cif_lines.append(f'{engine_key_name} {attr_obj.value}')
- x_data = experiment.data.x
- twotheta_min = f'{np.round(x_data.min(), 5):.5f}' # float(x_data.min())
- twotheta_max = f'{np.round(x_data.max(), 5):.5f}' # float(x_data.max())
- cif_lines.append('')
- if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
- cif_lines.append(f'_range_2theta_min {twotheta_min}')
- cif_lines.append(f'_range_2theta_max {twotheta_max}')
- elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
- cif_lines.append(f'_range_time_min {twotheta_min}')
- cif_lines.append(f'_range_time_max {twotheta_max}')
-
- cif_lines.append('')
- cif_lines.append('loop_')
- cif_lines.append('_phase_label')
- cif_lines.append('_phase_scale')
- cif_lines.append(f'{linked_phase.name} 1.0')
-
- if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ # Add extinction attribute data
+ if extinction and expt_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL:
+ extinction_mapping = {
+ 'mosaicity': '_extinction_mosaicity',
+ 'radius': '_extinction_radius',
+ }
cif_lines.append('')
- cif_lines.append('loop_')
- cif_lines.append('_pd_background_2theta')
- cif_lines.append('_pd_background_intensity')
- cif_lines.append(f'{twotheta_min} 0.0')
- cif_lines.append(f'{twotheta_max} 0.0')
- elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
+ cif_lines.append('_extinction_model gauss')
+ for local_attr_name, engine_key_name in extinction_mapping.items():
+ attr_obj = getattr(extinction, local_attr_name)
+ if attr_obj is not None:
+ cif_lines.append(f'{engine_key_name} {attr_obj.value}')
+
+ # Add range data
+ if expt_type.sample_form.value == SampleFormEnum.POWDER:
+ x_data = experiment.data.x
+ twotheta_min = f'{np.round(x_data.min(), 5):.5f}' # float(x_data.min())
+ twotheta_max = f'{np.round(x_data.max(), 5):.5f}' # float(x_data.max())
cif_lines.append('')
- cif_lines.append('loop_')
- cif_lines.append('_tof_backgroundpoint_time')
- cif_lines.append('_tof_backgroundpoint_intensity')
- cif_lines.append(f'{twotheta_min} 0.0')
- cif_lines.append(f'{twotheta_max} 0.0')
+ if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ cif_lines.append(f'_range_2theta_min {twotheta_min}')
+ cif_lines.append(f'_range_2theta_max {twotheta_max}')
+ elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
+ cif_lines.append(f'_range_time_min {twotheta_min}')
+ cif_lines.append(f'_range_time_max {twotheta_max}')
- if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ # Add orientation matrix data
+ # Hardcoded example values for now, as we don't use them yet,
+ # but Cryspy requires them for single crystal data.
+ if expt_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL:
cif_lines.append('')
- cif_lines.append('loop_')
- cif_lines.append('_pd_meas_2theta')
- cif_lines.append('_pd_meas_intensity')
- cif_lines.append('_pd_meas_intensity_sigma')
- elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
+ cif_lines.append('_diffrn_orient_matrix_type CCSL')
+ cif_lines.append('_diffrn_orient_matrix_ub_11 -0.088033')
+ cif_lines.append('_diffrn_orient_matrix_ub_12 -0.088004')
+ cif_lines.append('_diffrn_orient_matrix_ub_13 0.069970')
+ cif_lines.append('_diffrn_orient_matrix_ub_21 0.034058')
+ cif_lines.append('_diffrn_orient_matrix_ub_22 -0.188170')
+ cif_lines.append('_diffrn_orient_matrix_ub_23 -0.013039')
+ cif_lines.append('_diffrn_orient_matrix_ub_31 0.223600')
+ cif_lines.append('_diffrn_orient_matrix_ub_32 0.125751')
+ cif_lines.append('_diffrn_orient_matrix_ub_33 0.029490')
+
+ # Add phase data
+ if expt_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL:
+ cif_lines.append('')
+ cif_lines.append(f'_phase_label {linked_structure.name}')
+ cif_lines.append('_phase_scale 1.0')
+ elif expt_type.sample_form.value == SampleFormEnum.POWDER:
cif_lines.append('')
cif_lines.append('loop_')
- cif_lines.append('_tof_meas_time')
- cif_lines.append('_tof_meas_intensity')
- cif_lines.append('_tof_meas_intensity_sigma')
-
- y_data: np.ndarray = experiment.data.meas
- sy_data: np.ndarray = experiment.data.meas_su
-
- for x_val, y_val, sy_val in zip(x_data, y_data, sy_data, strict=True):
- cif_lines.append(f' {x_val:.5f} {y_val:.5f} {sy_val:.5f}')
+ cif_lines.append('_phase_label')
+ cif_lines.append('_phase_scale')
+ cif_lines.append(f'{linked_structure.name} 1.0')
+ # Add background data
+ if expt_type.sample_form.value == SampleFormEnum.POWDER:
+ if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ cif_lines.append('')
+ cif_lines.append('loop_')
+ cif_lines.append('_pd_background_2theta')
+ cif_lines.append('_pd_background_intensity')
+ cif_lines.append(f'{twotheta_min} 0.0')
+ cif_lines.append(f'{twotheta_max} 0.0')
+ elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
+ cif_lines.append('')
+ cif_lines.append('loop_')
+ cif_lines.append('_tof_backgroundpoint_time') # TODO: !!!!????
+ cif_lines.append('_tof_backgroundpoint_intensity') # TODO: !!!!????
+ cif_lines.append(f'{twotheta_min} 0.0') # TODO: !!!!????
+ cif_lines.append(f'{twotheta_max} 0.0') # TODO: !!!!????
+
+ # Add measured data: Single crystal
+ if expt_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL:
+ if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ cif_lines.append('')
+ cif_lines.append('loop_')
+ cif_lines.append('_diffrn_refln_index_h')
+ cif_lines.append('_diffrn_refln_index_k')
+ cif_lines.append('_diffrn_refln_index_l')
+ cif_lines.append('_diffrn_refln_intensity')
+ cif_lines.append('_diffrn_refln_intensity_sigma')
+ indices_h: np.ndarray = experiment.data.index_h
+ indices_k: np.ndarray = experiment.data.index_k
+ indices_l: np.ndarray = experiment.data.index_l
+ y_data: np.ndarray = experiment.data.intensity_meas
+ sy_data: np.ndarray = experiment.data.intensity_meas_su
+ for index_h, index_k, index_l, y_val, sy_val in zip(
+ indices_h, indices_k, indices_l, y_data, sy_data, strict=True
+ ):
+ cif_lines.append(
+ f'{index_h:4.0f}{index_k:4.0f}{index_l:4.0f} {y_val:.5f} {sy_val:.5f}'
+ )
+ elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
+ cif_lines.append('')
+ cif_lines.append('loop_')
+ cif_lines.append('_diffrn_refln_index_h')
+ cif_lines.append('_diffrn_refln_index_k')
+ cif_lines.append('_diffrn_refln_index_l')
+ cif_lines.append('_diffrn_refln_intensity')
+ cif_lines.append('_diffrn_refln_intensity_sigma')
+ cif_lines.append('_diffrn_refln_wavelength')
+ indices_h: np.ndarray = experiment.data.index_h
+ indices_k: np.ndarray = experiment.data.index_k
+ indices_l: np.ndarray = experiment.data.index_l
+ y_data: np.ndarray = experiment.data.intensity_meas
+ sy_data: np.ndarray = experiment.data.intensity_meas_su
+ wl_data: np.ndarray = experiment.data.wavelength
+ for index_h, index_k, index_l, y_val, sy_val, wl_val in zip(
+ indices_h, indices_k, indices_l, y_data, sy_data, wl_data, strict=True
+ ):
+ cif_lines.append(
+ f'{index_h:4.0f}{index_k:4.0f}{index_l:4.0f} {y_val:.5f} '
+ f'{sy_val:.5f} {wl_val:.5f}'
+ )
+ # Add measured data: Powder
+ elif expt_type.sample_form.value == SampleFormEnum.POWDER:
+ if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
+ cif_lines.append('')
+ cif_lines.append('loop_')
+ cif_lines.append('_pd_meas_2theta')
+ cif_lines.append('_pd_meas_intensity')
+ cif_lines.append('_pd_meas_intensity_sigma')
+ elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
+ cif_lines.append('')
+ cif_lines.append('loop_')
+ cif_lines.append('_tof_meas_time')
+ cif_lines.append('_tof_meas_intensity')
+ cif_lines.append('_tof_meas_intensity_sigma')
+ y_data: np.ndarray = experiment.data.intensity_meas
+ sy_data: np.ndarray = experiment.data.intensity_meas_su
+ for x_val, y_val, sy_val in zip(x_data, y_data, sy_data, strict=True):
+ cif_lines.append(f' {x_val:.5f} {y_val:.5f} {sy_val:.5f}')
+
+ # Combine all lines into a single CIF string
cryspy_experiment_cif = '\n'.join(cif_lines)
return cryspy_experiment_cif
diff --git a/src/easydiffraction/analysis/calculators/factory.py b/src/easydiffraction/analysis/calculators/factory.py
index 79b4eba9..f9860f81 100644
--- a/src/easydiffraction/analysis/calculators/factory.py
+++ b/src/easydiffraction/analysis/calculators/factory.py
@@ -1,105 +1,40 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2025 EasyScience contributors
# 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.
+"""
+
+from __future__ import annotations
from typing import Dict
-from typing import List
-from typing import Optional
from typing import Type
-from typing import Union
-from easydiffraction.analysis.calculators.base import CalculatorBase
-from easydiffraction.analysis.calculators.crysfml import CrysfmlCalculator
-from easydiffraction.analysis.calculators.cryspy import CryspyCalculator
-from easydiffraction.analysis.calculators.pdffit import PdffitCalculator
-from easydiffraction.utils.logging import console
-from easydiffraction.utils.logging import log
-from easydiffraction.utils.utils import render_table
+from easydiffraction.core.factory import FactoryBase
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
-class CalculatorFactory:
- """Factory for creating calculation engine instances.
+class CalculatorFactory(FactoryBase):
+ """
+ Factory for creating calculation engine instances.
- The factory exposes discovery helpers to list and show available
- calculators in the current environment and a creator that returns an
- instantiated calculator or ``None`` if the requested one is not
- available.
+ Only calculators whose ``engine_imported`` flag is ``True`` are
+ available for creation.
"""
- _potential_calculators: Dict[str, Dict[str, Union[str, Type[CalculatorBase]]]] = {
- 'crysfml': {
- 'description': 'CrysFML library for crystallographic calculations',
- 'class': CrysfmlCalculator,
- },
- 'cryspy': {
- 'description': 'CrysPy library for crystallographic calculations',
- 'class': CryspyCalculator,
- },
- 'pdffit': {
- 'description': 'PDFfit2 library for pair distribution function calculations',
- 'class': PdffitCalculator,
- },
+ _default_rules = {
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ }): CalculatorEnum.CRYSPY,
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.TOTAL),
+ }): CalculatorEnum.PDFFIT,
}
@classmethod
- def _supported_calculators(
- cls,
- ) -> Dict[str, Dict[str, Union[str, Type[CalculatorBase]]]]:
- """Return calculators whose engines are importable.
-
- This filters the list of potential calculators by instantiating
- their classes and checking the ``engine_imported`` property.
-
- Returns:
- Mapping from calculator name to its config dict.
- """
- return {
- name: cfg
- for name, cfg in cls._potential_calculators.items()
- if cfg['class']().engine_imported # instantiate and check the @property
- }
-
- @classmethod
- def list_supported_calculators(cls) -> List[str]:
- """List names of calculators available in the environment.
-
- Returns:
- List of calculator identifiers, e.g. ``["crysfml", ...]``.
- """
- return list(cls._supported_calculators().keys())
-
- @classmethod
- def show_supported_calculators(cls) -> None:
- """Pretty-print supported calculators and their descriptions."""
- columns_headers: List[str] = ['Calculator', 'Description']
- columns_alignment = ['left', 'left']
- columns_data: List[List[str]] = []
- for name, config in cls._supported_calculators().items():
- description: str = config.get('description', 'No description provided.')
- columns_data.append([name, description])
-
- console.paragraph('Supported calculators')
- render_table(
- columns_headers=columns_headers,
- columns_alignment=columns_alignment,
- columns_data=columns_data,
- )
-
- @classmethod
- def create_calculator(cls, calculator_name: str) -> Optional[CalculatorBase]:
- """Create a calculator instance by name.
-
- Args:
- calculator_name: Identifier of the calculator to create.
-
- Returns:
- A calculator instance or ``None`` if unknown or unsupported.
- """
- config = cls._supported_calculators().get(calculator_name)
- if not config:
- log.warning(
- f"Unknown calculator '{calculator_name}', "
- f'Supported calculators: {cls.list_supported_calculators()}'
- )
- return None
-
- return config['class']()
+ def _supported_map(cls) -> Dict[str, Type]:
+ """Only include calculators whose engines are importable."""
+ return {klass.type_info.tag: klass for klass in cls._registry if klass.engine_imported}
diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py
index 9f099ff6..4ce20276 100644
--- a/src/easydiffraction/analysis/calculators/pdffit.py
+++ b/src/easydiffraction/analysis/calculators/pdffit.py
@@ -1,6 +1,7 @@
-# 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.
+"""
+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.
@@ -14,8 +15,10 @@
import numpy as np
from easydiffraction.analysis.calculators.base import CalculatorBase
-from easydiffraction.experiments.experiment.base import ExperimentBase
-from easydiffraction.sample_models.sample_model.base import SampleModelBase
+from easydiffraction.analysis.calculators.factory import CalculatorFactory
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.item.base import ExperimentBase
+from easydiffraction.datablocks.structure.item.base import Structure
try:
from diffpy.pdffit2 import PdfFit
@@ -36,30 +39,72 @@
# 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
class PdffitCalculator(CalculatorBase):
"""Wrapper for Pdffit library."""
+ type_info = TypeInfo(
+ tag='pdffit',
+ description='PDFfit2 for pair distribution function calculations',
+ )
engine_imported: bool = PdfFit is not None
@property
- def name(self):
+ def name(self) -> str:
+ """Short identifier of this calculator engine."""
return 'pdffit'
- def calculate_structure_factors(self, sample_models, experiments):
+ def calculate_structure_factors(
+ self,
+ structures: object,
+ experiments: object,
+ ) -> list:
+ """
+ Return an empty list; PDF does not compute 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 sample_models, experiments
+ del structures, experiments
print('[pdffit] Calculating HKLs (not applicable)...')
return []
def calculate_pattern(
self,
- sample_model: SampleModelBase,
+ structure: Structure,
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
@@ -67,12 +112,12 @@ def calculate_pattern(
calculator = PdfFit()
# ---------------------------
- # Set sample model parameters
+ # Set structure parameters
# ---------------------------
# TODO: move CIF v2 -> CIF v1 conversion to a separate module
- # Convert the sample model to CIF supported by PDFfit
- cif_string_v2 = sample_model.as_cif
+ # Convert the structure to CIF supported by PDFfit
+ cif_string_v2 = structure.as_cif
# convert to version 1 of CIF format
# this means: replace all dots with underscores for
# cases where the dot is surrounded by letters on both sides.
@@ -80,18 +125,18 @@ def calculate_pattern(
cif_string_v1 = re.sub(pattern, '_', cif_string_v2)
# Create the PDFit structure
- structure = pdffit_cif_parser().parse(cif_string_v1)
+ pdffit_structure = pdffit_cif_parser().parse(cif_string_v1)
# Set all model parameters:
# space group, cell parameters, and atom sites (including ADPs)
- calculator.add_structure(structure)
+ calculator.add_structure(pdffit_structure)
# -------------------------
# Set experiment parameters
# -------------------------
# Set some peak-related parameters
- calculator.setvar('pscale', experiment.linked_phases[sample_model.name].scale.value)
+ calculator.setvar('pscale', experiment.linked_phases[structure.name].scale.value)
calculator.setvar('delta1', experiment.peak.sharp_delta_1.value)
calculator.setvar('delta2', experiment.peak.sharp_delta_2.value)
calculator.setvar('spdiameter', experiment.peak.damp_particle_diameter.value)
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.py b/src/easydiffraction/analysis/categories/aliases.py
deleted file mode 100644
index b55db11d..00000000
--- a/src/easydiffraction/analysis/categories/aliases.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""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.
-"""
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RegexValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class Alias(CategoryItem):
- """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``.
- """
-
- def __init__(
- self,
- *,
- label: str,
- param_uid: str,
- ) -> None:
- super().__init__()
-
- self._label: StringDescriptor = StringDescriptor(
- name='label',
- description='...',
- value_spec=AttributeSpec(
- value=label,
- type_=DataTypes.STRING,
- default='...',
- content_validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_alias.label',
- ]
- ),
- )
- self._param_uid: StringDescriptor = StringDescriptor(
- name='param_uid',
- description='...',
- value_spec=AttributeSpec(
- value=param_uid,
- type_=DataTypes.STRING,
- default='...',
- content_validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_alias.param_uid',
- ]
- ),
- )
-
- self._identity.category_code = 'alias'
- self._identity.category_entry_name = lambda: str(self.label.value)
-
- @property
- def label(self):
- """Alias label descriptor."""
- return self._label
-
- @label.setter
- def label(self, value):
- """Set alias label.
-
- Args:
- value: New label.
- """
- self._label.value = value
-
- @property
- def param_uid(self):
- """Parameter uid descriptor the alias points to."""
- return self._param_uid
-
- @param_uid.setter
- def param_uid(self, value):
- """Set the parameter uid.
-
- Args:
- value: New uid.
- """
- self._param_uid.value = value
-
-
-class Aliases(CategoryCollection):
- """Collection of :class:`Alias` items."""
-
- def __init__(self):
- """Create an empty collection of aliases."""
- super().__init__(item_type=Alias)
diff --git a/src/easydiffraction/analysis/categories/aliases/__init__.py b/src/easydiffraction/analysis/categories/aliases/__init__.py
new file mode 100644
index 00000000..6ca3a859
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/aliases/__init__.py
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.analysis.categories.aliases.default import Alias
+from easydiffraction.analysis.categories.aliases.default import Aliases
diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py
new file mode 100644
index 00000000..7b1e0df0
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/aliases/default.py
@@ -0,0 +1,101 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+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.
+"""
+
+from __future__ import annotations
+
+from easydiffraction.analysis.categories.aliases.factory import AliasesFactory
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class Alias(CategoryItem):
+ """
+ Single alias entry.
+
+ Maps a human-readable ``label`` to a concrete ``param_uid`` used by
+ the engine.
+ """
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._label = StringDescriptor(
+ name='label',
+ description='...', # TODO
+ value_spec=AttributeSpec(
+ default='_', # TODO, Maybe None?
+ validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(names=['_alias.label']),
+ )
+ self._param_uid = StringDescriptor(
+ name='param_uid',
+ description='...', # TODO
+ value_spec=AttributeSpec(
+ default='_',
+ validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(names=['_alias.param_uid']),
+ )
+
+ self._identity.category_code = 'alias'
+ self._identity.category_entry_name = lambda: str(self.label.value)
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def label(self) -> 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:
+ self._label.value = value
+
+ @property
+ def param_uid(self) -> 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:
+ self._param_uid.value = value
+
+
+@AliasesFactory.register
+class Aliases(CategoryCollection):
+ """Collection of :class:`Alias` items."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Parameter alias mappings',
+ )
+
+ def __init__(self) -> None:
+ """Create an empty collection of aliases."""
+ super().__init__(item_type=Alias)
diff --git a/src/easydiffraction/analysis/categories/aliases/factory.py b/src/easydiffraction/analysis/categories/aliases/factory.py
new file mode 100644
index 00000000..f2bebe43
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/aliases/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Aliases factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class AliasesFactory(FactoryBase):
+ """Create alias collections by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/analysis/categories/constraints.py b/src/easydiffraction/analysis/categories/constraints.py
deleted file mode 100644
index 205a28b1..00000000
--- a/src/easydiffraction/analysis/categories/constraints.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""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.
-"""
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.singletons import ConstraintsHandler
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RegexValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-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.
- """
-
- def __init__(
- self,
- *,
- lhs_alias: str,
- rhs_expr: str,
- ) -> None:
- super().__init__()
-
- self._lhs_alias: StringDescriptor = StringDescriptor(
- name='lhs_alias',
- description='...',
- value_spec=AttributeSpec(
- value=lhs_alias,
- type_=DataTypes.STRING,
- default='...',
- content_validator=RegexValidator(pattern=r'.*'),
- ),
- cif_handler=CifHandler(
- names=[
- '_constraint.lhs_alias',
- ]
- ),
- )
- self._rhs_expr: StringDescriptor = StringDescriptor(
- name='rhs_expr',
- description='...',
- value_spec=AttributeSpec(
- value=rhs_expr,
- type_=DataTypes.STRING,
- default='...',
- content_validator=RegexValidator(pattern=r'.*'),
- ),
- cif_handler=CifHandler(
- names=[
- '_constraint.rhs_expr',
- ]
- ),
- )
-
- self._identity.category_code = 'constraint'
- self._identity.category_entry_name = lambda: str(self.lhs_alias.value)
-
- @property
- def lhs_alias(self):
- """Alias name on the left-hand side of the equation."""
- return self._lhs_alias
-
- @lhs_alias.setter
- def lhs_alias(self, value):
- """Set the left-hand side alias.
-
- Args:
- value: New alias string.
- """
- self._lhs_alias.value = value
-
- @property
- def rhs_expr(self):
- """Right-hand side expression string."""
- return self._rhs_expr
-
- @rhs_expr.setter
- def rhs_expr(self, value):
- """Set the right-hand side expression.
-
- Args:
- value: New expression string.
- """
- self._rhs_expr.value = value
-
-
-class Constraints(CategoryCollection):
- """Collection of :class:`Constraint` items."""
-
- _update_priority = 90 # After most others, but before data categories
-
- def __init__(self):
- """Create an empty constraints collection."""
- super().__init__(item_type=Constraint)
-
- def _update(self, called_by_minimizer=False):
- del called_by_minimizer
-
- constraints = ConstraintsHandler.get()
- constraints.apply()
diff --git a/src/easydiffraction/analysis/categories/constraints/__init__.py b/src/easydiffraction/analysis/categories/constraints/__init__.py
new file mode 100644
index 00000000..97d8c03c
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/constraints/__init__.py
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.analysis.categories.constraints.default import Constraint
+from easydiffraction.analysis.categories.constraints.default import Constraints
diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py
new file mode 100644
index 00000000..3bb1b77e
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/constraints/default.py
@@ -0,0 +1,124 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+Simple symbolic constraint between parameters.
+
+Represents an equation of the form ``lhs_alias = rhs_expr`` stored as a
+single expression string. The left- and right-hand sides are derived by
+splitting the expression at the ``=`` sign.
+"""
+
+from __future__ import annotations
+
+from easydiffraction.analysis.categories.constraints.factory import ConstraintsFactory
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.singleton import ConstraintsHandler
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class Constraint(CategoryItem):
+ """Single constraint item stored as ``lhs = rhs`` expression."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._expression = StringDescriptor(
+ name='expression',
+ description='Constraint equation, e.g. "occ_Ba = 1 - occ_La".',
+ value_spec=AttributeSpec(
+ default='_', # TODO, Maybe None?
+ validator=RegexValidator(pattern=r'.*'),
+ ),
+ cif_handler=CifHandler(names=['_constraint.expression']),
+ )
+
+ self._identity.category_code = 'constraint'
+ self._identity.category_entry_name = lambda: self.lhs_alias
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def expression(self) -> StringDescriptor:
+ """
+ Full constraint equation (e.g. ``'occ_Ba = 1 - occ_La'``).
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object. Assigning to it updates the value.
+ """
+ return self._expression
+
+ @expression.setter
+ def expression(self, value: str) -> None:
+ self._expression.value = value
+
+ @property
+ def lhs_alias(self) -> str:
+ """Left-hand side alias derived from the expression."""
+ return self._split_expression()[0]
+
+ @property
+ def rhs_expr(self) -> str:
+ """Right-hand side expression derived from the expression."""
+ return self._split_expression()[1]
+
+ # ------------------------------------------------------------------
+ # Internal helpers
+ # ------------------------------------------------------------------
+
+ def _split_expression(self) -> tuple[str, str]:
+ """
+ Split the expression at the first ``=`` sign.
+
+ Returns
+ -------
+ tuple[str, str]
+ ``(lhs_alias, rhs_expr)`` with whitespace stripped.
+ """
+ raw = self._expression.value or ''
+ if '=' not in raw:
+ return (raw.strip(), '')
+ lhs, rhs = raw.split('=', 1)
+ return (lhs.strip(), rhs.strip())
+
+
+@ConstraintsFactory.register
+class Constraints(CategoryCollection):
+ """Collection of :class:`Constraint` items."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Symbolic parameter constraints',
+ )
+
+ _update_priority = 90 # After most others, but before data categories
+
+ def __init__(self) -> None:
+ """Create an empty constraints collection."""
+ super().__init__(item_type=Constraint)
+
+ def create(self, *, expression: str) -> None:
+ """
+ Create a constraint from an expression string.
+
+ Parameters
+ ----------
+ expression : str
+ Constraint equation, e.g. ``'biso_Co2 = biso_Co1'`` or
+ ``'occ_Ba = 1 - occ_La'``.
+ """
+ item = Constraint()
+ item.expression = expression
+ self.add(item)
+
+ def _update(self, called_by_minimizer: bool = False) -> None:
+ del called_by_minimizer
+
+ constraints = ConstraintsHandler.get()
+ constraints.apply()
diff --git a/src/easydiffraction/analysis/categories/constraints/factory.py b/src/easydiffraction/analysis/categories/constraints/factory.py
new file mode 100644
index 00000000..682c9684
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/constraints/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Constraints factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class ConstraintsFactory(FactoryBase):
+ """Create constraint collections by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/analysis/categories/fit_mode/__init__.py b/src/easydiffraction/analysis/categories/fit_mode/__init__.py
new file mode 100644
index 00000000..058d054c
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/fit_mode/__init__.py
@@ -0,0 +1,6 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.analysis.categories.fit_mode.enums import FitModeEnum
+from easydiffraction.analysis.categories.fit_mode.factory import FitModeFactory
+from easydiffraction.analysis.categories.fit_mode.fit_mode import FitMode
diff --git a/src/easydiffraction/analysis/categories/fit_mode/enums.py b/src/easydiffraction/analysis/categories/fit_mode/enums.py
new file mode 100644
index 00000000..5e904eae
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/fit_mode/enums.py
@@ -0,0 +1,26 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Enumeration for fit-mode values."""
+
+from __future__ import annotations
+
+from enum import Enum
+
+
+class FitModeEnum(str, Enum):
+ """Fitting strategy for the analysis."""
+
+ SINGLE = 'single'
+ JOINT = 'joint'
+
+ @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:
+ return 'Simultaneous fitting of all experiments; some parameters are shared'
diff --git a/src/easydiffraction/analysis/categories/fit_mode/factory.py b/src/easydiffraction/analysis/categories/fit_mode/factory.py
new file mode 100644
index 00000000..f10485f8
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/fit_mode/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Fit-mode factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class FitModeFactory(FactoryBase):
+ """Create fit-mode category items by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py
new file mode 100644
index 00000000..b0589a2e
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/fit_mode/fit_mode.py
@@ -0,0 +1,64 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+Fit-mode category item.
+
+Stores the active fitting strategy as a CIF-serializable descriptor
+validated by ``FitModeEnum``.
+"""
+
+from __future__ import annotations
+
+from easydiffraction.analysis.categories.fit_mode.enums import FitModeEnum
+from easydiffraction.analysis.categories.fit_mode.factory import FitModeFactory
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import MembershipValidator
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@FitModeFactory.register
+class FitMode(CategoryItem):
+ """
+ Fitting strategy selector.
+
+ Holds a single ``mode`` descriptor whose value is one of
+ ``FitModeEnum`` members (``'single'`` or ``'joint'``).
+ """
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Fit-mode category',
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._mode: StringDescriptor = StringDescriptor(
+ name='mode',
+ description='Fitting strategy',
+ value_spec=AttributeSpec(
+ default=FitModeEnum.default().value,
+ validator=MembershipValidator(allowed=[member.value for member in FitModeEnum]),
+ ),
+ cif_handler=CifHandler(names=['_analysis.fit_mode']),
+ )
+
+ self._identity.category_code = 'fit_mode'
+
+ @property
+ def mode(self) -> 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:
+ self._mode.value = value
diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments.py b/src/easydiffraction/analysis/categories/joint_fit_experiments.py
deleted file mode 100644
index 818dd6f6..00000000
--- a/src/easydiffraction/analysis/categories/joint_fit_experiments.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Joint-fit experiment weighting configuration.
-
-Stores per-experiment weights to be used when multiple experiments are
-fitted simultaneously.
-"""
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.core.validation import RegexValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-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.
- """
-
- def __init__(
- self,
- *,
- id: str,
- weight: float,
- ) -> None:
- super().__init__()
-
- self._id: StringDescriptor = StringDescriptor(
- name='id', # TODO: need new name instead of id
- description='...',
- value_spec=AttributeSpec(
- value=id,
- type_=DataTypes.STRING,
- default='...',
- content_validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_joint_fit_experiment.id',
- ]
- ),
- )
- self._weight: NumericDescriptor = NumericDescriptor(
- name='weight',
- description='...',
- value_spec=AttributeSpec(
- value=weight,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_joint_fit_experiment.weight',
- ]
- ),
- )
-
- self._identity.category_code = 'joint_fit_experiment'
- self._identity.category_entry_name = lambda: str(self.id.value)
-
- @property
- def id(self):
- """Experiment identifier descriptor."""
- return self._id
-
- @id.setter
- def id(self, value):
- """Set the experiment identifier.
-
- Args:
- value: New id string.
- """
- self._id.value = value
-
- @property
- def weight(self):
- """Weight factor descriptor."""
- return self._weight
-
- @weight.setter
- def weight(self, value):
- """Set the weight factor.
-
- Args:
- value: New weight value.
- """
- self._weight.value = value
-
-
-class JointFitExperiments(CategoryCollection):
- """Collection of :class:`JointFitExperiment` items."""
-
- def __init__(self):
- """Create an empty joint-fit experiments collection."""
- super().__init__(item_type=JointFitExperiment)
diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/__init__.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/__init__.py
new file mode 100644
index 00000000..1aa8f0ae
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/__init__.py
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.analysis.categories.joint_fit_experiments.default import JointFitExperiment
+from easydiffraction.analysis.categories.joint_fit_experiments.default import JointFitExperiments
diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py
new file mode 100644
index 00000000..b94bd7de
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/default.py
@@ -0,0 +1,100 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+Joint-fit experiment weighting configuration.
+
+Stores per-experiment weights to be used when multiple experiments are
+fitted simultaneously.
+"""
+
+from __future__ import annotations
+
+from easydiffraction.analysis.categories.joint_fit_experiments.factory import (
+ JointFitExperimentsFactory,
+)
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class JointFitExperiment(CategoryItem):
+ """A single joint-fit entry."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._id: StringDescriptor = StringDescriptor(
+ name='id', # TODO: need new name instead of id
+ description='Experiment identifier', # TODO
+ value_spec=AttributeSpec(
+ default='_',
+ validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(names=['_joint_fit_experiment.id']),
+ )
+ self._weight: NumericDescriptor = NumericDescriptor(
+ name='weight',
+ description='Weight factor', # TODO
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_joint_fit_experiment.weight']),
+ )
+
+ self._identity.category_code = 'joint_fit_experiment'
+ self._identity.category_entry_name = lambda: str(self.id.value)
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def id(self) -> 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:
+ self._id.value = value
+
+ @property
+ def weight(self) -> 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:
+ self._weight.value = value
+
+
+@JointFitExperimentsFactory.register
+class JointFitExperiments(CategoryCollection):
+ """Collection of :class:`JointFitExperiment` items."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Joint-fit experiment weights',
+ )
+
+ def __init__(self) -> None:
+ """Create an empty joint-fit experiments collection."""
+ super().__init__(item_type=JointFitExperiment)
diff --git a/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py
new file mode 100644
index 00000000..57666098
--- /dev/null
+++ b/src/easydiffraction/analysis/categories/joint_fit_experiments/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Joint-fit-experiments factory — delegates to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class JointFitExperimentsFactory(FactoryBase):
+ """Create joint-fit experiment collections by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
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 b6ef9928..dee08f86 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
@@ -6,22 +6,27 @@
import numpy as np
-from easydiffraction.experiments.experiments import Experiments
-from easydiffraction.sample_models.sample_models import SampleModels
+from easydiffraction.datablocks.experiment.collection import Experiments
+from easydiffraction.datablocks.structure.collection import Structures
def calculate_r_factor(
y_obs: np.ndarray,
y_calc: np.ndarray,
) -> float:
- """Calculate the R-factor (reliability factor) between observed and
- calculated data.
-
- Args:
- y_obs: Observed data points.
- y_calc: Calculated data points.
-
- Returns:
+ """
+ Calculate the R-factor between observed and calculated data.
+
+ Parameters
+ ----------
+ y_obs : np.ndarray
+ Observed data points.
+ y_calc : np.ndarray
+ Calculated data points.
+
+ Returns
+ -------
+ float
R-factor value.
"""
y_obs = np.asarray(y_obs)
@@ -36,15 +41,21 @@ def calculate_weighted_r_factor(
y_calc: np.ndarray,
weights: np.ndarray,
) -> float:
- """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.
-
- Returns:
+ """
+ Calculate weighted R-factor between observed and calculated data.
+
+ Parameters
+ ----------
+ y_obs : np.ndarray
+ Observed data points.
+ y_calc : np.ndarray
+ Calculated data points.
+ weights : np.ndarray
+ Weights for each data point.
+
+ Returns
+ -------
+ float
Weighted R-factor value.
"""
y_obs = np.asarray(y_obs)
@@ -59,14 +70,19 @@ def calculate_rb_factor(
y_obs: np.ndarray,
y_calc: np.ndarray,
) -> float:
- """Calculate the Bragg R-factor between observed and calculated
- data.
-
- Args:
- y_obs: Observed data points.
- y_calc: Calculated data points.
-
- Returns:
+ """
+ Calculate the Bragg R-factor between observed and calculated data.
+
+ Parameters
+ ----------
+ y_obs : np.ndarray
+ Observed data points.
+ y_calc : np.ndarray
+ Calculated data points.
+
+ Returns
+ -------
+ float
Bragg R-factor value.
"""
y_obs = np.asarray(y_obs)
@@ -80,14 +96,19 @@ def calculate_r_factor_squared(
y_obs: np.ndarray,
y_calc: np.ndarray,
) -> float:
- """Calculate the R-factor squared between observed and calculated
- data.
-
- Args:
- y_obs: Observed data points.
- y_calc: Calculated data points.
-
- Returns:
+ """
+ Calculate the R-factor squared between observed and calculated data.
+
+ Parameters
+ ----------
+ y_obs : np.ndarray
+ Observed data points.
+ y_calc : np.ndarray
+ Calculated data points.
+
+ Returns
+ -------
+ float
R-factor squared value.
"""
y_obs = np.asarray(y_obs)
@@ -101,13 +122,19 @@ def calculate_reduced_chi_square(
residuals: np.ndarray,
num_parameters: int,
) -> 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.
-
- Returns:
+ """
+ Calculate the reduced chi-square statistic.
+
+ Parameters
+ ----------
+ residuals : np.ndarray
+ Residuals between observed and calculated data.
+ num_parameters : int
+ Number of free parameters used in the model.
+
+ Returns
+ -------
+ float
Reduced chi-square value.
"""
residuals = np.asarray(residuals)
@@ -121,31 +148,39 @@ def calculate_reduced_chi_square(
def get_reliability_inputs(
- sample_models: SampleModels,
+ structures: Structures,
experiments: Experiments,
) -> Tuple[np.ndarray, np.ndarray, Optional[np.ndarray]]:
- """Collect observed and calculated data points for reliability
- calculations.
-
- Args:
- sample_models: Collection of sample models.
- experiments: Collection of experiments.
-
- Returns:
- Tuple containing arrays of (observed values, calculated values,
- error values)
+ """
+ Collect observed and calculated data for reliability calculations.
+
+ Parameters
+ ----------
+ structures : Structures
+ Collection of structures.
+ experiments : Experiments
+ Collection of experiments.
+
+ Returns
+ -------
+ np.ndarray
+ Observed values.
+ np.ndarray
+ Calculated values.
+ Optional[np.ndarray]
+ Error values, or None if not available.
"""
y_obs_all = []
y_calc_all = []
y_err_all = []
for experiment in experiments.values():
- for sample_model in sample_models:
- sample_model._update_categories()
+ for structure in structures:
+ structure._update_categories()
experiment._update_categories()
- y_calc = experiment.data.calc
- y_meas = experiment.data.meas
- y_meas_su = experiment.data.meas_su
+ y_calc = experiment.data.intensity_calc
+ y_meas = experiment.data.intensity_meas
+ y_meas_su = experiment.data.intensity_meas_su
if y_meas is not None and y_calc is not None:
# If standard uncertainty is not provided, use ones
diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py
index d9dbc40e..a1468aa5 100644
--- a/src/easydiffraction/analysis/fit_helpers/reporting.py
+++ b/src/easydiffraction/analysis/fit_helpers/reporting.py
@@ -1,7 +1,6 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
-from typing import Any
from typing import List
from typing import Optional
@@ -14,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
@@ -24,41 +24,53 @@ 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.
-
- 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 : bool, default=False
+ Indicates if the fit was successful.
+ parameters : Optional[List[object]], default=None
+ List of parameters used in the fit.
+ chi_square : Optional[float], default=None
+ Chi-square value of the fit.
+ reduced_chi_square : Optional[float], default=None
+ Reduced chi-square value of the fit.
+ message : str, default=''
+ Message related to the fit.
+ iterations : int, default=0
+ Number of iterations performed.
+ engine_result : Optional[object], default=None
+ Result from the fitting engine.
+ starting_parameters : Optional[List[object]], default=None
+ Initial parameters for the fit.
+ 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.
"""
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
@@ -77,14 +89,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 : Optional[List[float]], default=None
+ Observed intensities for pattern R-factor metrics.
+ y_calc : Optional[List[float]], default=None
+ Calculated intensities for pattern R-factor metrics.
+ y_err : Optional[List[float]], default=None
+ Standard deviations of observed intensities for wR.
+ f_obs : Optional[List[float]], default=None
+ Observed structure-factor magnitudes for Bragg R.
+ f_calc : Optional[List[float]], default=None
+ 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 438fd46d..804dc536 100644
--- a/src/easydiffraction/analysis/fit_helpers/tracking.py
+++ b/src/easydiffraction/analysis/fit_helpers/tracking.py
@@ -1,9 +1,8 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
import time
from contextlib import suppress
-from typing import Any
from typing import List
from typing import Optional
@@ -20,6 +19,7 @@
clear_output = None
from easydiffraction.analysis.fit_helpers.metrics import calculate_reduced_chi_square
+from easydiffraction.utils.enums import VerbosityEnum
from easydiffraction.utils.environment import in_jupyter
from easydiffraction.utils.utils import render_table
@@ -36,31 +36,40 @@
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.
"""
- 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:
+ """
+ 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()
-def _make_display_handle() -> Any | None:
- """Create and initialize a display/update handle for the
- environment.
+def _make_display_handle() -> object | None:
+ """
+ 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 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()
@@ -77,7 +86,8 @@ def _make_display_handle() -> Any | 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
@@ -92,10 +102,11 @@ def __init__(self) -> None:
self._best_chi2: Optional[float] = None
self._best_iteration: Optional[int] = None
self._fitting_time: Optional[float] = None
+ self._verbosity: VerbosityEnum = VerbosityEnum.FULL
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."""
@@ -112,13 +123,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.
-
- Returns:
+ """
+ Update progress with current residuals and parameters.
+
+ Parameters
+ ----------
+ residuals : np.ndarray
+ Residuals between measured and calculated data.
+ parameters : List[float]
+ Current free parameters being fitted.
+
+ Returns
+ -------
+ np.ndarray
Residuals unchanged, for optimizer consumption.
"""
self._iteration += 1
@@ -200,11 +217,19 @@ 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 : str
+ Name of the minimizer used for the run.
"""
+ if self._verbosity is VerbosityEnum.SILENT:
+ return
+ if self._verbosity is VerbosityEnum.SHORT:
+ return
+
console.print(f"🚀 Starting fit process with '{minimizer_name}'...")
console.print('📈 Goodness-of-fit (reduced χ²) change:')
@@ -221,14 +246,19 @@ 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 : List[str]
+ Columns corresponding to DEFAULT_HEADERS.
"""
+ self._df_rows.append(row)
+ if self._verbosity is not VerbosityEnum.FULL:
+ return
# Append and update via the active handle (Jupyter or
# terminal live)
- self._df_rows.append(row)
render_table(
columns_headers=DEFAULT_HEADERS,
columns_alignment=DEFAULT_ALIGNMENTS,
@@ -246,6 +276,9 @@ def finish_tracking(self) -> None:
]
self.add_tracking_info(row)
+ if self._verbosity is not VerbosityEnum.FULL:
+ return
+
# Close terminal live if used
if self._display_handle is not None and hasattr(self._display_handle, 'close'):
with suppress(Exception):
diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py
index ee6e3c84..de6dcdaa 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
@@ -11,9 +11,10 @@
from easydiffraction.analysis.fit_helpers.metrics import get_reliability_inputs
from easydiffraction.analysis.minimizers.factory import MinimizerFactory
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.experiments.experiments import Experiments
-from easydiffraction.sample_models.sample_models import SampleModels
+from easydiffraction.core.variable import Parameter
+from easydiffraction.datablocks.experiment.collection import Experiments
+from easydiffraction.datablocks.structure.collection import Structures
+from easydiffraction.utils.enums import VerbosityEnum
if TYPE_CHECKING:
from easydiffraction.analysis.fit_helpers.reporting import FitResults
@@ -22,33 +23,42 @@
class Fitter:
"""Handles the fitting workflow using a pluggable minimizer."""
- def __init__(self, selection: str = 'lmfit (leastsq)') -> None:
+ def __init__(self, selection: str = 'lmfit') -> None:
self.selection: str = selection
- self.engine: str = selection.split(' ')[0] # Extracts 'lmfit' or 'dfols'
- self.minimizer = MinimizerFactory.create_minimizer(selection)
+ self.engine: str = selection
+ self.minimizer = MinimizerFactory.create(selection)
self.results: Optional[FitResults] = None
def fit(
self,
- sample_models: SampleModels,
+ structures: Structures,
experiments: Experiments,
weights: Optional[np.array] = None,
- analysis=None,
+ analysis: object = None,
+ verbosity: VerbosityEnum = VerbosityEnum.FULL,
) -> 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:
- sample_models: Collection of sample models.
- 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.
+
+ Parameters
+ ----------
+ structures : Structures
+ Collection of structures.
+ experiments : Experiments
+ Collection of experiments.
+ weights : Optional[np.array], default=None
+ Optional weights for joint fitting.
+ analysis : object, default=None
+ Optional Analysis object to update its categories during
+ fitting.
+ verbosity : VerbosityEnum, default=VerbosityEnum.FULL
+ Console output verbosity.
"""
- params = sample_models.free_parameters + experiments.free_parameters
+ params = structures.free_parameters + experiments.free_parameters
if not params:
print('⚠️ No parameters selected for fitting.')
@@ -58,36 +68,53 @@ 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,
- sample_models=sample_models,
+ structures=structures,
experiments=experiments,
weights=weights,
analysis=analysis,
)
# Perform fitting
- self.results = self.minimizer.fit(params, objective_function)
+ self.results = self.minimizer.fit(params, objective_function, verbosity=verbosity)
def _process_fit_results(
self,
- sample_models: SampleModels,
+ 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:
- sample_models: Collection of sample models.
- experiments: Collection of experiments.
+ calculates R-factors and other metrics, then renders them to the
+ console.
+
+ Parameters
+ ----------
+ structures : Structures
+ Collection of structures.
+ experiments : Experiments
+ Collection of experiments.
"""
y_obs, y_calc, y_err = get_reliability_inputs(
- sample_models,
+ structures,
experiments,
)
@@ -105,54 +132,71 @@ def _process_fit_results(
def _collect_free_parameters(
self,
- sample_models: SampleModels,
+ structures: Structures,
experiments: Experiments,
) -> List[Parameter]:
- """Collect free parameters from sample models and experiments.
-
- Args:
- sample_models: Collection of sample models.
- experiments: Collection of experiments.
-
- Returns:
+ """
+ Collect free parameters from structures and experiments.
+
+ Parameters
+ ----------
+ structures : Structures
+ Collection of structures.
+ experiments : Experiments
+ Collection of experiments.
+
+ Returns
+ -------
+ List[Parameter]
List of free parameters.
"""
- free_params: List[Parameter] = sample_models.free_parameters + experiments.free_parameters
+ free_params: List[Parameter] = structures.free_parameters + experiments.free_parameters
return free_params
def _residual_function(
self,
engine_params: Dict[str, Any],
parameters: List[Parameter],
- sample_models: SampleModels,
+ structures: Structures,
experiments: Experiments,
weights: Optional[np.array] = None,
- analysis=None,
+ analysis: object = 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.
-
- Args:
- engine_params: Engine-specific parameter dict.
- parameters: List of parameters being optimized.
- sample_models: Collection of sample models.
- experiments: Collection of experiments.
- weights: Optional weights for joint fitting.
- analysis: Optional Analysis object to update its categories
- during fitting.
-
- Returns:
+ """
+ Compute residuals between measured and calculated patterns.
+
+ It updates the parameter values according to the
+ optimizer-provided engine_params.
+
+ Parameters
+ ----------
+ engine_params : Dict[str, Any]
+ Engine-specific parameter dict.
+ parameters : List[Parameter]
+ List of parameters being optimized.
+ structures : Structures
+ Collection of structures.
+ experiments : Experiments
+ Collection of experiments.
+ weights : Optional[np.array], default=None
+ Optional weights for joint fitting.
+ 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
self.minimizer._sync_result_to_parameters(parameters, engine_params)
# Update categories to reflect new parameter values
- # Order matters: sample models first (symmetry, structure),
+ # Order matters: structures first (symmetry, structure),
# then analysis (constraints), then experiments (calculations)
- for sample_model in sample_models:
- sample_model._update_categories()
+ for structure in structures:
+ structure._update_categories()
if analysis is not None:
analysis._update_categories(called_by_minimizer=True)
@@ -182,9 +226,9 @@ def _residual_function(
# Calculate the difference between measured and calculated
# patterns
- y_calc: np.ndarray = experiment.data.calc
- y_meas: np.ndarray = experiment.data.meas
- y_meas_su: np.ndarray = experiment.data.meas_su
+ y_calc: np.ndarray = experiment.data.intensity_calc
+ y_meas: np.ndarray = experiment.data.intensity_meas
+ y_meas_su: np.ndarray = experiment.data.intensity_meas_su
diff = (y_meas - y_calc) / y_meas_su
# Residuals are squared before going into reduced
diff --git a/src/easydiffraction/analysis/minimizers/__init__.py b/src/easydiffraction/analysis/minimizers/__init__.py
index 429f2648..01fbc136 100644
--- a/src/easydiffraction/analysis/minimizers/__init__.py
+++ b/src/easydiffraction/analysis/minimizers/__init__.py
@@ -1,2 +1,5 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2025 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.analysis.minimizers.dfols import DfolsMinimizer
+from easydiffraction.analysis.minimizers.lmfit import LmfitMinimizer
diff --git a/src/easydiffraction/analysis/minimizers/base.py b/src/easydiffraction/analysis/minimizers/base.py
index 275ad973..63b6bc27 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
@@ -13,17 +13,17 @@
from easydiffraction.analysis.fit_helpers.reporting import FitResults
from easydiffraction.analysis.fit_helpers.tracking import FitProgressTracker
+from easydiffraction.utils.enums import VerbosityEnum
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`.
+ """
+ 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`.
"""
def __init__(
@@ -43,13 +43,23 @@ def __init__(
self._fitting_time: Optional[float] = None
self.tracker: FitProgressTracker = FitProgressTracker()
- def _start_tracking(self, minimizer_name: str) -> None:
- """Initialize progress tracking and timer.
-
- Args:
- minimizer_name: Human-readable name shown in progress.
+ def _start_tracking(
+ self,
+ minimizer_name: str,
+ verbosity: VerbosityEnum = VerbosityEnum.FULL,
+ ) -> None:
+ """
+ Initialize progress tracking and timer.
+
+ Parameters
+ ----------
+ minimizer_name : str
+ Human-readable name shown in progress.
+ verbosity : VerbosityEnum, default=VerbosityEnum.FULL
+ Console output verbosity.
"""
self.tracker.reset()
+ self.tracker._verbosity = verbosity
self.tracker.start_tracking(minimizer_name)
self.tracker.start_timer()
@@ -60,12 +70,17 @@ 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.
- Args:
- parameters: List of free parameters to be fitted.
+ Parameters
+ ----------
+ parameters : List[Any]
+ List of free parameters to be fitted.
- Returns:
+ Returns
+ -------
+ Dict[str, Any]
Mapping of keyword arguments to pass into ``_run_solver``.
"""
pass
@@ -73,36 +88,40 @@ 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.
- """
+ """Copy raw_result values back to parameters in-place."""
pass
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``.
-
- Args:
- parameters: Parameters after the solver finished.
- raw_result: Backend-specific solver output object.
-
- Returns:
- FitResults: Aggregated outcome of the fit.
+ """
+ Build :class:`FitResults` and store it on ``self.result``.
+
+ Parameters
+ ----------
+ parameters : List[object]
+ Parameters after the solver finished.
+ raw_result : object
+ 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)
@@ -117,30 +136,39 @@ 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],
+ verbosity: VerbosityEnum = VerbosityEnum.FULL,
) -> FitResults:
- """Run the full minimization workflow.
-
- Args:
- parameters: Free parameters to optimize.
- objective_function: Callable returning residuals for a given
- set of engine arguments.
-
- Returns:
+ """
+ Run the full minimization workflow.
+
+ Parameters
+ ----------
+ parameters : List[object]
+ Free parameters to optimize.
+ objective_function : Callable[..., object]
+ Callable returning residuals for a given set of engine
+ arguments.
+ verbosity : VerbosityEnum, default=VerbosityEnum.FULL
+ Console output verbosity.
+
+ Returns
+ -------
+ FitResults
FitResults with success flag, best chi2 and timing.
"""
minimizer_name = self.name or 'Unnamed Minimizer'
if self.method is not None:
minimizer_name += f' ({self.method})'
- self._start_tracking(minimizer_name)
+ self._start_tracking(minimizer_name, verbosity=verbosity)
solver_args = self._prepare_solver_args(parameters)
raw_result = self._run_solver(objective_function, **solver_args)
@@ -153,33 +181,33 @@ def fit(
def _objective_function(
self,
- engine_params: Dict[str, Any],
- parameters: List[Any],
- sample_models: 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(
engine_params,
parameters,
- sample_models,
+ structures,
experiments,
calculator,
)
def _create_objective_function(
self,
- parameters: List[Any],
- sample_models: 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,
parameters,
- sample_models,
+ structures,
experiments,
calculator,
)
diff --git a/src/easydiffraction/analysis/minimizers/dfols.py b/src/easydiffraction/analysis/minimizers/dfols.py
index 544d4b7a..fad9ad06 100644
--- a/src/easydiffraction/analysis/minimizers/dfols.py
+++ b/src/easydiffraction/analysis/minimizers/dfols.py
@@ -1,7 +1,6 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
-from typing import Any
from typing import Dict
from typing import List
@@ -9,26 +8,32 @@
from dfols import solve
from easydiffraction.analysis.minimizers.base import MinimizerBase
+from easydiffraction.analysis.minimizers.factory import MinimizerFactory
+from easydiffraction.core.metadata import TypeInfo
DEFAULT_MAX_ITERATIONS = 1000
+@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',
+ description='DFO-LS derivative-free least-squares optimization',
+ )
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 = []
@@ -39,38 +44,49 @@ 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.
+ """
+ Synchronize the solver result back to the parameters.
- Args:
- parameters: List of parameters being optimized.
- raw_result: The result object returned by the solver.
+ Parameters
+ ----------
+ parameters : List[object]
+ List of parameters being optimized.
+ raw_result : object
+ 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
for i, param in enumerate(parameters):
- param.value = result_values[i]
+ # Bypass validation but set the dirty flag so
+ # _update_categories() knows work is needed.
+ param._set_value_from_minimizer(result_values[i])
# DFO-LS doesn't provide uncertainties; set to None or
# calculate later if needed
param.uncertainty = None
- def _check_success(self, raw_result: Any) -> bool:
- """Determines success from DFO-LS result dictionary.
+ def _check_success(self, raw_result: object) -> bool:
+ """
+ Determine success from DFO-LS result dictionary.
- Args:
- raw_result: The result object returned by the solver.
+ Parameters
+ ----------
+ raw_result : object
+ The result object returned by the solver.
- Returns:
+ 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/factory.py b/src/easydiffraction/analysis/minimizers/factory.py
index 0b1afaa2..18f67cc6 100644
--- a/src/easydiffraction/analysis/minimizers/factory.py
+++ b/src/easydiffraction/analysis/minimizers/factory.py
@@ -1,126 +1,15 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2025 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
+"""Minimizer factory — delegates to ``FactoryBase``."""
-from typing import Any
-from typing import Dict
-from typing import List
-from typing import Optional
-from typing import Type
+from __future__ import annotations
-from easydiffraction.analysis.minimizers.base import MinimizerBase
-from easydiffraction.analysis.minimizers.dfols import DfolsMinimizer
-from easydiffraction.analysis.minimizers.lmfit import LmfitMinimizer
-from easydiffraction.utils.logging import console
-from easydiffraction.utils.utils import render_table
+from easydiffraction.core.factory import FactoryBase
-class MinimizerFactory:
- _available_minimizers: Dict[str, Dict[str, Any]] = {
- 'lmfit': {
- 'engine': 'lmfit',
- 'method': 'leastsq',
- 'description': 'LMFIT library using the default Levenberg-Marquardt '
- 'least squares method',
- 'class': LmfitMinimizer,
- },
- 'lmfit (leastsq)': {
- 'engine': 'lmfit',
- 'method': 'leastsq',
- 'description': 'LMFIT library with Levenberg-Marquardt least squares method',
- 'class': LmfitMinimizer,
- },
- 'lmfit (least_squares)': {
- 'engine': 'lmfit',
- 'method': 'least_squares',
- 'description': 'LMFIT library with SciPy’s trust region reflective algorithm',
- 'class': LmfitMinimizer,
- },
- 'dfols': {
- 'engine': 'dfols',
- 'method': None,
- 'description': 'DFO-LS library for derivative-free least-squares optimization',
- 'class': DfolsMinimizer,
- },
- }
-
- @classmethod
- def list_available_minimizers(cls) -> List[str]:
- """List all available minimizers.
-
- Returns:
- A list of minimizer names.
- """
- return list(cls._available_minimizers.keys())
-
- @classmethod
- def show_available_minimizers(cls) -> None:
- # TODO: Rename this method to `show_supported_minimizers` for
- # consistency with other methods in the library. E.g.
- # `show_supported_calculators`, etc.
- """Display a table of available minimizers and their
- descriptions.
- """
- columns_headers: List[str] = ['Minimizer', 'Description']
- columns_alignment = ['left', 'left']
- columns_data: List[List[str]] = []
- for name, config in cls._available_minimizers.items():
- description: str = config.get('description', 'No description provided.')
- columns_data.append([name, description])
-
- console.paragraph('Supported minimizers')
- render_table(
- columns_headers=columns_headers,
- columns_alignment=columns_alignment,
- columns_data=columns_data,
- )
-
- @classmethod
- def create_minimizer(cls, selection: str) -> MinimizerBase:
- """Create a minimizer instance based on the selection.
-
- Args:
- selection: The name of the minimizer to create.
+class MinimizerFactory(FactoryBase):
+ """Factory for creating minimizer instances."""
- Returns:
- An instance of the selected minimizer.
-
- Raises:
- ValueError: If the selection is not a valid minimizer.
- """
- config = cls._available_minimizers.get(selection)
- if not config:
- raise ValueError(
- f"Unknown minimizer '{selection}'. Use one of {cls.list_available_minimizers()}"
- )
-
- minimizer_class: Type[MinimizerBase] = config.get('class')
- method: Optional[str] = config.get('method')
-
- kwargs: Dict[str, Any] = {}
- if method is not None:
- kwargs['method'] = method
-
- return minimizer_class(**kwargs)
-
- @classmethod
- def register_minimizer(
- cls,
- name: str,
- minimizer_cls: Type[MinimizerBase],
- method: Optional[str] = None,
- description: str = 'No description provided.',
- ) -> None:
- """Register a new minimizer.
-
- Args:
- name: The name of the minimizer.
- minimizer_cls: The class of the minimizer.
- method: The method used by the minimizer (optional).
- description: A description of the minimizer.
- """
- cls._available_minimizers[name] = {
- 'engine': name,
- 'method': method,
- 'description': description,
- 'class': minimizer_cls,
- }
+ _default_rules = {
+ frozenset(): 'lmfit',
+ }
diff --git a/src/easydiffraction/analysis/minimizers/lmfit.py b/src/easydiffraction/analysis/minimizers/lmfit.py
index d7a651cb..771bacad 100644
--- a/src/easydiffraction/analysis/minimizers/lmfit.py
+++ b/src/easydiffraction/analysis/minimizers/lmfit.py
@@ -1,21 +1,28 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
-from typing import Any
from typing import Dict
from typing import List
import lmfit
from easydiffraction.analysis.minimizers.base import MinimizerBase
+from easydiffraction.analysis.minimizers.factory import MinimizerFactory
+from easydiffraction.core.metadata import TypeInfo
DEFAULT_METHOD = 'leastsq'
DEFAULT_MAX_ITERATIONS = 1000
+@MinimizerFactory.register
class LmfitMinimizer(MinimizerBase):
"""Minimizer using the lmfit package."""
+ type_info = TypeInfo(
+ tag='lmfit',
+ description='LMFIT with Levenberg-Marquardt least squares',
+ )
+
def __init__(
self,
name: str = 'lmfit',
@@ -30,16 +37,21 @@ def __init__(
def _prepare_solver_args(
self,
- parameters: List[Any],
- ) -> Dict[str, Any]:
- """Prepares the solver arguments for the lmfit minimizer.
+ parameters: List[object],
+ ) -> Dict[str, object]:
+ """
+ Prepare the solver arguments for the lmfit minimizer.
- Args:
- parameters: List of parameters to be optimized.
+ Parameters
+ ----------
+ parameters : List[object]
+ List of parameters to be optimized.
- Returns:
+ Returns
+ -------
+ Dict[str, object]
A dictionary containing the prepared lmfit. Parameters
- object.
+ object.
"""
engine_parameters = lmfit.Parameters()
for param in parameters:
@@ -52,14 +64,20 @@ def _prepare_solver_args(
)
return {'engine_parameters': engine_parameters}
- 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.
-
- Returns:
+ def _run_solver(self, objective_function: object, **kwargs: object) -> object:
+ """
+ Run the lmfit solver.
+
+ Parameters
+ ----------
+ 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')
@@ -74,30 +92,41 @@ 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.
-
- Args:
- parameters: List of parameters being optimized.
- raw_result: The result object returned by the solver.
+ """
+ Synchronize the result from the solver to the parameters.
+
+ Parameters
+ ----------
+ parameters : List[object]
+ List of parameters being optimized.
+ raw_result : object
+ The result object returned by the solver.
"""
param_values = raw_result.params if hasattr(raw_result, 'params') else raw_result
for param in parameters:
param_result = param_values.get(param._minimizer_uid)
if param_result is not None:
- param._value = param_result.value # Bypass ranges check
+ # Bypass validation but set the dirty flag so
+ # _update_categories() knows work is needed.
+ param._set_value_from_minimizer(param_result.value)
param.uncertainty = getattr(param_result, 'stderr', None)
- def _check_success(self, raw_result: Any) -> bool:
- """Determines success from lmfit MinimizerResult.
+ def _check_success(self, raw_result: object) -> bool:
+ """
+ Determine success from lmfit MinimizerResult.
- Args:
- raw_result: The result object returned by the solver.
+ Parameters
+ ----------
+ raw_result : object
+ The result object returned by the solver.
- Returns:
+ Returns
+ -------
+ bool
True if the optimization was successful, False otherwise.
"""
return getattr(raw_result, 'success', False)
@@ -106,18 +135,25 @@ 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.
-
- Args:
- params: The current parameters.
- iter: The current iteration number.
- resid: The residuals.
- *args: Additional positional arguments.
- **kwargs: Additional keyword arguments.
+ """
+ Handle each iteration callback of the minimizer.
+
+ Parameters
+ ----------
+ params : lmfit.Parameters
+ The current parameters.
+ iter : int
+ The current iteration number.
+ resid : object
+ The residuals.
+ *args : object
+ Additional positional arguments.
+ **kwargs : object
+ Additional keyword arguments.
"""
# Intentionally unused, required by callback signature
del params, resid, args, kwargs
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 6be057da..25e81894 100644
--- a/src/easydiffraction/core/category.py
+++ b/src/easydiffraction/core/category.py
@@ -1,17 +1,19 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2025 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
from easydiffraction.core.collection import CollectionBase
from easydiffraction.core.guard import GuardedBase
-from easydiffraction.core.parameters import GenericDescriptorBase
-from easydiffraction.core.validation import checktype
+from easydiffraction.core.variable import GenericDescriptorBase
+from easydiffraction.core.variable import GenericStringDescriptor
from easydiffraction.io.cif.serialize import category_collection_from_cif
from easydiffraction.io.cif.serialize import category_collection_to_cif
from easydiffraction.io.cif.serialize import category_item_from_cif
from easydiffraction.io.cif.serialize import category_item_to_cif
+# ======================================================================
+
class CategoryItem(GuardedBase):
"""Base class for items in a category collection."""
@@ -28,12 +30,13 @@ 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:
+ """Fully qualified name: datablock, category, entry."""
parts = [
self._identity.datablock_entry_name,
self._identity.category_code,
@@ -44,7 +47,8 @@ def unique_name(self):
return '.'.join(str_parts)
@property
- def parameters(self):
+ def parameters(self) -> list:
+ """All GenericDescriptorBase instances on this item."""
return [v for v in vars(self).values() if isinstance(v, GenericDescriptorBase)]
@property
@@ -52,13 +56,114 @@ 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)
+ def help(self) -> None:
+ """Print parameters, other properties, and methods."""
+ from easydiffraction.utils.logging import console
+ from easydiffraction.utils.utils import render_table
+
+ cls = type(self)
+ console.paragraph(f"Help for '{cls.__name__}'")
+
+ # Deduplicate properties
+ seen: dict = {}
+ for key, prop in cls._iter_properties():
+ if key not in seen:
+ seen[key] = prop
+
+ # Split into descriptor-backed and other
+ param_rows = []
+ other_rows = []
+ p_idx = 0
+ o_idx = 0
+ for key in sorted(seen):
+ prop = seen[key]
+ try:
+ val = getattr(self, key)
+ except Exception:
+ val = None
+ if isinstance(val, GenericDescriptorBase):
+ p_idx += 1
+ type_str = 'string' if isinstance(val, GenericStringDescriptor) else 'numeric'
+ writable = '✓' if prop.fset else '✗'
+ param_rows.append([
+ str(p_idx),
+ key,
+ type_str,
+ str(val.value),
+ writable,
+ val.description or '',
+ ])
+ else:
+ o_idx += 1
+ writable = '✓' if prop.fset else '✗'
+ doc = self._first_sentence(prop.fget.__doc__ if prop.fget else None)
+ other_rows.append([str(o_idx), key, writable, doc])
+
+ if param_rows:
+ console.paragraph('Parameters')
+ render_table(
+ columns_headers=[
+ '#',
+ 'Name',
+ 'Type',
+ 'Value',
+ 'Writable',
+ 'Description',
+ ],
+ columns_alignment=[
+ 'right',
+ 'left',
+ 'left',
+ 'right',
+ 'center',
+ 'left',
+ ],
+ columns_data=param_rows,
+ )
+
+ if other_rows:
+ console.paragraph('Other properties')
+ render_table(
+ columns_headers=[
+ '#',
+ 'Name',
+ 'Writable',
+ 'Description',
+ ],
+ columns_alignment=[
+ 'right',
+ 'left',
+ 'center',
+ 'left',
+ ],
+ columns_data=other_rows,
+ )
+
+ methods = dict(cls._iter_methods())
+ method_rows = []
+ for i, key in enumerate(sorted(methods), 1):
+ doc = self._first_sentence(getattr(methods[key], '__doc__', None))
+ method_rows.append([str(i), f'{key}()', doc])
+
+ if method_rows:
+ console.paragraph('Methods')
+ render_table(
+ columns_headers=['#', 'Name', 'Description'],
+ columns_alignment=['right', 'left', 'left'],
+ columns_data=method_rows,
+ )
+
+
+# ======================================================================
+
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).
"""
@@ -66,6 +171,22 @@ class CategoryCollection(CollectionBase):
# TODO: Common for all categories
_update_priority = 10 # Default. Lower values run first.
+ def _key_for(self, item: object) -> str | None:
+ """Return the category-level identity key for *item*."""
+ return item._identity.category_entry_name
+
+ 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.
+ """
+ parent = getattr(self, '_parent', None)
+ if parent is not None and hasattr(parent, '_need_categories_update'):
+ parent._need_categories_update = True
+
def __str__(self) -> str:
"""Human-readable representation of this component."""
name = self._log_name
@@ -73,16 +194,17 @@ 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; collections have no unique name."""
return None
@property
- def parameters(self):
+ def parameters(self) -> list:
"""All parameters from all items in this collection."""
params = []
for item in self._items:
@@ -94,22 +216,37 @@ 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)
- @checktype
- def _add(self, item) -> None:
- """Add an item to the collection."""
+ def add(self, item: object) -> None:
+ """
+ Insert or replace a pre-built item into the collection.
+
+ Parameters
+ ----------
+ item : object
+ A ``CategoryItem`` instance to add.
+ """
self[item._identity.category_entry_name] = item
+ self._mark_parent_dirty()
+
+ 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``.
- # TODO: Disallow args and only allow kwargs?
- # TODO: Check kwargs as for, e.g.,
- # ExperimentFactory.create(**kwargs)?
- @checktype
- def add(self, *args, **kwargs) -> None:
- """Create and add a new child instance from the provided
- arguments.
+ Parameters
+ ----------
+ **kwargs : object
+ Attribute names and values for the new item.
"""
- child_obj = self._item_type(*args, **kwargs)
- self._add(child_obj)
+ child_obj = self._item_type()
+
+ for attr, val in kwargs.items():
+ setattr(child_obj, attr, val)
+
+ self.add(child_obj)
diff --git a/src/easydiffraction/core/collection.py b/src/easydiffraction/core/collection.py
index a84bdb51..8f590b07 100644
--- a/src/easydiffraction/core/collection.py
+++ b/src/easydiffraction/core/collection.py
@@ -1,48 +1,72 @@
-# 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.
+"""
+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
+from typing import Generator
+from typing import Iterator
+
from easydiffraction.core.guard import GuardedBase
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
+ Type of items accepted by the collection. Used for validation
+ 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):
- """Return an item by its identity key.
-
- Rebuilds the internal index on a cache miss to stay consistent
- with recent mutations.
+ def __getitem__(self, key: str | int) -> GuardedBase:
"""
- try:
- return self._index[name]
- except KeyError:
- self._rebuild_index()
- return self._index[name]
+ Return an item by name or positional index.
+
+ Parameters
+ ----------
+ key : str | int
+ Identity key (str) or zero-based positional index (int).
+
+ Returns
+ -------
+ GuardedBase
+ The item matching the given key or index.
+
+ Raises
+ ------
+ TypeError
+ If *key* is neither ``str`` nor ``int``.
+ """
+ if isinstance(key, int):
+ return self._items[key]
+ if isinstance(key, str):
+ try:
+ return self._index[key]
+ except KeyError:
+ self._rebuild_index()
+ return self._index[key]
+ raise TypeError(f'Collection indices must be str or int, not {type(key).__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 identity exists; if so, replace it
+ # Check if item with same key exists; if so, replace it
for i, existing_item in enumerate(self._items):
- if existing_item._identity.category_entry_name == name:
+ if self._key_for(existing_item) == name:
self._items[i] = item
self._rebuild_index()
return
@@ -53,16 +77,20 @@ def __setitem__(self, name: str, item) -> None:
def __delitem__(self, name: str) -> None:
"""Delete an item by key or raise ``KeyError`` if missing."""
- # Remove from _items by identity entry name
for i, item in enumerate(self._items):
- if item._identity.category_entry_name == name:
+ if self._key_for(item) == name:
object.__setattr__(item, '_parent', None) # Unlink the parent before removal
del self._items[i]
self._rebuild_index()
return
raise KeyError(name)
- def __iter__(self):
+ def __contains__(self, name: str) -> bool:
+ """Check whether an item with the given key exists."""
+ self._rebuild_index()
+ return name in self._index
+
+ def __iter__(self) -> Iterator[GuardedBase]:
"""Iterate over items in insertion order."""
return iter(self._items)
@@ -70,9 +98,31 @@ def __len__(self) -> int:
"""Return the number of items in the collection."""
return len(self._items)
- def _key_for(self, item):
- """Return the identity key for ``item`` (category or
- datablock).
+ def remove(self, name: str) -> None:
+ """
+ Remove an item by its key.
+
+ Parameters
+ ----------
+ name : str
+ Identity key of the item to remove.
+
+ Raises
+ ------
+ KeyError
+ If no item with the given key exists.
+ """
+ try:
+ del self[name]
+ except KeyError:
+ raise
+
+ def _key_for(self, item: GuardedBase) -> str | None:
+ """
+ Return the identity key for *item*.
+
+ Subclasses must override to return the appropriate key
+ (``category_entry_name`` or ``datablock_entry_name``).
"""
return item._identity.category_entry_name or item._identity.datablock_entry_name
@@ -84,19 +134,41 @@ 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())
+
+ def help(self) -> None:
+ """Print a summary of public attributes and contained items."""
+ super().help()
+
+ from easydiffraction.utils.logging import console
+ from easydiffraction.utils.utils import render_table
+
+ if self._items:
+ console.paragraph(f'Items ({len(self._items)})')
+ rows = []
+ for i, item in enumerate(self._items, 1):
+ key = self._key_for(item)
+ rows.append([str(i), str(key), f"['{key}']"])
+ render_table(
+ columns_headers=['#', 'Name', 'Access'],
+ columns_alignment=['right', 'left', 'left'],
+ columns_data=rows,
+ )
+ else:
+ console.paragraph('Items')
+ console.print('(empty)')
diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py
index a8cc0ed8..6a1ef289 100644
--- a/src/easydiffraction/core/datablock.py
+++ b/src/easydiffraction/core/datablock.py
@@ -1,37 +1,43 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2025 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
-from typeguard import typechecked
-
from easydiffraction.core.category import CategoryCollection
from easydiffraction.core.category import CategoryItem
from easydiffraction.core.collection import CollectionBase
from easydiffraction.core.guard import GuardedBase
-from easydiffraction.core.parameters import Parameter
+from easydiffraction.core.variable import Parameter
class DatablockItem(GuardedBase):
"""Base class for items in a datablock collection."""
- def __init__(self):
+ def __init__(self) -> None:
super().__init__()
- self._need_categories_update = False
+ self._need_categories_update = True
def __str__(self) -> str:
"""Human-readable representation of this component."""
- name = self._log_name
- items = getattr(self, '_items', None)
- return f'<{name} ({items})>'
+ name = self.unique_name
+ cls = type(self).__name__
+ categories = '\n'.join(f' - {c}' for c in self.categories)
+ return f"{cls} datablock '{name}':\n{categories}"
+
+ def __repr__(self) -> str:
+ """Developer-oriented representation of this component."""
+ name = self.unique_name
+ cls = type(self).__name__
+ num_categories = len(self.categories)
+ return f'<{cls} datablock "{name}" ({num_categories} categories)>'
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
- # case of sample models. In the case of experiments, it should
+ # case of structures. In the case of experiments, it should
# run calculations to update the "data" categories.
# Any parameter change should set _need_categories_update to
# True.
@@ -40,9 +46,15 @@ def _update_categories(
# Should this be also called when parameters are accessed? E.g.
# if one change background coefficients, then access the
# background points in the data category?
- # return
- # if not self._need_categories_update:
- # return
+ #
+ # Dirty-flag guard: skip if no parameter has changed since the
+ # last update. Minimisers use _set_value_from_minimizer()
+ # which bypasses validation but still sets this flag.
+ # During fitting the guard is bypassed because experiment
+ # calculations depend on structure parameters owned by a
+ # different DatablockItem whose flag changes are invisible here.
+ if not called_by_minimizer and not self._need_categories_update:
+ return
for category in self.categories:
category._update(called_by_minimizer=called_by_minimizer)
@@ -50,11 +62,13 @@ def _update_categories(
self._need_categories_update = False
@property
- def unique_name(self):
+ def unique_name(self) -> str | None:
+ """Unique name of this datablock item (from identity)."""
return self._identity.datablock_entry_name
@property
- def categories(self):
+ def categories(self) -> list:
+ """All category objects in this datablock by priority."""
cats = [
v for v in vars(self).values() if isinstance(v, (CategoryItem, CategoryCollection))
]
@@ -62,10 +76,8 @@ def categories(self):
return sorted(cats, key=lambda c: type(c)._update_priority)
@property
- def parameters(self):
- """All parameters from all categories contained in this
- datablock.
- """
+ def parameters(self) -> list:
+ """All parameters from all categories in this datablock."""
params = []
for v in self.categories:
params.extend(v.parameters)
@@ -79,14 +91,59 @@ def as_cif(self) -> str:
self._update_categories()
return datablock_item_to_cif(self)
+ def help(self) -> None:
+ """Print a summary of public attributes and categories."""
+ super().help()
+
+ from easydiffraction.utils.logging import console
+ from easydiffraction.utils.utils import render_table
+
+ cats = self.categories
+ if cats:
+ console.paragraph('Categories')
+ rows = []
+ for c in cats:
+ code = c._identity.category_code or type(c).__name__
+ type_name = type(c).__name__
+ num_params = len(c.parameters)
+ rows.append([code, type_name, str(num_params)])
+ render_table(
+ columns_headers=['Category', 'Type', '# Parameters'],
+ columns_alignment=['left', 'left', 'right'],
+ columns_data=rows,
+ )
+
+
+# ======================================================================
+
class DatablockCollection(CollectionBase):
- """Handles top-level category collections (e.g. SampleModels,
- Experiments).
+ """
+ Collection of top-level datablocks (e.g. Structures, Experiments).
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.
"""
+ 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: object) -> None:
+ """
+ Add a pre-built item to the collection.
+
+ Parameters
+ ----------
+ item : object
+ A ``DatablockItem`` instance (e.g. a ``Structure`` or
+ ``ExperimentBase`` subclass).
+ """
+ self[item._identity.datablock_entry_name] = item
+
def __str__(self) -> str:
"""Human-readable representation of this component."""
name = self._log_name
@@ -94,25 +151,26 @@ def __str__(self) -> str:
return f'<{name} collection ({size} items)>'
@property
- def unique_name(self):
+ def unique_name(self) -> str | None:
+ """Return None; collections have no unique name."""
return None
@property
- def parameters(self):
+ def parameters(self) -> list:
"""All parameters from all datablocks in this collection."""
params = []
for db in self._items:
params.extend(db.parameters)
return params
- # was in class AbstractDatablock(ABC):
@property
def fittable_parameters(self) -> list:
+ """All non-constrained Parameters 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
@@ -121,8 +179,3 @@ def as_cif(self) -> str:
from easydiffraction.io.cif.serialize import datablock_collection_to_cif
return datablock_collection_to_cif(self)
-
- @typechecked
- def _add(self, item) -> None:
- """Add an item to the collection."""
- self[item._identity.datablock_entry_name] = item
diff --git a/src/easydiffraction/core/diagnostic.py b/src/easydiffraction/core/diagnostic.py
index 74c2ab3b..dc5ea4c4 100644
--- a/src/easydiffraction/core/diagnostic.py
+++ b/src/easydiffraction/core/diagnostic.py
@@ -1,6 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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.
@@ -19,8 +20,9 @@ class Diagnostics:
# ==============================================================
@staticmethod
- def type_override_error(cls_name: str, expected, got):
- """Report an invalid DataTypes override.
+ 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 +43,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,11 +55,9 @@ def attr_error(
name: str,
key: str,
allowed: set[str],
- label='Allowed',
- ):
- """Log access to an unknown attribute and suggest closest
- key.
- """
+ label: str = 'Allowed',
+ ) -> None:
+ """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)
@@ -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 3500768f..af99217a 100644
--- a/src/easydiffraction/core/factory.py
+++ b/src/easydiffraction/core/factory.py
@@ -1,36 +1,271 @@
-# 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.
-from typing import Iterable
-from typing import Mapping
+Concrete factories inherit from ``FactoryBase`` and only need to define
+``_default_rules``.
+"""
+
+from __future__ import annotations
+
+from typing import Any
+from typing import Dict
+from typing import FrozenSet
+from typing import List
+from typing import Tuple
+from typing import Type
+
+from easydiffraction.utils.logging import console
+from easydiffraction.utils.utils import render_table
class FactoryBase:
- """Reusable argument validation mixin."""
+ """
+ Shared base for all factories.
+
+ Subclasses must set:
+
+ * ``_default_rules`` -- mapping of ``frozenset`` conditions to tag
+ strings. Use ``frozenset(): 'tag'`` for a universal default.
+
+ The ``__init_subclass__`` hook ensures every subclass gets its own
+ independent ``_registry`` list.
+ """
+
+ _registry: List[Type] = []
+ _default_rules: Dict[FrozenSet[Tuple[str, Any]], str] = {}
+
+ def __init_subclass__(cls, **kwargs: object) -> None:
+ """Give each subclass its own independent registry and rules."""
+ super().__init_subclass__(**kwargs)
+ cls._registry = []
+ if '_default_rules' not in cls.__dict__:
+ cls._default_rules = {}
+
+ # ------------------------------------------------------------------
+ # Registration
+ # ------------------------------------------------------------------
+
+ @classmethod
+ def register(cls, klass: type) -> type:
+ """
+ Class decorator to register a concrete class.
+
+ Usage::
+
+ @SomeFactory.register class MyClass(SomeBase): type_info =
+ TypeInfo(...)
+
+ Returns the class unmodified.
+ """
+ cls._registry.append(klass)
+ return klass
+
+ # ------------------------------------------------------------------
+ # Supported-map helpers
+ # ------------------------------------------------------------------
+
+ @classmethod
+ def _supported_map(cls) -> Dict[str, Type]:
+ """Build ``{tag: class}`` from all registered classes."""
+ return {klass.type_info.tag: klass for klass in cls._registry}
+
+ @classmethod
+ def supported_tags(cls) -> List[str]:
+ """Return list of all supported tags."""
+ return list(cls._supported_map().keys())
+
+ # ------------------------------------------------------------------
+ # Default resolution
+ # ------------------------------------------------------------------
+
+ @classmethod
+ 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.
+
+ Parameters
+ ----------
+ **conditions : object
+ Experimental-axis values, e.g.
+ ``scattering_type=ScatteringTypeEnum.BRAGG``.
- @staticmethod
- def _validate_args(
- present: set[str],
- allowed_specs: Iterable[Mapping[str, Iterable[str]]],
- factory_name: str,
+ Returns
+ -------
+ str
+ The resolved default tag string.
+
+ Raises
+ ------
+ ValueError
+ If no rule matches the given conditions.
+ """
+ condition_set = frozenset(conditions.items())
+ best_match_tag: str | None = None
+ best_match_size = -1
+
+ for rule_key, rule_tag in cls._default_rules.items():
+ if rule_key <= condition_set and len(rule_key) > best_match_size:
+ best_match_tag = rule_tag
+ best_match_size = len(rule_key)
+
+ if best_match_tag is None:
+ raise ValueError(
+ f'No default rule matches conditions {dict(conditions)}. '
+ f'Available rules: {cls._default_rules}'
+ )
+ return best_match_tag
+
+ # ------------------------------------------------------------------
+ # Creation
+ # ------------------------------------------------------------------
+
+ @classmethod
+ def create(cls, tag: str, **kwargs: object) -> object:
+ """
+ Instantiate a registered class by *tag*.
+
+ Parameters
+ ----------
+ tag : str
+ ``type_info.tag`` value.
+ **kwargs : object
+ Forwarded to the class constructor.
+
+ Returns
+ -------
+ object
+ A new instance of the registered class.
+
+ Raises
+ ------
+ ValueError
+ If *tag* is not in the registry.
+ """
+ supported = cls._supported_map()
+ if tag not in supported:
+ raise ValueError(f"Unsupported type: '{tag}'. Supported: {list(supported.keys())}")
+ return supported[tag](**kwargs)
+
+ @classmethod
+ def create_default_for(cls, **conditions: object) -> object:
+ """
+ Instantiate the default class for a given context.
+
+ Combines ``default_tag(**conditions)`` with ``create(tag)``.
+
+ Parameters
+ ----------
+ **conditions : object
+ Experimental-axis values.
+
+ Returns
+ -------
+ object
+ A new instance of the default class.
+ """
+ tag = cls.default_tag(**conditions)
+ return cls.create(tag)
+
+ # ------------------------------------------------------------------
+ # Querying
+ # ------------------------------------------------------------------
+
+ @classmethod
+ def supported_for(
+ cls,
+ *,
+ 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.
+
+ Parameters
+ ----------
+ calculator : object, default=None
+ Optional ``CalculatorEnum`` value.
+ sample_form : object, default=None
+ Optional ``SampleFormEnum`` value.
+ scattering_type : object, default=None
+ Optional ``ScatteringTypeEnum`` value.
+ beam_mode : object, default=None
+ Optional ``BeamModeEnum`` value.
+ radiation_probe : object, default=None
+ Optional ``RadiationProbeEnum`` value.
+
+ Returns
+ -------
+ List[Type]
+ Classes matching the given conditions.
+ """
+ result = []
+ for klass in cls._supported_map().values():
+ compat = getattr(klass, 'compatibility', None)
+ if compat and not compat.supports(
+ sample_form=sample_form,
+ scattering_type=scattering_type,
+ beam_mode=beam_mode,
+ radiation_probe=radiation_probe,
+ ):
+ continue
+ calc_support = getattr(klass, 'calculator_support', None)
+ if calculator and calc_support and not calc_support.supports(calculator):
+ continue
+ result.append(klass)
+ return result
+
+ # ------------------------------------------------------------------
+ # Display
+ # ------------------------------------------------------------------
+
+ @classmethod
+ def show_supported(
+ cls,
+ *,
+ calculator: object = None,
+ sample_form: object = None,
+ scattering_type: object = None,
+ beam_mode: object = None,
+ radiation_probe: object = None,
) -> None:
- """Validate provided arguments against allowed combinations."""
- for spec in allowed_specs:
- required = set(spec.get('required', []))
- optional = set(spec.get('optional', []))
- if required.issubset(present) and present <= (required | optional):
- return # valid combo
- # build readable error message
- combos = []
- for spec in allowed_specs:
- req = ', '.join(spec.get('required', []))
- opt = ', '.join(spec.get('optional', []))
- if opt:
- combos.append(f'({req}[, {opt}])')
- else:
- combos.append(f'({req})')
- raise ValueError(
- f'Invalid argument combination for {factory_name} creation.\n'
- f'Provided: {sorted(present)}\n'
- f'Allowed combinations:\n ' + '\n '.join(combos)
+ """
+ Pretty-print a table of supported types.
+
+ Parameters
+ ----------
+ calculator : object, default=None
+ Optional ``CalculatorEnum`` filter.
+ sample_form : object, default=None
+ Optional ``SampleFormEnum`` filter.
+ scattering_type : object, default=None
+ Optional ``ScatteringTypeEnum`` filter.
+ beam_mode : object, default=None
+ Optional ``BeamModeEnum`` filter.
+ radiation_probe : object, default=None
+ Optional ``RadiationProbeEnum`` filter.
+ """
+ matching = cls.supported_for(
+ calculator=calculator,
+ sample_form=sample_form,
+ scattering_type=scattering_type,
+ beam_mode=beam_mode,
+ radiation_probe=radiation_probe,
+ )
+ columns_headers = ['Type', 'Description']
+ columns_alignment = ['left', 'left']
+ columns_data = [[klass.type_info.tag, klass.type_info.description] for klass in matching]
+ console.paragraph('Supported types')
+ render_table(
+ columns_headers=columns_headers,
+ columns_alignment=columns_alignment,
+ columns_data=columns_data,
)
diff --git a/src/easydiffraction/core/guard.py b/src/easydiffraction/core/guard.py
index 0979f4cd..17b60fb5 100644
--- a/src/easydiffraction/core/guard.py
+++ b/src/easydiffraction/core/guard.py
@@ -1,30 +1,35 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2025 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
+from __future__ import annotations
+
from abc import ABC
from abc import abstractmethod
+from typing import Generator
from easydiffraction.core.diagnostic import Diagnostics
from easydiffraction.core.identity import Identity
class GuardedBase(ABC):
- """Base class enforcing controlled attribute access and parent
- linkage.
- """
+ """Base class enforcing controlled attribute access and linkage."""
_diagnoser = Diagnostics()
- def __init__(self):
+ def __init__(self) -> None:
+ super().__init__()
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):
+ 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:
@@ -35,7 +40,8 @@ def __getattr__(self, key: str):
label='Allowed readable/writable',
)
- def __setattr__(self, key: str, value):
+ 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)
@@ -67,20 +73,21 @@ 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):
- """Iterate over all public properties defined in the class
- hierarchy.
+ def _iter_properties(cls) -> Generator[tuple[str, property], None, None]:
+ """
+ Iterate over all public properties 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():
@@ -89,12 +96,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}
@@ -103,18 +110,19 @@ 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:
+ """Fallback unique name: the class name."""
return type(self).__name__
# @property
@@ -128,16 +136,92 @@ def unique_name(self):
@property
@abstractmethod
- def parameters(self):
- """Return a list of parameter objects (to be implemented by
- subclasses).
- """
+ def parameters(self) -> list:
+ """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
+ def _first_sentence(docstring: str | None) -> str:
+ """
+ Extract the first paragraph from a docstring.
+
+ Returns text before the first blank line, with continuation
+ lines joined into a single string.
+ """
+ if not docstring:
+ return ''
+ first_para = docstring.strip().split('\n\n')[0]
+ return ' '.join(line.strip() for line in first_para.splitlines())
+
+ @classmethod
+ def _iter_methods(cls) -> Generator[tuple[str, object], None, None]:
+ """
+ Iterate over public methods in the class hierarchy.
+
+ Yields
+ ------
+ tuple[str, object]
+ Each (name, function) pair.
+ """
+ seen: set = set()
+ for base in cls.mro():
+ for key, attr in base.__dict__.items():
+ if key.startswith('_') or key in seen:
+ continue
+ if isinstance(attr, property):
+ continue
+ raw = attr
+ if isinstance(raw, (staticmethod, classmethod)):
+ raw = raw.__func__
+ if callable(raw):
+ seen.add(key)
+ yield key, raw
+
+ def help(self) -> None:
+ """Print a summary of public properties and methods."""
+ from easydiffraction.utils.logging import console
+ from easydiffraction.utils.utils import render_table
+
+ cls = type(self)
+ console.paragraph(f"Help for '{cls.__name__}'")
+
+ # Deduplicate (MRO may yield the same name)
+ seen: dict = {}
+ for key, prop in cls._iter_properties():
+ if key not in seen:
+ seen[key] = prop
+
+ prop_rows = []
+ for i, key in enumerate(sorted(seen), 1):
+ prop = seen[key]
+ writable = '✓' if prop.fset else '✗'
+ doc = self._first_sentence(prop.fget.__doc__ if prop.fget else None)
+ prop_rows.append([str(i), key, writable, doc])
+
+ if prop_rows:
+ console.paragraph('Properties')
+ render_table(
+ columns_headers=['#', 'Name', 'Writable', 'Description'],
+ columns_alignment=['right', 'left', 'center', 'left'],
+ columns_data=prop_rows,
+ )
+
+ methods = dict(cls._iter_methods())
+ method_rows = []
+ for i, key in enumerate(sorted(methods), 1):
+ doc = self._first_sentence(getattr(methods[key], '__doc__', None))
+ method_rows.append([str(i), f'{key}()', doc])
+
+ if method_rows:
+ console.paragraph('Methods')
+ render_table(
+ columns_headers=['#', 'Name', 'Description'],
+ columns_alignment=['right', 'left', 'left'],
+ columns_data=method_rows,
+ )
diff --git a/src/easydiffraction/core/identity.py b/src/easydiffraction/core/identity.py
index 5848bf18..d64fce81 100644
--- a/src/easydiffraction/core/identity.py
+++ b/src/easydiffraction/core/identity.py
@@ -1,6 +1,7 @@
-# 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.
+"""
+Identity helpers to build CIF-like hierarchical names.
Used by containers and items to expose datablock/category/entry names
without tight coupling.
@@ -19,13 +20,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 +48,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
new file mode 100644
index 00000000..318d64bb
--- /dev/null
+++ b/src/easydiffraction/core/metadata.py
@@ -0,0 +1,119 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+Metadata dataclasses for factory-created classes.
+
+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.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import FrozenSet
+
+
+@dataclass(frozen=True)
+class TypeInfo:
+ """
+ Stable identity and description for a factory-created class.
+
+ Attributes
+ ----------
+ 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 : str, default=''
+ One-line human-readable explanation. Used in
+ ``show_supported()`` tables and documentation.
+ """
+
+ tag: str
+ description: str = ''
+
+
+@dataclass(frozen=True)
+class Compatibility:
+ """
+ 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
+ "compatible with any value of this axis" (i.e. no restriction).
+ """
+
+ sample_form: FrozenSet = frozenset()
+ scattering_type: FrozenSet = frozenset()
+ beam_mode: FrozenSet = frozenset()
+ radiation_probe: FrozenSet = frozenset()
+
+ def supports(
+ self,
+ 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.
+
+ Each argument is an optional enum member. Returns ``True`` if
+ every provided value is in the corresponding frozenset (or the
+ frozenset is empty, meaning *any*).
+
+ Example::
+
+ compat.supports( scattering_type=ScatteringTypeEnum.BRAGG,
+ beam_mode=BeamModeEnum.CONSTANT_WAVELENGTH, )
+ """
+ for axis, value in (
+ ('sample_form', sample_form),
+ ('scattering_type', scattering_type),
+ ('beam_mode', beam_mode),
+ ('radiation_probe', radiation_probe),
+ ):
+ if value is None:
+ continue
+ allowed = getattr(self, axis)
+ if allowed and value not in allowed:
+ return False
+ return True
+
+
+@dataclass(frozen=True)
+class CalculatorSupport:
+ """
+ Which calculation engines can handle this class.
+
+ Attributes
+ ----------
+ calculators : FrozenSet, default=frozenset()
+ Frozenset of ``CalculatorEnum`` values. Empty means "any
+ calculator" (no restriction).
+ """
+
+ calculators: FrozenSet = frozenset()
+
+ def supports(self, calculator: object) -> bool:
+ """
+ Check if a specific calculator can handle this class.
+
+ Parameters
+ ----------
+ 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).
+ """
+ if not self.calculators:
+ return True
+ return calculator in self.calculators
diff --git a/src/easydiffraction/core/singletons.py b/src/easydiffraction/core/singleton.py
similarity index 71%
rename from src/easydiffraction/core/singletons.py
rename to src/easydiffraction/core/singleton.py
index 2cd553c1..d40e62bc 100644
--- a/src/easydiffraction/core/singletons.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
@@ -12,9 +12,12 @@
T = TypeVar('T', bound='SingletonBase')
+# ======================================================================
+
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.
@@ -24,12 +27,15 @@ 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
+# ======================================================================
+
+
class UidMapHandler(SingletonBase):
"""Global handler to manage UID-to-Parameter object mapping."""
@@ -38,16 +44,17 @@ 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):
- """Adds a single Parameter or Descriptor object to the UID map.
+ def add_to_uid_map(self, parameter: object) -> None:
+ """
+ Add a single Parameter or Descriptor object to the UID map.
Only Descriptor or Parameter instances are allowed (not
Components or others).
"""
- from easydiffraction.core.parameters import GenericDescriptorBase
+ from easydiffraction.core.variable import GenericDescriptorBase
if not isinstance(parameter, GenericDescriptorBase):
raise TypeError(
@@ -56,8 +63,9 @@ def add_to_uid_map(self, parameter):
)
self._uid_map[parameter.uid] = parameter
- def replace_uid(self, old_uid, new_uid):
- """Replaces an existing UID key in the UID map with a new UID.
+ def replace_uid(self, old_uid: str, new_uid: str) -> None:
+ """
+ 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.
@@ -71,11 +79,14 @@ def replace_uid(self, old_uid, new_uid):
# TODO: Implement removing from the UID map
+# ======================================================================
+
+
# TODO: Implement changing atrr '.constrained' back to False
# 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 =
@@ -94,46 +105,43 @@ 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):
- """Sets the alias map (name → parameter wrapper).
+ def set_aliases(self, aliases: object) -> None:
+ """
+ Set 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())
- def set_constraints(self, constraints):
- """Sets the constraints and triggers parsing into internal
- format.
+ def set_constraints(self, constraints: object) -> None:
+ """
+ Set 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()
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:
- lhs_alias = expr_obj.lhs_alias.value
- rhs_expr = expr_obj.rhs_expr.value
+ lhs_alias = expr_obj.lhs_alias
+ rhs_expr = expr_obj.rhs_expr
if lhs_alias and rhs_expr:
constraint = (lhs_alias.strip(), rhs_expr.strip())
self._parsed_constraints.append(constraint)
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
+ 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:
@@ -164,8 +172,7 @@ def apply(self) -> None:
param = uid_map[dependent_uid]
# Update its value and mark it as constrained
- param._value = rhs_value # To bypass ranges check
- param._constrained = True # To bypass read-only check
+ param._set_value_constrained(rhs_value)
except Exception as error:
print(f"Failed to apply constraint '{lhs_alias} = {rhs_expr}': {error}")
diff --git a/src/easydiffraction/core/validation.py b/src/easydiffraction/core/validation.py
index 5e31c485..d3c411af 100644
--- a/src/easydiffraction/core/validation.py
+++ b/src/easydiffraction/core/validation.py
@@ -1,12 +1,12 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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.
"""
-import functools
import re
from abc import ABC
from abc import abstractmethod
@@ -14,73 +14,47 @@
from enum import auto
import numpy as np
-from typeguard import TypeCheckError
-from typeguard import typechecked
from easydiffraction.core.diagnostic import Diagnostics
-from easydiffraction.utils.logging import log
-# ==============================================================
+# ======================================================================
# Shared constants
-# ==============================================================
+# ======================================================================
+
+
+# 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
+
+
+# ======================================================================
class DataTypes(Enum):
- NUMERIC = (int, float, np.integer, np.floating, np.number)
+ """Enumeration of supported data types for descriptors."""
+
+ NUMERIC = (int, float, np.integer, np.floating)
STRING = (str,)
BOOL = (bool,)
ANY = (object,) # fallback for unconstrained
- def __str__(self):
+ def __str__(self) -> str:
+ """Return the lowercase name of the data type."""
return self.name.lower()
@property
- def expected_type(self):
+ def expected_type(self) -> tuple:
"""Convenience alias for tuple of allowed Python types."""
return self.value
-# ==============================================================
-# Runtime type checking decorator
-# ==============================================================
-
-# Runtime type checking decorator for validating those methods
-# annotated with type hints, which are writable for the user, and
-# which are not covered by custom validators for Parameter attribute
-# types and content, implemented below.
-
-
-def checktype(func=None, *, context=None):
- """Runtime type check decorator using typeguard.
-
- When a TypeCheckError occurs, the error is logged and None is
- returned. If context is provided, it is added to the message.
- """
-
- def decorator(f):
- checked_func = typechecked(f)
-
- @functools.wraps(f)
- def wrapper(*args, **kwargs):
- try:
- return checked_func(*args, **kwargs)
- except TypeCheckError as err:
- msg = str(err)
- if context:
- msg = f'{context}: {msg}'
- log.error(message=msg, exc_type=TypeError)
- return None
-
- return wrapper
-
- if func is None:
- return decorator
- return decorator(func)
-
-
-# ==============================================================
+# ======================================================================
# Validation stages (enum/constant)
-# ==============================================================
+# ======================================================================
class ValidationStage(Enum):
@@ -91,21 +65,29 @@ class ValidationStage(Enum):
MEMBERSHIP = auto()
REGEX = auto()
- def __str__(self):
+ def __str__(self) -> str:
+ """Return the lowercase name of the validation stage."""
return self.name.lower()
-# ==============================================================
+# ======================================================================
# Advanced runtime custom validators for Parameter types/content
-# ==============================================================
+# ======================================================================
class ValidatorBase(ABC):
"""Abstract base class for all validators."""
@abstractmethod
- def validated(self, value, name, default=None, current=None):
- """Return a validated value or fallback.
+ 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.
"""
@@ -113,17 +95,20 @@ 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
+# ======================================================================
+
+
class TypeValidator(ValidatorBase):
- """Ensure a value is of the expected Python type."""
+ """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)
@@ -132,13 +117,14 @@ def __init__(self, expected_type: DataTypes):
def validated(
self,
- value,
- name,
- default=None,
- current=None,
- allow_none=False,
- ):
- """Validate type and return value or fallback.
+ 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.
"""
@@ -171,24 +157,27 @@ def validated(
return value
+# ======================================================================
+
+
class RangeValidator(ValidatorBase):
"""Ensure a numeric value lies within [ge, le]."""
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(
@@ -209,23 +198,27 @@ def validated(
return value
+# ======================================================================
+
+
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):
+ 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
@@ -248,19 +241,22 @@ def validated(
return value
+# ======================================================================
+
+
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(
@@ -280,9 +276,9 @@ def validated(
return value
-# ==============================================================
+# ======================================================================
# Attribute specification holding metadata and validators
-# ==============================================================
+# ======================================================================
class AttributeSpec:
@@ -291,25 +287,24 @@ class AttributeSpec:
def __init__(
self,
*,
- value=None,
- type_=None,
- default=None,
- content_validator=None,
+ default: object = None,
+ data_type: DataTypes | None = None,
+ validator: ValidatorBase | None = None,
allow_none: bool = False,
- ):
- self.value = value
+ ) -> None:
self.default = default
self.allow_none = allow_none
- self._type_validator = TypeValidator(type_) if type_ else None
- self._content_validator = content_validator
+ self._data_type_validator = TypeValidator(data_type) if data_type else None
+ self._validator = validator
def validated(
self,
- value,
- name,
- current=None,
- ):
- """Validate through type and content validators.
+ value: object,
+ name: str,
+ current: object = None,
+ ) -> object:
+ """
+ Validate through type and content validators.
Returns validated value, possibly default or current if errors
occur. None may short-circuit further checks when allowed.
@@ -319,8 +314,8 @@ def validated(
default = self.default() if callable(self.default) else self.default
# Type validation
- if self._type_validator:
- val = self._type_validator.validated(
+ if self._data_type_validator:
+ val = self._data_type_validator.validated(
val,
name,
default=default,
@@ -334,8 +329,8 @@ def validated(
return None
# Content validation
- if self._content_validator and val is not None:
- val = self._content_validator.validated(
+ if self._validator and val is not None:
+ val = self._validator.validated(
val,
name,
default=default,
diff --git a/src/easydiffraction/core/parameters.py b/src/easydiffraction/core/variable.py
similarity index 59%
rename from src/easydiffraction/core/parameters.py
rename to src/easydiffraction/core/variable.py
index 37b982e0..8e06b2a9 100644
--- a/src/easydiffraction/core/parameters.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
@@ -6,13 +6,12 @@
import secrets
import string
from typing import TYPE_CHECKING
-from typing import Any
import numpy as np
from easydiffraction.core.diagnostic import Diagnostics
from easydiffraction.core.guard import GuardedBase
-from easydiffraction.core.singletons import UidMapHandler
+from easydiffraction.core.singleton import UidMapHandler
from easydiffraction.core.validation import AttributeSpec
from easydiffraction.core.validation import DataTypes
from easydiffraction.core.validation import RangeValidator
@@ -23,24 +22,22 @@
if TYPE_CHECKING:
from easydiffraction.io.cif.handler import CifHandler
+# ======================================================================
+
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 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.
+ the expected data type and can extend the public API with additional
+ behavior (e.g. units).
"""
_BOOL_SPEC_TEMPLATE = AttributeSpec(
- type_=DataTypes.BOOL,
+ data_type=DataTypes.BOOL,
default=False,
)
@@ -50,13 +47,18 @@ def __init__(
value_spec: AttributeSpec,
name: str,
description: str = None,
- ):
- """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.
+ ) -> None:
+ """
+ Initialize the descriptor with validation and identity.
+
+ Parameters
+ ----------
+ value_spec : AttributeSpec
+ Validation specification for the value.
+ name : str
+ Local name of the descriptor within its category.
+ description : str, default=None
+ Optional human-readable description.
"""
super().__init__()
@@ -64,8 +66,8 @@ def __init__(
if expected_type:
user_type = (
- value_spec._type_validator.expected_type
- if value_spec._type_validator is not None
+ value_spec._data_type_validator.expected_type
+ if value_spec._data_type_validator is not None
else None
)
if user_type and user_type is not expected_type:
@@ -76,19 +78,27 @@ def __init__(
)
else:
# Enforce descriptor's own type if not already defined
- value_spec._type_validator = TypeValidator(expected_type)
+ value_spec._data_type_validator = TypeValidator(expected_type)
self._value_spec = value_spec
self._name = name
self._description = description
# Initial validated states
- self._value = self._value_spec.validated(
- value_spec.value,
- name=self.unique_name,
- )
+ # self._value = self._value_spec.validated(
+ # value_spec.value,
+ # name=self.unique_name,
+ # )
+
+ # Assign default directly.
+ # Skip validation — defaults are trusted.
+ # Callable is needed for dynamic defaults like SpaceGroup
+ # it_coordinate_system_code, and similar cases.
+ default = value_spec.default
+ 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
@@ -97,11 +107,8 @@ def name(self) -> str:
return self._name
@property
- def unique_name(self):
- """Fully qualified name including datablock, category and entry
- name.
- """
- # 7c: Use filter(None, [...])
+ def unique_name(self) -> str:
+ """Fully qualified name: datablock, category and entry."""
parts = [
self._identity.datablock_entry_name,
self._identity.category_code,
@@ -110,10 +117,8 @@ def unique_name(self):
]
return '.'.join(filter(None, parts))
- def _parent_of_type(self, cls):
- """Walk up the parent chain and return the first parent of type
- `cls`.
- """
+ def _parent_of_type(self, cls: type) -> object | None:
+ """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:
@@ -123,19 +128,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:
@@ -154,14 +159,35 @@ def value(self, v):
if parent_datablock is not None:
parent_datablock._need_categories_update = True
+ 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
+ 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.
+ """
+ self._value = v
+ parent_datablock = self._datablock_item()
+ if parent_datablock is not 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):
- """Return a flat list of parameters contained by this object.
+ 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
itself. Composite objects override this to flatten nested
@@ -174,34 +200,45 @@ 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)
+# ======================================================================
+
+
class GenericStringDescriptor(GenericDescriptorBase):
+ """Base descriptor that constrains values to strings."""
+
_value_type = DataTypes.STRING
def __init__(
self,
- **kwargs: Any,
+ **kwargs: object,
) -> None:
super().__init__(**kwargs)
+# ======================================================================
+
+
class GenericNumericDescriptor(GenericDescriptorBase):
+ """Base descriptor that constrains values to numbers."""
+
_value_type = DataTypes.NUMERIC
def __init__(
self,
*,
units: str = '',
- **kwargs: Any,
+ **kwargs: object,
) -> None:
super().__init__(**kwargs)
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:
@@ -214,8 +251,12 @@ def units(self) -> str:
return self._units
+# ======================================================================
+
+
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
@@ -224,24 +265,24 @@ class GenericParameter(GenericNumericDescriptor):
def __init__(
self,
- **kwargs: Any,
- ):
+ **kwargs: object,
+ ) -> None:
super().__init__(**kwargs)
# Initial validated states
self._free_spec = self._BOOL_SPEC_TEMPLATE
self._free = self._free_spec.default
self._uncertainty_spec = AttributeSpec(
- type_=DataTypes.NUMERIC,
- content_validator=RangeValidator(ge=0),
+ data_type=DataTypes.NUMERIC,
+ validator=RangeValidator(ge=0),
allow_none=True,
)
self._uncertainty = self._uncertainty_spec.default
- self._fit_min_spec = AttributeSpec(type_=DataTypes.NUMERIC, default=-np.inf)
+ self._fit_min_spec = AttributeSpec(data_type=DataTypes.NUMERIC, default=-np.inf)
self._fit_min = self._fit_min_spec.default
- self._fit_max_spec = AttributeSpec(type_=DataTypes.NUMERIC, default=np.inf)
+ self._fit_max_spec = AttributeSpec(data_type=DataTypes.NUMERIC, default=np.inf)
self._fit_max = self._fit_max_spec.default
- self._start_value_spec = AttributeSpec(type_=DataTypes.NUMERIC, default=0.0)
+ self._start_value_spec = AttributeSpec(data_type=DataTypes.NUMERIC, default=0.0)
self._start_value = self._start_value_spec.default
self._constrained_spec = self._BOOL_SPEC_TEMPLATE
self._constrained = self._constrained_spec.default
@@ -250,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:
@@ -265,136 +307,156 @@ 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 name(self) -> str:
- """Local name of the parameter (without category/datablock)."""
- return self._name
-
- @property
- def unique_name(self):
- """Fully qualified parameter name including its context path."""
- parts = [
- self._identity.datablock_entry_name,
- self._identity.category_code,
- self._identity.category_entry_name,
- self.name,
- ]
- return '.'.join(filter(None, parts))
-
- @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: object) -> None:
+ """
+ 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
+ ``ConstraintsHandler.apply()``.
+ """
+ self.value = v
+ 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):
- """Estimated standard uncertainty of the fitted value, if
- available.
- """
+ def uncertainty(self) -> float | None:
+ """Estimated standard uncertainty of the fitted value."""
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
)
+# ======================================================================
+
+
class StringDescriptor(GenericStringDescriptor):
+ """String descriptor bound to a CIF handler."""
+
def __init__(
self,
*,
cif_handler: CifHandler,
- **kwargs: Any,
+ **kwargs: object,
) -> None:
- """String descriptor bound to a CIF handler.
-
- Args:
- cif_handler: Object that tracks CIF identifiers.
- **kwargs: Forwarded to GenericStringDescriptor.
+ """
+ Initialize a string descriptor bound to a CIF handler.
+
+ Parameters
+ ----------
+ cif_handler : CifHandler
+ Object that tracks CIF identifiers.
+ **kwargs : object
+ Forwarded to GenericStringDescriptor.
"""
super().__init__(**kwargs)
self._cif_handler = cif_handler
self._cif_handler.attach(self)
+# ======================================================================
+
+
class NumericDescriptor(GenericNumericDescriptor):
+ """Numeric descriptor bound to a CIF handler."""
+
def __init__(
self,
*,
cif_handler: CifHandler,
- **kwargs: Any,
+ **kwargs: object,
) -> None:
- """Numeric descriptor bound to a CIF handler.
-
- Args:
- cif_handler: Object that tracks CIF identifiers.
- **kwargs: Forwarded to GenericNumericDescriptor.
+ """
+ Numeric descriptor bound to a CIF handler.
+
+ Parameters
+ ----------
+ cif_handler : CifHandler
+ Object that tracks CIF identifiers.
+ **kwargs : object
+ Forwarded to GenericNumericDescriptor.
"""
super().__init__(**kwargs)
self._cif_handler = cif_handler
self._cif_handler.attach(self)
+# ======================================================================
+
+
class Parameter(GenericParameter):
+ """Fittable parameter bound to a CIF handler."""
+
def __init__(
self,
*,
cif_handler: CifHandler,
- **kwargs: Any,
+ **kwargs: object,
) -> None:
- """Fittable parameter bound to a CIF handler.
-
- Args:
- cif_handler: Object that tracks CIF identifiers.
- **kwargs: Forwarded to GenericParameter.
+ """
+ Fittable parameter bound to a CIF handler.
+
+ Parameters
+ ----------
+ cif_handler : CifHandler
+ Object that tracks CIF identifiers.
+ **kwargs : object
+ Forwarded to GenericParameter.
"""
super().__init__(**kwargs)
self._cif_handler = cif_handler
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..bc90383f 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
@@ -21,14 +21,19 @@ 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
- group.
-
- Args:
- cell: Dictionary containing lattice parameters.
- name_hm: Hermann-Mauguin symbol of the space group.
-
- Returns:
+ """
+ Apply symmetry constraints to unit cell parameters.
+
+ Parameters
+ ----------
+ cell : Dict[str, float]
+ Dictionary containing lattice parameters.
+ 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)
@@ -90,16 +95,23 @@ def apply_atom_site_symmetry_constraints(
coord_code: int,
wyckoff_letter: str,
) -> Dict[str, Any]:
- """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:
+ """
+ Apply symmetry constraints to atom site coordinates.
+
+ Parameters
+ ----------
+ atom_site : Dict[str, Any]
+ Dictionary containing atom position data.
+ name_hm : str
+ Hermann-Mauguin symbol of the space group.
+ coord_code : int
+ Coordinate system code.
+ 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/crystallography/space_groups.py b/src/easydiffraction/crystallography/space_groups.py
index 114b3467..4047b8c5 100644
--- a/src/easydiffraction/crystallography/space_groups.py
+++ b/src/easydiffraction/crystallography/space_groups.py
@@ -1,6 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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
@@ -10,11 +11,11 @@
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:
- """Load pickle data from an internal gz file (trusted boundary).
+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
this function. If distribution process changes, revisit.
@@ -23,7 +24,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/__init__.py b/src/easydiffraction/datablocks/__init__.py
new file mode 100644
index 00000000..4e798e20
--- /dev/null
+++ b/src/easydiffraction/datablocks/__init__.py
@@ -0,0 +1,2 @@
+# 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
new file mode 100644
index 00000000..4e798e20
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/__init__.py
@@ -0,0 +1,2 @@
+# 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
new file mode 100644
index 00000000..4e798e20
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/__init__.py
@@ -0,0 +1,2 @@
+# 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
new file mode 100644
index 00000000..7ffe8f22
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/background/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.categories.background.chebyshev import (
+ ChebyshevPolynomialBackground,
+)
+from easydiffraction.datablocks.experiment.categories.background.line_segment import (
+ LineSegmentBackground,
+)
diff --git a/src/easydiffraction/experiments/categories/background/base.py b/src/easydiffraction/datablocks/experiment/categories/background/base.py
similarity index 75%
rename from src/easydiffraction/experiments/categories/background/base.py
rename to src/easydiffraction/datablocks/experiment/categories/background/base.py
index 78cc5ef1..913cb764 100644
--- a/src/easydiffraction/experiments/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
@@ -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/experiments/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py
similarity index 52%
rename from src/easydiffraction/experiments/categories/background/chebyshev.py
rename to src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py
index 3532e35e..098c4268 100644
--- a/src/easydiffraction/experiments/categories/background/chebyshev.py
+++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py
@@ -1,6 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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.
"""
@@ -14,14 +15,19 @@
from numpy.polynomial.chebyshev import chebval
from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.parameters import StringDescriptor
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
from easydiffraction.core.validation import RangeValidator
from easydiffraction.core.validation import RegexValidator
-from easydiffraction.experiments.categories.background.base import BackgroundBase
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import Parameter
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.background.base import BackgroundBase
+from easydiffraction.datablocks.experiment.categories.background.factory import BackgroundFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
from easydiffraction.io.cif.handler import CifHandler
from easydiffraction.utils.logging import console
from easydiffraction.utils.logging import log
@@ -29,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
@@ -37,99 +44,117 @@ class PolynomialTerm(CategoryItem):
not break immediately. Tests should migrate to the short names.
"""
- def __init__(
- self,
- *,
- id=None, # TODO: rename as in the case of data points?
- order=None,
- coef=None,
- ) -> None:
+ def __init__(self) -> None:
super().__init__()
self._id = StringDescriptor(
name='id',
- description='Identifier for this background polynomial term.',
+ description='Identifier for this background polynomial term',
value_spec=AttributeSpec(
- type_=DataTypes.STRING,
- value=id,
default='0',
# TODO: the following pattern is valid for dict key
# (keywords are not checked). CIF label is less strict.
# Do we need conversion between CIF and internal label?
- content_validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_background.id',
- ]
+ validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
),
+ cif_handler=CifHandler(names=['_pd_background.id']),
)
self._order = NumericDescriptor(
name='order',
description='Order used in a Chebyshev polynomial background term',
value_spec=AttributeSpec(
- value=order,
- type_=DataTypes.NUMERIC,
default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_background.Chebyshev_order',
- ]
+ validator=RangeValidator(),
),
+ cif_handler=CifHandler(names=['_pd_background.Chebyshev_order']),
)
self._coef = Parameter(
name='coef',
description='Coefficient used in a Chebyshev polynomial background term',
value_spec=AttributeSpec(
- value=coef,
- type_=DataTypes.NUMERIC,
default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_background.Chebyshev_coef',
- ]
+ validator=RangeValidator(),
),
+ cif_handler=CifHandler(names=['_pd_background.Chebyshev_coef']),
)
self._identity.category_code = 'background'
self._identity.category_entry_name = lambda: str(self._id.value)
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
@property
- def id(self):
+ 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.
+ """
return self._id
@id.setter
- def id(self, value):
+ def id(self, value: str) -> None:
self._id.value = value
@property
- def order(self):
+ 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.
+ """
return self._order
@order.setter
- def order(self, value):
+ def order(self, value: float) -> None:
self._order.value = value
@property
- def coef(self):
+ 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.
+ """
return self._coef
@coef.setter
- def coef(self, value):
+ def coef(self, value: float) -> None:
self._coef.value = value
+@BackgroundFactory.register
class ChebyshevPolynomialBackground(BackgroundBase):
- _description: str = 'Chebyshev polynomial background'
-
- def __init__(self):
+ """Chebyshev polynomial background model."""
+
+ type_info = TypeInfo(
+ tag='chebyshev',
+ description='Chebyshev polynomial background',
+ )
+ compatibility = Compatibility(
+ beam_mode=frozenset({
+ BeamModeEnum.CONSTANT_WAVELENGTH,
+ BeamModeEnum.TIME_OF_FLIGHT,
+ }),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({
+ CalculatorEnum.CRYSPY,
+ CalculatorEnum.CRYSFML,
+ }),
+ )
+
+ 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
@@ -138,14 +163,14 @@ def _update(self, called_by_minimizer=False):
if not self._items:
log.warning('No background points found. Setting background to zero.')
- data._set_bkg(np.zeros_like(x))
+ data._set_intensity_bkg(np.zeros_like(x))
return
u = (x - x.min()) / (x.max() - x.min()) * 2 - 1
coefs = [term.coef.value for term in self._items]
y = chebval(u, coefs)
- data._set_bkg(y)
+ data._set_intensity_bkg(y)
def show(self) -> None:
"""Print a table of polynomial orders and coefficients."""
diff --git a/src/easydiffraction/experiments/categories/background/enums.py b/src/easydiffraction/datablocks/experiment/categories/background/enums.py
similarity index 84%
rename from src/easydiffraction/experiments/categories/background/enums.py
rename to src/easydiffraction/datablocks/experiment/categories/background/enums.py
index d7edf42e..9e78effc 100644
--- a/src/easydiffraction/experiments/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."""
@@ -12,7 +12,7 @@ class BackgroundTypeEnum(str, Enum):
"""Supported background model types."""
LINE_SEGMENT = 'line-segment'
- CHEBYSHEV = 'chebyshev polynomial'
+ CHEBYSHEV = 'chebyshev'
@classmethod
def default(cls) -> 'BackgroundTypeEnum':
diff --git a/src/easydiffraction/datablocks/experiment/categories/background/factory.py b/src/easydiffraction/datablocks/experiment/categories/background/factory.py
new file mode 100644
index 00000000..c4d300c8
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/background/factory.py
@@ -0,0 +1,14 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Background factory — delegates entirely to ``FactoryBase``."""
+
+from easydiffraction.core.factory import FactoryBase
+from easydiffraction.datablocks.experiment.categories.background.enums import BackgroundTypeEnum
+
+
+class BackgroundFactory(FactoryBase):
+ """Create background collections by tag."""
+
+ _default_rules = {
+ frozenset(): BackgroundTypeEnum.LINE_SEGMENT,
+ }
diff --git a/src/easydiffraction/experiments/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py
similarity index 54%
rename from src/easydiffraction/experiments/categories/background/line_segment.py
rename to src/easydiffraction/datablocks/experiment/categories/background/line_segment.py
index bf202ce3..2f0a5496 100644
--- a/src/easydiffraction/experiments/categories/background/line_segment.py
+++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py
@@ -1,6 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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.
"""
@@ -13,14 +14,19 @@
from scipy.interpolate import interp1d
from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.parameters import StringDescriptor
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
from easydiffraction.core.validation import RangeValidator
from easydiffraction.core.validation import RegexValidator
-from easydiffraction.experiments.categories.background.base import BackgroundBase
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import Parameter
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.background.base import BackgroundBase
+from easydiffraction.datablocks.experiment.categories.background.factory import BackgroundFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
from easydiffraction.io.cif.handler import CifHandler
from easydiffraction.utils.logging import console
from easydiffraction.utils.logging import log
@@ -30,44 +36,27 @@
class LineSegment(CategoryItem):
"""Single background control point for interpolation."""
- def __init__(
- self,
- *,
- id=None, # TODO: rename as in the case of data points?
- x=None,
- y=None,
- ) -> None:
+ def __init__(self) -> None:
super().__init__()
self._id = StringDescriptor(
name='id',
- description='Identifier for this background line segment.',
+ description='Identifier for this background line segment',
value_spec=AttributeSpec(
- type_=DataTypes.STRING,
- value=id,
default='0',
# TODO: the following pattern is valid for dict key
# (keywords are not checked). CIF label is less strict.
# Do we need conversion between CIF and internal label?
- content_validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_background.id',
- ]
+ validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
),
+ cif_handler=CifHandler(names=['_pd_background.id']),
)
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(
- value=x,
- type_=DataTypes.NUMERIC,
default=0.0,
- content_validator=RangeValidator(),
+ validator=RangeValidator(),
),
cif_handler=CifHandler(
names=[
@@ -78,15 +67,10 @@ def __init__(
)
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(
- value=y,
- type_=DataTypes.NUMERIC,
default=0.0,
- content_validator=RangeValidator(),
+ validator=RangeValidator(),
), # TODO: rename to intensity
cif_handler=CifHandler(
names=[
@@ -99,38 +83,74 @@ def __init__(
self._identity.category_code = 'background'
self._identity.category_entry_name = lambda: str(self._id.value)
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
@property
- def id(self):
+ 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.
+ """
return self._id
@id.setter
- def id(self, value):
+ def id(self, value: str) -> None:
self._id.value = value
@property
- def x(self):
+ def x(self) -> NumericDescriptor:
+ """
+ X-coordinates used to create many straight-line segments.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object. Assigning to it updates the
+ parameter value.
+ """
return self._x
@x.setter
- def x(self, value):
+ def x(self, value: float) -> None:
self._x.value = value
@property
- def y(self):
+ def y(self) -> Parameter:
+ """
+ Intensity used to create many straight-line segments.
+
+ Reading this property returns the underlying ``Parameter``
+ object. Assigning to it updates the parameter value.
+ """
return self._y
@y.setter
- def y(self, value):
+ def y(self, value: float) -> None:
self._y.value = value
+@BackgroundFactory.register
class LineSegmentBackground(BackgroundBase):
- _description: str = 'Linear interpolation between points'
-
- def __init__(self):
+ """Linear-interpolation background between user-defined points."""
+
+ type_info = TypeInfo(
+ tag='line-segment',
+ description='Linear interpolation between points',
+ )
+ compatibility = Compatibility(
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH, BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ 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
@@ -139,7 +159,7 @@ def _update(self, called_by_minimizer=False):
if not self._items:
log.debug('No background points found. Setting background to zero.')
- data._set_bkg(np.zeros_like(x))
+ data._set_intensity_bkg(np.zeros_like(x))
return
segments_x = np.array([point.x.value for point in self._items])
@@ -153,7 +173,7 @@ def _update(self, called_by_minimizer=False):
)
y = interp_func(x)
- data._set_bkg(y)
+ data._set_intensity_bkg(y)
def show(self) -> None:
"""Print a table of control points (x, intensity)."""
diff --git a/src/easydiffraction/datablocks/experiment/categories/data/__init__.py b/src/easydiffraction/datablocks/experiment/categories/data/__init__.py
new file mode 100644
index 00000000..3599f3b5
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/data/__init__.py
@@ -0,0 +1,7 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.categories.data.bragg_pd import PdCwlData
+from easydiffraction.datablocks.experiment.categories.data.bragg_pd import PdTofData
+from easydiffraction.datablocks.experiment.categories.data.bragg_sc import ReflnData
+from easydiffraction.datablocks.experiment.categories.data.total_pd import TotalData
diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py
new file mode 100644
index 00000000..883f7f80
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py
@@ -0,0 +1,625 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from __future__ import annotations
+
+import numpy as np
+
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import MembershipValidator
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.io.cif.handler import CifHandler
+from easydiffraction.utils.utils import tof_to_d
+from easydiffraction.utils.utils import twotheta_to_d
+
+
+class PdDataPointBaseMixin:
+ """Single base data point mixin for powder diffraction data."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._point_id = StringDescriptor(
+ name='point_id',
+ description='Identifier for this data point in the dataset',
+ value_spec=AttributeSpec(
+ default='0',
+ # TODO: the following pattern is valid for dict key
+ # (keywords are not checked). CIF label is less strict.
+ # Do we need conversion between CIF and internal label?
+ validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_data.point_id',
+ ]
+ ),
+ )
+ self._d_spacing = NumericDescriptor(
+ name='d_spacing',
+ description='d-spacing value corresponding to this data point',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_pd_proc.d_spacing']),
+ )
+ self._intensity_meas = NumericDescriptor(
+ name='intensity_meas',
+ description='Intensity recorded at each measurement point (angle/time)',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_meas.intensity_total',
+ '_pd_proc.intensity_norm',
+ ]
+ ),
+ )
+ self._intensity_meas_su = NumericDescriptor(
+ name='intensity_meas_su',
+ description='Standard uncertainty of the measured intensity at this point',
+ value_spec=AttributeSpec(
+ default=1.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_meas.intensity_total_su',
+ '_pd_proc.intensity_norm_su',
+ ]
+ ),
+ )
+ self._intensity_calc = NumericDescriptor(
+ name='intensity_calc',
+ description='Intensity of a computed diffractogram at this point',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_pd_calc.intensity_total']),
+ )
+ self._intensity_bkg = NumericDescriptor(
+ name='intensity_bkg',
+ description='Intensity of a computed background at this point',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_pd_calc.intensity_bkg']),
+ )
+ self._calc_status = StringDescriptor(
+ name='calc_status',
+ description='Status code of the data point in the calculation process',
+ value_spec=AttributeSpec(
+ default='incl', # TODO: Make Enum
+ validator=MembershipValidator(allowed=['incl', 'excl']),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_data.refinement_status', # TODO: rename to calc_status
+ ]
+ ),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def point_id(self) -> 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.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._d_spacing
+
+ @property
+ def intensity_meas(self) -> NumericDescriptor:
+ """
+ Intensity recorded at each measurement point (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 point.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._intensity_meas_su
+
+ @property
+ def intensity_calc(self) -> NumericDescriptor:
+ """
+ Intensity of a computed diffractogram at this point.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._intensity_calc
+
+ @property
+ def intensity_bkg(self) -> NumericDescriptor:
+ """
+ Intensity of a computed background at this point.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._intensity_bkg
+
+ @property
+ def calc_status(self) -> StringDescriptor:
+ """
+ Status code of the data point in the calculation process.
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object.
+ """
+ return self._calc_status
+
+
+class PdCwlDataPointMixin:
+ """Mixin for CWL powder diffraction data points."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._two_theta = NumericDescriptor(
+ name='two_theta',
+ description='Measured 2θ diffraction angle.',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0, le=180),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_proc.2theta_scan',
+ '_pd_meas.2theta_scan',
+ ]
+ ),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def two_theta(self) -> NumericDescriptor:
+ """
+ Measured 2θ diffraction angle (deg).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._two_theta
+
+
+class PdTofDataPointMixin:
+ """Mixin for powder diffraction data points with time-of-flight."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._time_of_flight = NumericDescriptor(
+ name='time_of_flight',
+ description='Measured time for time-of-flight neutron measurement.',
+ units='µs',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_pd_meas.time_of_flight']),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def time_of_flight(self) -> NumericDescriptor:
+ """
+ Measured time for time-of-flight neutron measurement (µs).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._time_of_flight
+
+
+class PdCwlDataPoint(
+ PdDataPointBaseMixin, # TODO: rename to BasePdDataPointMixin???
+ PdCwlDataPointMixin, # TODO: rename to CwlPdDataPointMixin???
+ CategoryItem, # Must be last to ensure mixins initialized first
+ # TODO: Check this. AI suggest class
+ # CwlThompsonCoxHastings(
+ # PeakBase, # From CategoryItem
+ # CwlBroadeningMixin,
+ # FcjAsymmetryMixin,
+ # ):
+ # But also says, that in fact, it is just for consistency. And both
+ # orders work.
+):
+ """Powder diffraction data point for CWL experiments."""
+
+ def __init__(self) -> None:
+ super().__init__()
+ self._identity.category_code = 'pd_data'
+ self._identity.category_entry_name = lambda: str(self.point_id.value)
+
+
+class PdTofDataPoint(
+ PdDataPointBaseMixin,
+ PdTofDataPointMixin,
+ CategoryItem, # Must be last to ensure mixins initialized first
+):
+ """Powder diffraction data point for time-of-flight experiments."""
+
+ def __init__(self) -> None:
+ super().__init__()
+ self._identity.category_code = 'pd_data'
+ self._identity.category_entry_name = lambda: str(self.point_id.value)
+
+
+class PdDataBase(CategoryCollection):
+ """Base class for powder diffraction data collections."""
+
+ # TODO: ???
+
+ # Redefine update priority to ensure data updated after other
+ # categories. Higher number = runs later. Default for other
+ # categories, e.g., background and excluded regions are 10 by
+ # default
+ _update_priority = 100
+
+ #################
+ # Private methods
+ #################
+
+ # Should be set only once
+
+ def _set_point_id(self, values: object) -> None:
+ """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:
+ """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: object) -> None:
+ """Set standard uncertainty of measured intensity values."""
+ for p, v in zip(self._items, values, strict=True):
+ p.intensity_meas_su._value = v
+
+ # Can be set multiple times
+
+ def _set_d_spacing(self, values: object) -> None:
+ """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:
+ """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:
+ """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:
+ """Set refinement status."""
+ for p, v in zip(self._items, values, strict=True):
+ if v:
+ p.calc_status._value = 'incl'
+ elif not v:
+ p.calc_status._value = 'excl'
+ else:
+ raise ValueError(
+ f'Invalid refinement status value: {v}. Expected boolean True/False.'
+ )
+
+ @property
+ def _calc_mask(self) -> np.ndarray:
+ return self.calc_status == 'incl'
+
+ @property
+ 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: bool = False) -> None:
+ experiment = self._parent
+ experiments = experiment._parent
+ project = experiments._parent
+ structures = project.structures
+ calculator = experiment.calculator
+
+ initial_calc = np.zeros_like(self.x)
+ calc = initial_calc
+
+ # TODO: refactor _get_valid_linked_phases to only be responsible
+ # for returning list. Warning message should be defined here,
+ # at least some of them.
+ # TODO: Adapt following the _update method in bragg_sc.py
+ for linked_phase in experiment._get_valid_linked_phases(structures):
+ structure_id = linked_phase._identity.category_entry_name
+ structure_scale = linked_phase.scale.value
+ structure = structures[structure_id]
+
+ structure_calc = calculator.calculate_pattern(
+ structure,
+ experiment,
+ called_by_minimizer=called_by_minimizer,
+ )
+
+ structure_scaled_calc = structure_scale * structure_calc
+ calc += structure_scaled_calc
+
+ self._set_intensity_calc(calc + self.intensity_bkg)
+
+ ###################
+ # Public properties
+ ###################
+
+ @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?
+ )
+
+ @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?
+ )
+
+ @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?
+ )
+
+ @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
+ # cause fitting algorithms to fail.
+ # The current implementation is inefficient.
+ # In the future, we should extend the functionality of
+ # the NumericDescriptor to automatically replace the value
+ # outside of the valid range (`validator`) with a
+ # default value (`default`), when the value is set.
+ # BraggPdExperiment._load_ascii_data_to_experiment() handles
+ # this for ASCII data, but we also need to handle CIF data and
+ # come up with a consistent approach for both data sources.
+ original = np.fromiter(
+ (p.intensity_meas_su.value for p in self._calc_items),
+ dtype=float, # TODO: needed? DataTypes.NUMERIC?
+ )
+ # Replace values smaller than 0.0001 with 1.0
+ modified = np.where(original < 0.0001, 1.0, original)
+ return modified
+
+ @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?
+ )
+
+ @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?
+ )
+
+
+@DataFactory.register
+class PdCwlData(PdDataBase):
+ """Bragg powder CWL data collection."""
+
+ # TODO: ???
+ # _description: str = 'Powder diffraction data points for
+ # constant-wavelength experiments.'
+ type_info = TypeInfo(tag='bragg-pd', description='Bragg powder CWL data')
+ compatibility = Compatibility(
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH, BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__(item_type=PdCwlDataPoint)
+
+ #################
+ # Private methods
+ #################
+
+ # Should be set only once
+
+ def _create_items_set_xcoord_and_id(self, values: object) -> None:
+ """Set 2θ values."""
+ # TODO: split into multiple methods
+
+ # Create items
+ self._items = [self._item_type() for _ in range(values.size)]
+
+ # Set two-theta values
+ for p, v in zip(self._items, values, strict=True):
+ p.two_theta._value = v
+
+ # Set point IDs
+ self._set_point_id([str(i + 1) for i in range(values.size)])
+
+ # Misc
+
+ def _update(self, called_by_minimizer: bool = False) -> None:
+ super()._update(called_by_minimizer)
+
+ experiment = self._parent
+ d_spacing = twotheta_to_d(
+ self.x,
+ experiment.instrument.setup_wavelength.value,
+ )
+ self._set_d_spacing(d_spacing)
+
+ ###################
+ # Public properties
+ ###################
+
+ @property
+ def two_theta(self) -> np.ndarray:
+ """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?
+ )
+
+ @property
+ def x(self) -> np.ndarray:
+ """Alias for two_theta."""
+ return self.two_theta
+
+ @property
+ def unfiltered_x(self) -> np.ndarray:
+ """Get the 2θ values for all data points in this collection."""
+ return np.fromiter(
+ (p.two_theta.value for p in self._items),
+ dtype=float, # TODO: needed? DataTypes.NUMERIC?
+ )
+
+
+@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}),
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__(item_type=PdTofDataPoint)
+
+ #################
+ # Private methods
+ #################
+
+ # Should be set only once
+
+ def _create_items_set_xcoord_and_id(self, values: object) -> None:
+ """Set time-of-flight values."""
+ # TODO: split into multiple methods
+
+ # Create items
+ self._items = [self._item_type() for _ in range(values.size)]
+
+ # Set time-of-flight values
+ for p, v in zip(self._items, values, strict=True):
+ p.time_of_flight._value = v
+
+ # Set point IDs
+ self._set_point_id([str(i + 1) for i in range(values.size)])
+
+ # Misc
+
+ def _update(self, called_by_minimizer: bool = False) -> None:
+ super()._update(called_by_minimizer)
+
+ experiment = self._parent
+ d_spacing = tof_to_d(
+ self.x,
+ experiment.instrument.calib_d_to_tof_offset.value,
+ experiment.instrument.calib_d_to_tof_linear.value,
+ experiment.instrument.calib_d_to_tof_quad.value,
+ )
+ self._set_d_spacing(d_spacing)
+
+ ###################
+ # Public properties
+ ###################
+
+ @property
+ def time_of_flight(self) -> np.ndarray:
+ """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?
+ )
+
+ @property
+ def x(self) -> np.ndarray:
+ """Alias for time_of_flight."""
+ return self.time_of_flight
+
+ @property
+ def unfiltered_x(self) -> np.ndarray:
+ """Get the TOF values for all data points in this collection."""
+ return np.fromiter(
+ (p.time_of_flight.value for p in self._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
new file mode 100644
index 00000000..d19eb75f
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py
@@ -0,0 +1,434 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from __future__ import annotations
+
+import numpy as np
+
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.io.cif.handler import CifHandler
+from easydiffraction.utils.logging import log
+from easydiffraction.utils.utils import sin_theta_over_lambda_to_d_spacing
+
+
+class Refln(CategoryItem):
+ """Single reflection for single-crystal diffraction data."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._id = StringDescriptor(
+ name='id',
+ description='Identifier of the reflection',
+ value_spec=AttributeSpec(
+ default='0',
+ # TODO: the following pattern is valid for dict key
+ # (keywords are not checked). CIF label is less strict.
+ # Do we need conversion between CIF and internal label?
+ validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(names=['_refln.id']),
+ )
+ self._d_spacing = NumericDescriptor(
+ name='d_spacing',
+ description='Distance between lattice planes for this reflection',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_refln.d_spacing']),
+ )
+ self._sin_theta_over_lambda = NumericDescriptor(
+ name='sin_theta_over_lambda',
+ description='The sin(θ)/λ value for this reflection',
+ units='Å⁻¹',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_refln.sin_theta_over_lambda']),
+ )
+ self._index_h = NumericDescriptor(
+ name='index_h',
+ description='Miller index h of a measured reflection',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_refln.index_h']),
+ )
+ self._index_k = NumericDescriptor(
+ name='index_k',
+ description='Miller index k of a measured reflection',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_refln.index_k']),
+ )
+ self._index_l = NumericDescriptor(
+ name='index_l',
+ description='Miller index l of a measured reflection',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_refln.index_l']),
+ )
+ self._intensity_meas = NumericDescriptor(
+ name='intensity_meas',
+ description=' The intensity of the reflection derived from the measurements.',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_refln.intensity_meas']),
+ )
+ self._intensity_meas_su = NumericDescriptor(
+ name='intensity_meas_su',
+ description='Standard uncertainty of the measured intensity.',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_refln.intensity_meas_su']),
+ )
+ self._intensity_calc = NumericDescriptor(
+ name='intensity_calc',
+ description='Intensity of the reflection calculated from atom site data',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_refln.intensity_calc']),
+ )
+ self._wavelength = NumericDescriptor(
+ name='wavelength',
+ description='Mean wavelength of radiation for this reflection',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(names=['_refln.wavelength']),
+ )
+
+ self._identity.category_code = 'refln'
+ self._identity.category_entry_name = lambda: str(self.id.value)
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def id(self) -> StringDescriptor:
+ """
+ Identifier of the reflection.
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object.
+ """
+ return self._id
+
+ @property
+ def d_spacing(self) -> NumericDescriptor:
+ """
+ Distance between lattice planes 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 (Å⁻¹).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._sin_theta_over_lambda
+
+ @property
+ def index_h(self) -> NumericDescriptor:
+ """
+ Miller index h of a measured reflection.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._index_h
+
+ @property
+ def index_k(self) -> NumericDescriptor:
+ """
+ Miller index k of a measured reflection.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._index_k
+
+ @property
+ def index_l(self) -> 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.
+
+ 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.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._intensity_meas_su
+
+ @property
+ def intensity_calc(self) -> NumericDescriptor:
+ """
+ Intensity of the reflection calculated from atom site data.
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._intensity_calc
+
+ @property
+ def wavelength(self) -> NumericDescriptor:
+ """
+ Mean wavelength of radiation for this reflection (Å).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._wavelength
+
+
+@DataFactory.register
+class ReflnData(CategoryCollection):
+ """Collection of reflections for single crystal diffraction data."""
+
+ type_info = TypeInfo(tag='bragg-sc', description='Bragg single-crystal reflection data')
+ compatibility = Compatibility(
+ sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}),
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH, BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY}),
+ )
+
+ _update_priority = 100
+
+ def __init__(self) -> None:
+ super().__init__(item_type=Refln)
+
+ #################
+ # Private methods
+ #################
+
+ # Should be set only once
+
+ def _create_items_set_hkl_and_id(
+ self,
+ indices_h: object,
+ indices_k: object,
+ indices_l: object,
+ ) -> None:
+ """Set Miller indices."""
+ # TODO: split into multiple methods
+
+ # Create items
+ self._items = [self._item_type() for _ in range(indices_h.size)]
+
+ # Set indices
+ for item, index_h, index_k, index_l in zip(
+ self._items, indices_h, indices_k, indices_l, strict=True
+ ):
+ item.index_h._value = index_h
+ item.index_k._value = index_k
+ item.index_l._value = index_l
+
+ # Set reflection IDs
+ self._set_id([str(i + 1) for i in range(indices_h.size)])
+
+ def _set_id(self, values: object) -> None:
+ """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:
+ """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: object) -> None:
+ """Set standard uncertainty of measured intensity values."""
+ for p, v in zip(self._items, values, strict=True):
+ p.intensity_meas_su._value = v
+
+ def _set_wavelength(self, values: object) -> None:
+ """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:
+ """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:
+ """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:
+ """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: bool = False) -> None:
+ experiment = self._parent
+ experiments = experiment._parent
+ project = experiments._parent
+ structures = project.structures
+ calculator = experiment.calculator
+
+ linked_crystal = experiment.linked_crystal
+ linked_crystal_id = experiment.linked_crystal.id.value
+
+ if linked_crystal_id not in structures.names:
+ log.error(
+ f"Linked crystal ID '{linked_crystal_id}' not found in "
+ f'structure IDs {structures.names}.'
+ )
+ return
+
+ structure_id = linked_crystal_id
+ structure_scale = linked_crystal.scale.value
+ structure = structures[structure_id]
+
+ stol, raw_calc = calculator.calculate_structure_factors(
+ structure,
+ experiment,
+ called_by_minimizer=called_by_minimizer,
+ )
+
+ d_spacing = sin_theta_over_lambda_to_d_spacing(stol)
+ calc = structure_scale * raw_calc
+
+ self._set_d_spacing(d_spacing)
+ self._set_sin_theta_over_lambda(stol)
+ self._set_intensity_calc(calc)
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @property
+ def intensity_calc(self) -> np.ndarray:
+ """Calculated intensities for all reflections."""
+ return np.fromiter(
+ (p.intensity_calc.value for p in self._items),
+ 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,
+ )
diff --git a/src/easydiffraction/datablocks/experiment/categories/data/factory.py b/src/easydiffraction/datablocks/experiment/categories/data/factory.py
new file mode 100644
index 00000000..d8cdcf12
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/data/factory.py
@@ -0,0 +1,33 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Data collection factory — delegates to ``FactoryBase``."""
+
+from easydiffraction.core.factory import FactoryBase
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+
+
+class DataFactory(FactoryBase):
+ """Factory for creating diffraction data collections."""
+
+ _default_rules = {
+ frozenset({
+ ('sample_form', SampleFormEnum.POWDER),
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('beam_mode', BeamModeEnum.CONSTANT_WAVELENGTH),
+ }): 'bragg-pd',
+ frozenset({
+ ('sample_form', SampleFormEnum.POWDER),
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('beam_mode', BeamModeEnum.TIME_OF_FLIGHT),
+ }): 'bragg-pd-tof',
+ frozenset({
+ ('sample_form', SampleFormEnum.POWDER),
+ ('scattering_type', ScatteringTypeEnum.TOTAL),
+ }): 'total-pd',
+ frozenset({
+ ('sample_form', SampleFormEnum.SINGLE_CRYSTAL),
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ }): 'bragg-sc',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py
new file mode 100644
index 00000000..c8d0aa9d
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py
@@ -0,0 +1,376 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Data categories for total scattering (PDF) experiments."""
+
+from __future__ import annotations
+
+import numpy as np
+
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import MembershipValidator
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class TotalDataPoint(CategoryItem):
+ """
+ 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.
+ """
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._point_id = StringDescriptor(
+ name='point_id',
+ description='Identifier for this data point in the dataset',
+ value_spec=AttributeSpec(
+ default='0',
+ validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_data.point_id', # TODO: Use total scattering CIF names
+ ]
+ ),
+ )
+ self._r = NumericDescriptor(
+ name='r',
+ description='Interatomic distance in real space',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_proc.r', # TODO: Use PDF-specific CIF names
+ ]
+ ),
+ )
+ self._g_r_meas = NumericDescriptor(
+ name='g_r_meas',
+ description='Measured pair distribution function G(r)',
+ value_spec=AttributeSpec(
+ default=0.0,
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_meas.intensity_total', # TODO: Use PDF-specific CIF names
+ ]
+ ),
+ )
+ self._g_r_meas_su = NumericDescriptor(
+ name='g_r_meas_su',
+ description='Standard uncertainty of measured G(r)',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_meas.intensity_total_su', # TODO: Use PDF-specific CIF names
+ ]
+ ),
+ )
+ self._g_r_calc = NumericDescriptor(
+ name='g_r_calc',
+ description='Calculated pair distribution function G(r)',
+ value_spec=AttributeSpec(
+ default=0.0,
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_calc.intensity_total', # TODO: Use PDF-specific CIF names
+ ]
+ ),
+ )
+ self._calc_status = StringDescriptor(
+ name='calc_status',
+ description='Status code of the data point in calculation',
+ value_spec=AttributeSpec(
+ default='incl',
+ validator=MembershipValidator(allowed=['incl', 'excl']),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_pd_data.refinement_status', # TODO: Use PDF-specific CIF names
+ ]
+ ),
+ )
+
+ self._identity.category_code = 'total_data'
+ self._identity.category_entry_name = lambda: str(self.point_id.value)
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def point_id(self) -> 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 (Å).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._r
+
+ @property
+ def g_r_meas(self) -> NumericDescriptor:
+ """
+ Measured pair distribution function G(r).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._g_r_meas
+
+ @property
+ def g_r_meas_su(self) -> NumericDescriptor:
+ """
+ Standard uncertainty of measured G(r).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._g_r_meas_su
+
+ @property
+ def g_r_calc(self) -> NumericDescriptor:
+ """
+ Calculated pair distribution function G(r).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object.
+ """
+ return self._g_r_calc
+
+ @property
+ def calc_status(self) -> StringDescriptor:
+ """
+ Status code of the data point in calculation.
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object.
+ """
+ return self._calc_status
+
+
+class TotalDataBase(CategoryCollection):
+ """Base class for total scattering data collections."""
+
+ _update_priority = 100
+
+ #################
+ # Private methods
+ #################
+
+ # Should be set only once
+
+ def _set_point_id(self, values: object) -> None:
+ """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:
+ """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: object) -> None:
+ """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
+
+ # Can be set multiple times
+
+ def _set_g_r_calc(self, values: object) -> None:
+ """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:
+ """Set calculation status."""
+ for p, v in zip(self._items, values, strict=True):
+ if v:
+ p.calc_status._value = 'incl'
+ elif not v:
+ p.calc_status._value = 'excl'
+ else:
+ raise ValueError(
+ f'Invalid calculation status value: {v}. Expected boolean True/False.'
+ )
+
+ @property
+ def _calc_mask(self) -> np.ndarray:
+ return self.calc_status == 'incl'
+
+ @property
+ 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: bool = False) -> None:
+ experiment = self._parent
+ experiments = experiment._parent
+ project = experiments._parent
+ structures = project.structures
+ calculator = experiment.calculator
+
+ initial_calc = np.zeros_like(self.x)
+ calc = initial_calc
+
+ # TODO: refactor _get_valid_linked_phases to only be responsible
+ # for returning list. Warning message should be defined here,
+ # at least some of them.
+ # TODO: Adapt following the _update method in bragg_sc.py
+ for linked_phase in experiment._get_valid_linked_phases(structures):
+ structure_id = linked_phase._identity.category_entry_name
+ structure_scale = linked_phase.scale.value
+ structure = structures[structure_id]
+
+ structure_calc = calculator.calculate_pattern(
+ structure,
+ experiment,
+ called_by_minimizer=called_by_minimizer,
+ )
+
+ structure_scaled_calc = structure_scale * structure_calc
+ calc += structure_scaled_calc
+
+ self._set_g_r_calc(calc)
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @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,
+ )
+
+ @property
+ def intensity_bkg(self) -> np.ndarray:
+ """Background is always zero for PDF data."""
+ return np.zeros_like(self.intensity_calc)
+
+
+@DataFactory.register
+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.
+ """
+
+ type_info = TypeInfo(
+ tag='total-pd',
+ description='Total scattering (PDF) data',
+ )
+ compatibility = Compatibility(
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ scattering_type=frozenset({ScatteringTypeEnum.TOTAL}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH, BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.PDFFIT}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__(item_type=TotalDataPoint)
+
+ #################
+ # Private methods
+ #################
+
+ # Should be set only once
+
+ def _create_items_set_xcoord_and_id(self, values: object) -> None:
+ """Set r values."""
+ # TODO: split into multiple methods
+
+ # Create items
+ self._items = [self._item_type() for _ in range(values.size)]
+
+ # Set r values
+ for p, v in zip(self._items, values, strict=True):
+ p.r._value = v
+
+ # Set point IDs
+ self._set_point_id([str(i + 1) for i in range(values.size)])
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def x(self) -> np.ndarray:
+ """Get the r values for data points included in calculations."""
+ return np.fromiter(
+ (p.r.value for p in self._calc_items),
+ dtype=float, # TODO: needed? DataTypes.NUMERIC?
+ )
+
+ @property
+ def unfiltered_x(self) -> np.ndarray:
+ """Get the r values for all data points."""
+ return np.fromiter(
+ (p.r.value for p in self._items),
+ dtype=float, # TODO: needed? DataTypes.NUMERIC?
+ )
diff --git a/src/easydiffraction/datablocks/experiment/categories/diffrn/__init__.py b/src/easydiffraction/datablocks/experiment/categories/diffrn/__init__.py
new file mode 100644
index 00000000..6c11ee7e
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/diffrn/__init__.py
@@ -0,0 +1,4 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.categories.diffrn.default import DefaultDiffrn
diff --git a/src/easydiffraction/datablocks/experiment/categories/diffrn/default.py b/src/easydiffraction/datablocks/experiment/categories/diffrn/default.py
new file mode 100644
index 00000000..9635fc2e
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/diffrn/default.py
@@ -0,0 +1,136 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Default diffraction ambient-conditions category."""
+
+from __future__ import annotations
+
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.datablocks.experiment.categories.diffrn.factory import DiffrnFactory
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@DiffrnFactory.register
+class DefaultDiffrn(CategoryItem):
+ """Ambient conditions recorded during diffraction measurement."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Diffraction ambient conditions',
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._ambient_temperature = NumericDescriptor(
+ name='ambient_temperature',
+ description='Mean temperature during measurement',
+ units='K',
+ value_spec=AttributeSpec(
+ default=None,
+ allow_none=True,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_diffrn.ambient_temperature']),
+ )
+
+ self._ambient_pressure = NumericDescriptor(
+ name='ambient_pressure',
+ description='Mean hydrostatic pressure during measurement',
+ units='kPa',
+ value_spec=AttributeSpec(
+ default=None,
+ allow_none=True,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_diffrn.ambient_pressure']),
+ )
+
+ self._ambient_magnetic_field = NumericDescriptor(
+ name='ambient_magnetic_field',
+ description='Mean magnetic field during measurement',
+ units='T',
+ value_spec=AttributeSpec(
+ default=None,
+ allow_none=True,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_diffrn.ambient_magnetic_field']),
+ )
+
+ self._ambient_electric_field = NumericDescriptor(
+ name='ambient_electric_field',
+ description='Mean electric field during measurement',
+ units='V/m',
+ value_spec=AttributeSpec(
+ default=None,
+ allow_none=True,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_diffrn.ambient_electric_field']),
+ )
+
+ self._identity.category_code = 'diffrn'
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def ambient_temperature(self) -> NumericDescriptor:
+ """
+ Mean temperature during measurement (K).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object. Assigning to it updates the value.
+ """
+ return self._ambient_temperature
+
+ @ambient_temperature.setter
+ def ambient_temperature(self, value: float) -> None:
+ self._ambient_temperature.value = value
+
+ @property
+ def ambient_pressure(self) -> NumericDescriptor:
+ """
+ Mean hydrostatic pressure during measurement (kPa).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object. Assigning to it updates the value.
+ """
+ return self._ambient_pressure
+
+ @ambient_pressure.setter
+ def ambient_pressure(self, value: float) -> None:
+ self._ambient_pressure.value = value
+
+ @property
+ def ambient_magnetic_field(self) -> NumericDescriptor:
+ """
+ Mean magnetic field during measurement (T).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object. Assigning to it updates the value.
+ """
+ return self._ambient_magnetic_field
+
+ @ambient_magnetic_field.setter
+ def ambient_magnetic_field(self, value: float) -> None:
+ self._ambient_magnetic_field.value = value
+
+ @property
+ def ambient_electric_field(self) -> NumericDescriptor:
+ """
+ Mean electric field during measurement (V/m).
+
+ Reading this property returns the underlying
+ ``NumericDescriptor`` object. Assigning to it updates the value.
+ """
+ return self._ambient_electric_field
+
+ @ambient_electric_field.setter
+ def ambient_electric_field(self, value: float) -> None:
+ self._ambient_electric_field.value = value
diff --git a/src/easydiffraction/datablocks/experiment/categories/diffrn/factory.py b/src/easydiffraction/datablocks/experiment/categories/diffrn/factory.py
new file mode 100644
index 00000000..ef5fb719
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/diffrn/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Factory for diffraction ambient-conditions categories."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class DiffrnFactory(FactoryBase):
+ """Create diffraction ambient-conditions category instances."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/__init__.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/__init__.py
new file mode 100644
index 00000000..8d232629
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.categories.excluded_regions.default import (
+ ExcludedRegion,
+)
+from easydiffraction.datablocks.experiment.categories.excluded_regions.default import (
+ ExcludedRegions,
+)
diff --git a/src/easydiffraction/experiments/categories/excluded_regions.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py
similarity index 60%
rename from src/easydiffraction/experiments/categories/excluded_regions.py
rename to src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py
index 0ebce0a4..fdf130c2 100644
--- a/src/easydiffraction/experiments/categories/excluded_regions.py
+++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py
@@ -1,19 +1,26 @@
-# 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)."""
+from __future__ import annotations
+
from typing import List
import numpy as np
from easydiffraction.core.category import CategoryCollection
from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import StringDescriptor
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
from easydiffraction.core.validation import RangeValidator
from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import NumericDescriptor
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.excluded_regions.factory import (
+ ExcludedRegionsFactory,
+)
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
from easydiffraction.io.cif.handler import CifHandler
from easydiffraction.utils.logging import console
from easydiffraction.utils.utils import render_table
@@ -22,111 +29,119 @@
class ExcludedRegion(CategoryItem):
"""Closed interval [start, end] to be excluded."""
- def __init__(
- self,
- *,
- id=None, # TODO: rename as in the case of data points?
- start=None,
- end=None,
- ):
+ def __init__(self) -> None:
super().__init__()
# 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(
- type_=DataTypes.STRING,
- value=id,
default='0',
# TODO: the following pattern is valid for dict key
# (keywords are not checked). CIF label is less strict.
# Do we need conversion between CIF and internal label?
- content_validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_excluded_region.id',
- ]
+ validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
),
+ cif_handler=CifHandler(names=['_excluded_region.id']),
)
self._start = NumericDescriptor(
name='start',
description='Start of the excluded region.',
value_spec=AttributeSpec(
- value=start,
- type_=DataTypes.NUMERIC,
default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_excluded_region.start',
- ]
+ validator=RangeValidator(),
),
+ cif_handler=CifHandler(names=['_excluded_region.start']),
)
self._end = NumericDescriptor(
name='end',
description='End of the excluded region.',
value_spec=AttributeSpec(
- value=end,
- type_=DataTypes.NUMERIC,
default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_excluded_region.end',
- ]
+ validator=RangeValidator(),
),
+ cif_handler=CifHandler(names=['_excluded_region.end']),
)
- # self._category_entry_attr_name = f'{start}-{end}'
- # self._category_entry_attr_name = self.start.name
- # self.name = self.start.value
self._identity.category_code = 'excluded_regions'
self._identity.category_entry_name = lambda: str(self._id.value)
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
@property
- def id(self):
+ def id(self) -> 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):
+ def id(self, value: str) -> None:
self._id.value = value
@property
def start(self) -> 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):
+ def start(self, value: float) -> None:
self._start.value = value
@property
def end(self) -> 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):
+ def end(self, value: float) -> None:
self._end.value = value
+@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
fitting and plotting.
"""
- def __init__(self):
+ type_info = TypeInfo(
+ tag='default',
+ description='Excluded x-axis regions for fitting and plotting',
+ )
+ compatibility = Compatibility(
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ )
+
+ 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
- x = data.all_x
+ x = data.unfiltered_x
# Start with a mask of all False (nothing excluded yet)
combined_mask = np.full_like(x, fill_value=False, dtype=bool)
diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py
new file mode 100644
index 00000000..e12fb0c0
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/factory.py
@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+Excluded-regions factory — delegates entirely to ``FactoryBase``.
+"""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class ExcludedRegionsFactory(FactoryBase):
+ """Create excluded-regions collections by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/__init__.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/__init__.py
new file mode 100644
index 00000000..197b5510
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/__init__.py
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
index 00000000..1b9d3811
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py
@@ -0,0 +1,142 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# 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
+``CifHandler``.
+"""
+
+from __future__ import annotations
+
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import MembershipValidator
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.experiment_type.factory import (
+ ExperimentTypeFactory,
+)
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import RadiationProbeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@ExperimentTypeFactory.register
+class ExperimentType(CategoryItem):
+ """Container of attributes defining the experiment type."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Experiment type descriptor',
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._sample_form = StringDescriptor(
+ name='sample_form',
+ description='Powder diffraction or single crystal diffraction',
+ value_spec=AttributeSpec(
+ default=SampleFormEnum.default().value,
+ validator=MembershipValidator(allowed=[member.value for member in SampleFormEnum]),
+ ),
+ cif_handler=CifHandler(names=['_expt_type.sample_form']),
+ )
+
+ self._beam_mode = StringDescriptor(
+ name='beam_mode',
+ 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]),
+ ),
+ cif_handler=CifHandler(names=['_expt_type.beam_mode']),
+ )
+ self._radiation_probe = StringDescriptor(
+ name='radiation_probe',
+ description='Neutron or X-ray diffraction measurement',
+ value_spec=AttributeSpec(
+ default=RadiationProbeEnum.default().value,
+ validator=MembershipValidator(
+ allowed=[member.value for member in RadiationProbeEnum]
+ ),
+ ),
+ cif_handler=CifHandler(names=['_expt_type.radiation_probe']),
+ )
+ self._scattering_type = StringDescriptor(
+ name='scattering_type',
+ description='Conventional Bragg diffraction or total scattering (PDF)',
+ value_spec=AttributeSpec(
+ default=ScatteringTypeEnum.default().value,
+ validator=MembershipValidator(
+ allowed=[member.value for member in ScatteringTypeEnum]
+ ),
+ ),
+ cif_handler=CifHandler(names=['_expt_type.scattering_type']),
+ )
+
+ self._identity.category_code = 'expt_type'
+
+ # ------------------------------------------------------------------
+ # Private setters (used by factories and loaders only)
+ # ------------------------------------------------------------------
+
+ def _set_sample_form(self, value: str) -> None:
+ self._sample_form.value = value
+
+ def _set_beam_mode(self, value: str) -> None:
+ self._beam_mode.value = value
+
+ def _set_radiation_probe(self, value: str) -> None:
+ self._radiation_probe.value = value
+
+ def _set_scattering_type(self, value: str) -> None:
+ self._scattering_type.value = value
+
+ # ------------------------------------------------------------------
+ # Public read-only properties
+ # ------------------------------------------------------------------
+
+ @property
+ def sample_form(self) -> StringDescriptor:
+ """
+ Powder diffraction or single crystal diffraction.
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object.
+ """
+ return self._sample_form
+
+ @property
+ def beam_mode(self) -> StringDescriptor:
+ """
+ Constant wavelength (CW) or time-of-flight (TOF) measurement.
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object.
+ """
+ return self._beam_mode
+
+ @property
+ def radiation_probe(self) -> StringDescriptor:
+ """
+ Neutron or X-ray diffraction measurement.
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object.
+ """
+ return self._radiation_probe
+
+ @property
+ def scattering_type(self) -> StringDescriptor:
+ """
+ Conventional Bragg diffraction or total scattering (PDF).
+
+ Reading this property returns the underlying
+ ``StringDescriptor`` object.
+ """
+ return self._scattering_type
diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/factory.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/factory.py
new file mode 100644
index 00000000..05f0d2d9
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Experiment-type factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class ExperimentTypeFactory(FactoryBase):
+ """Create experiment-type descriptors by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/__init__.py b/src/easydiffraction/datablocks/experiment/categories/extinction/__init__.py
new file mode 100644
index 00000000..3e6fa39a
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/extinction/__init__.py
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
index 00000000..4e4bd9ed
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/extinction/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Extinction factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class ExtinctionFactory(FactoryBase):
+ """Create extinction correction models by tag."""
+
+ _default_rules = {
+ frozenset(): 'shelx',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py
new file mode 100644
index 00000000..dd736a1a
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/extinction/shelx.py
@@ -0,0 +1,94 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Shelx-style isotropic extinction correction."""
+
+from __future__ import annotations
+
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.datablocks.experiment.categories.extinction.factory import ExtinctionFactory
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@ExtinctionFactory.register
+class ShelxExtinction(CategoryItem):
+ """Shelx-style extinction correction for single crystals."""
+
+ type_info = TypeInfo(
+ tag='shelx',
+ description='Shelx-style isotropic extinction correction',
+ )
+ compatibility = Compatibility(
+ sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._mosaicity = Parameter(
+ name='mosaicity',
+ description='Mosaicity value for extinction correction',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=1.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_extinction.mosaicity',
+ ]
+ ),
+ )
+ self._radius = Parameter(
+ name='radius',
+ description='Crystal radius for extinction correction',
+ units='µm',
+ value_spec=AttributeSpec(
+ default=1.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_extinction.radius',
+ ]
+ ),
+ )
+
+ self._identity.category_code = 'extinction'
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ 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.
+ """
+ return self._mosaicity
+
+ @mosaicity.setter
+ def mosaicity(self, value: float) -> None:
+ self._mosaicity.value = value
+
+ @property
+ 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.
+ """
+ return self._radius
+
+ @radius.setter
+ def radius(self, value: float) -> None:
+ self._radius.value = value
diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/__init__.py b/src/easydiffraction/datablocks/experiment/categories/instrument/__init__.py
new file mode 100644
index 00000000..d12b40c3
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/instrument/__init__.py
@@ -0,0 +1,7 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.categories.instrument.cwl import CwlPdInstrument
+from easydiffraction.datablocks.experiment.categories.instrument.cwl import CwlScInstrument
+from easydiffraction.datablocks.experiment.categories.instrument.tof import TofPdInstrument
+from easydiffraction.datablocks.experiment.categories.instrument.tof import TofScInstrument
diff --git a/src/easydiffraction/experiments/categories/instrument/base.py b/src/easydiffraction/datablocks/experiment/categories/instrument/base.py
similarity index 72%
rename from src/easydiffraction/experiments/categories/instrument/base.py
rename to src/easydiffraction/datablocks/experiment/categories/instrument/base.py
index 0d1c04d5..a2568884 100644
--- a/src/easydiffraction/experiments/categories/instrument/base.py
+++ b/src/easydiffraction/datablocks/experiment/categories/instrument/base.py
@@ -1,6 +1,7 @@
-# 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.
+"""
+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
new file mode 100644
index 00000000..3a3628bd
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py
@@ -0,0 +1,127 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.datablocks.experiment.categories.instrument.base import InstrumentBase
+from easydiffraction.datablocks.experiment.categories.instrument.factory import InstrumentFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class CwlInstrumentBase(InstrumentBase):
+ """Base class for constant-wavelength instruments."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._setup_wavelength: Parameter = Parameter(
+ name='wavelength',
+ description='Incident neutron or X-ray wavelength',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=1.5406,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_instr.wavelength',
+ ]
+ ),
+ )
+
+ @property
+ 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.
+ """
+ return self._setup_wavelength
+
+ @setup_wavelength.setter
+ def setup_wavelength(self, value: float) -> None:
+ self._setup_wavelength.value = value
+
+
+@InstrumentFactory.register
+class CwlScInstrument(CwlInstrumentBase):
+ """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}),
+ sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+
+@InstrumentFactory.register
+class CwlPdInstrument(CwlInstrumentBase):
+ """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}),
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({
+ CalculatorEnum.CRYSPY,
+ CalculatorEnum.CRYSFML,
+ CalculatorEnum.PDFFIT,
+ }),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._calib_twotheta_offset: Parameter = Parameter(
+ name='twotheta_offset',
+ description='Instrument misalignment offset',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_instr.2theta_offset',
+ ]
+ ),
+ )
+
+ @property
+ 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.
+ """
+ return self._calib_twotheta_offset
+
+ @calib_twotheta_offset.setter
+ def calib_twotheta_offset(self, value: float) -> None:
+ self._calib_twotheta_offset.value = value
diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/factory.py b/src/easydiffraction/datablocks/experiment/categories/instrument/factory.py
new file mode 100644
index 00000000..fce8ad5c
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/instrument/factory.py
@@ -0,0 +1,30 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Instrument factory — delegates to ``FactoryBase``."""
+
+from easydiffraction.core.factory import FactoryBase
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+
+
+class InstrumentFactory(FactoryBase):
+ """Create instrument instances for supported modes."""
+
+ _default_rules = {
+ frozenset({
+ ('beam_mode', BeamModeEnum.CONSTANT_WAVELENGTH),
+ ('sample_form', SampleFormEnum.POWDER),
+ }): 'cwl-pd',
+ frozenset({
+ ('beam_mode', BeamModeEnum.CONSTANT_WAVELENGTH),
+ ('sample_form', SampleFormEnum.SINGLE_CRYSTAL),
+ }): 'cwl-sc',
+ frozenset({
+ ('beam_mode', BeamModeEnum.TIME_OF_FLIGHT),
+ ('sample_form', SampleFormEnum.POWDER),
+ }): 'tof-pd',
+ frozenset({
+ ('beam_mode', BeamModeEnum.TIME_OF_FLIGHT),
+ ('sample_form', SampleFormEnum.SINGLE_CRYSTAL),
+ }): 'tof-sc',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py
new file mode 100644
index 00000000..7e1db98e
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py
@@ -0,0 +1,179 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.datablocks.experiment.categories.instrument.base import InstrumentBase
+from easydiffraction.datablocks.experiment.categories.instrument.factory import InstrumentFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@InstrumentFactory.register
+class TofScInstrument(InstrumentBase):
+ """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}),
+ sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+
+@InstrumentFactory.register
+class TofPdInstrument(InstrumentBase):
+ """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}),
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._setup_twotheta_bank: Parameter = Parameter(
+ name='twotheta_bank',
+ description='Detector bank position',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=150.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_instr.2theta_bank']),
+ )
+ self._calib_d_to_tof_offset: Parameter = Parameter(
+ name='d_to_tof_offset',
+ description='TOF offset',
+ units='µs',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_instr.d_to_tof_offset']),
+ )
+ self._calib_d_to_tof_linear: Parameter = Parameter(
+ name='d_to_tof_linear',
+ description='TOF linear conversion',
+ units='µs/Å',
+ value_spec=AttributeSpec(
+ default=10000.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_instr.d_to_tof_linear']),
+ )
+ self._calib_d_to_tof_quad: Parameter = Parameter(
+ name='d_to_tof_quad',
+ description='TOF quadratic correction',
+ units='µs/Ų',
+ value_spec=AttributeSpec(
+ default=-0.00001, # TODO: Fix CrysPy to accept 0
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_instr.d_to_tof_quad']),
+ )
+ self._calib_d_to_tof_recip: Parameter = Parameter(
+ name='d_to_tof_recip',
+ description='TOF reciprocal velocity correction',
+ units='µs·Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_instr.d_to_tof_recip']),
+ )
+
+ @property
+ 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.
+ """
+ return self._setup_twotheta_bank
+
+ @setup_twotheta_bank.setter
+ def setup_twotheta_bank(self, value: float) -> None:
+ self._setup_twotheta_bank.value = value
+
+ @property
+ 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.
+ """
+ return self._calib_d_to_tof_offset
+
+ @calib_d_to_tof_offset.setter
+ def calib_d_to_tof_offset(self, value: float) -> None:
+ self._calib_d_to_tof_offset.value = value
+
+ @property
+ 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.
+ """
+ return self._calib_d_to_tof_linear
+
+ @calib_d_to_tof_linear.setter
+ def calib_d_to_tof_linear(self, value: float) -> None:
+ self._calib_d_to_tof_linear.value = value
+
+ @property
+ 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.
+ """
+ return self._calib_d_to_tof_quad
+
+ @calib_d_to_tof_quad.setter
+ def calib_d_to_tof_quad(self, value: float) -> None:
+ self._calib_d_to_tof_quad.value = value
+
+ @property
+ 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.
+ """
+ return self._calib_d_to_tof_recip
+
+ @calib_d_to_tof_recip.setter
+ def calib_d_to_tof_recip(self, value: float) -> None:
+ self._calib_d_to_tof_recip.value = value
diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py
new file mode 100644
index 00000000..4b93121b
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
index 00000000..d441aa9c
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py
@@ -0,0 +1,89 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Default linked-crystal reference (id + scale)."""
+
+from __future__ import annotations
+
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.linked_crystal.factory import (
+ LinkedCrystalFactory,
+)
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@LinkedCrystalFactory.register
+class LinkedCrystal(CategoryItem):
+ """Linked crystal reference for single-crystal diffraction."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Crystal reference with id and scale factor',
+ )
+ compatibility = Compatibility(
+ sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._id = StringDescriptor(
+ name='id',
+ description='Identifier of the linked crystal',
+ value_spec=AttributeSpec(
+ default='Si',
+ validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(names=['_sc_crystal_block.id']),
+ )
+ self._scale = Parameter(
+ name='scale',
+ description='Scale factor of the linked crystal',
+ value_spec=AttributeSpec(
+ default=1.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_sc_crystal_block.scale']),
+ )
+
+ self._identity.category_code = 'linked_crystal'
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def id(self) -> 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:
+ self._id.value = value
+
+ @property
+ 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.
+ """
+ return self._scale
+
+ @scale.setter
+ def scale(self, value: float) -> None:
+ self._scale.value = value
diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py
new file mode 100644
index 00000000..b34b8073
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Linked-crystal factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class LinkedCrystalFactory(FactoryBase):
+ """Create linked-crystal references by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py
new file mode 100644
index 00000000..dda7d445
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.categories.linked_phases.default import LinkedPhase
+from easydiffraction.datablocks.experiment.categories.linked_phases.default import LinkedPhases
diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py
new file mode 100644
index 00000000..97d03c66
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py
@@ -0,0 +1,99 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Linked phases allow combining phases with scale factors."""
+
+from __future__ import annotations
+
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.experiment.categories.linked_phases.factory import (
+ LinkedPhasesFactory,
+)
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class LinkedPhase(CategoryItem):
+ """Link to a phase by id with a scale factor."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._id = StringDescriptor(
+ name='id',
+ description='Identifier of the linked phase',
+ value_spec=AttributeSpec(
+ default='Si',
+ validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(names=['_pd_phase_block.id']),
+ )
+ self._scale = Parameter(
+ name='scale',
+ description='Scale factor of the linked phase.',
+ value_spec=AttributeSpec(
+ default=1.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_pd_phase_block.scale']),
+ )
+
+ self._identity.category_code = 'linked_phases'
+ self._identity.category_entry_name = lambda: str(self.id.value)
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def id(self) -> 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:
+ self._id.value = value
+
+ @property
+ 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.
+ """
+ return self._scale
+
+ @scale.setter
+ def scale(self, value: float) -> None:
+ self._scale.value = value
+
+
+@LinkedPhasesFactory.register
+class LinkedPhases(CategoryCollection):
+ """Collection of LinkedPhase instances."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Phase references with scale factors',
+ )
+ compatibility = Compatibility(
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ )
+
+ def __init__(self) -> None:
+ """Create an empty collection of linked phases."""
+ super().__init__(item_type=LinkedPhase)
diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py
new file mode 100644
index 00000000..56970ee8
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Linked-phases factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class LinkedPhasesFactory(FactoryBase):
+ """Create linked-phases collections by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/__init__.py b/src/easydiffraction/datablocks/experiment/categories/peak/__init__.py
new file mode 100644
index 00000000..667bafb4
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/__init__.py
@@ -0,0 +1,10 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.categories.peak.cwl import CwlPseudoVoigt
+from easydiffraction.datablocks.experiment.categories.peak.cwl import CwlSplitPseudoVoigt
+from easydiffraction.datablocks.experiment.categories.peak.cwl import CwlThompsonCoxHastings
+from easydiffraction.datablocks.experiment.categories.peak.tof import TofPseudoVoigt
+from easydiffraction.datablocks.experiment.categories.peak.tof import TofPseudoVoigtBackToBack
+from easydiffraction.datablocks.experiment.categories.peak.tof import TofPseudoVoigtIkedaCarpenter
+from easydiffraction.datablocks.experiment.categories.peak.total import TotalGaussianDampedSinc
diff --git a/src/easydiffraction/experiments/categories/peak/base.py b/src/easydiffraction/datablocks/experiment/categories/peak/base.py
similarity index 75%
rename from src/easydiffraction/experiments/categories/peak/base.py
rename to src/easydiffraction/datablocks/experiment/categories/peak/base.py
index 5f2654a7..050411b1 100644
--- a/src/easydiffraction/experiments/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
new file mode 100644
index 00000000..a2b4f63b
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl.py
@@ -0,0 +1,88 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Constant-wavelength peak profile classes."""
+
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.categories.peak.base import PeakBase
+from easydiffraction.datablocks.experiment.categories.peak.cwl_mixins import CwlBroadeningMixin
+from easydiffraction.datablocks.experiment.categories.peak.cwl_mixins import (
+ EmpiricalAsymmetryMixin,
+)
+from easydiffraction.datablocks.experiment.categories.peak.cwl_mixins import FcjAsymmetryMixin
+from easydiffraction.datablocks.experiment.categories.peak.factory import PeakFactory
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+
+
+@PeakFactory.register
+class CwlPseudoVoigt(
+ PeakBase,
+ CwlBroadeningMixin,
+):
+ """Constant-wavelength pseudo-Voigt peak shape."""
+
+ type_info = TypeInfo(
+ tag='pseudo-voigt',
+ description='Pseudo-Voigt profile',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+
+@PeakFactory.register
+class CwlSplitPseudoVoigt(
+ PeakBase,
+ CwlBroadeningMixin,
+ EmpiricalAsymmetryMixin,
+):
+ """Split pseudo-Voigt (empirical asymmetry) for CWL mode."""
+
+ type_info = TypeInfo(
+ tag='split pseudo-voigt',
+ description='Split pseudo-Voigt with empirical asymmetry correction',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+
+@PeakFactory.register
+class CwlThompsonCoxHastings(
+ PeakBase,
+ CwlBroadeningMixin,
+ FcjAsymmetryMixin,
+):
+ """Thompson–Cox–Hastings with FCJ asymmetry for CWL mode."""
+
+ type_info = TypeInfo(
+ tag='thompson-cox-hastings',
+ description='Thompson-Cox-Hastings with FCJ asymmetry correction',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py
new file mode 100644
index 00000000..6e4f29c8
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py
@@ -0,0 +1,314 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+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
+multiple inheritance.
+"""
+
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class CwlBroadeningMixin:
+ """CWL Gaussian and Lorentz broadening parameters."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._broad_gauss_u: Parameter = Parameter(
+ name='broad_gauss_u',
+ description='Gaussian broadening from sample size and resolution',
+ units='deg²',
+ value_spec=AttributeSpec(
+ default=0.01,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.broad_gauss_u']),
+ )
+ self._broad_gauss_v: Parameter = Parameter(
+ name='broad_gauss_v',
+ description='Gaussian broadening instrumental contribution',
+ units='deg²',
+ value_spec=AttributeSpec(
+ default=-0.01,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.broad_gauss_v']),
+ )
+ self._broad_gauss_w: Parameter = Parameter(
+ name='broad_gauss_w',
+ description='Gaussian broadening instrumental contribution',
+ units='deg²',
+ value_spec=AttributeSpec(
+ default=0.02,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.broad_gauss_w']),
+ )
+ self._broad_lorentz_x: Parameter = Parameter(
+ name='broad_lorentz_x',
+ description='Lorentzian broadening from sample strain effects',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.broad_lorentz_x']),
+ )
+ self._broad_lorentz_y: Parameter = Parameter(
+ name='broad_lorentz_y',
+ description='Lorentzian broadening from microstructural defects',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.broad_lorentz_y']),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def broad_gauss_u(self) -> Parameter:
+ """
+ Gaussian broadening from sample size and 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:
+ self._broad_gauss_u.value = value
+
+ @property
+ def broad_gauss_v(self) -> Parameter:
+ """
+ Gaussian broadening instrumental 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:
+ self._broad_gauss_v.value = value
+
+ @property
+ def broad_gauss_w(self) -> Parameter:
+ """
+ Gaussian broadening instrumental 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:
+ self._broad_gauss_w.value = value
+
+ @property
+ def broad_lorentz_x(self) -> Parameter:
+ """
+ Lorentzian broadening (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:
+ self._broad_lorentz_x.value = value
+
+ @property
+ def broad_lorentz_y(self) -> Parameter:
+ """
+ Lorentzian broadening from microstructural defects (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:
+ self._broad_lorentz_y.value = value
+
+
+class EmpiricalAsymmetryMixin:
+ """Empirical CWL peak asymmetry parameters."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._asym_empir_1: Parameter = Parameter(
+ name='asym_empir_1',
+ description='Empirical asymmetry coefficient p1',
+ units='',
+ value_spec=AttributeSpec(
+ default=0.1,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_empir_1']),
+ )
+ self._asym_empir_2: Parameter = Parameter(
+ name='asym_empir_2',
+ description='Empirical asymmetry coefficient p2',
+ units='',
+ value_spec=AttributeSpec(
+ default=0.2,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_empir_2']),
+ )
+ self._asym_empir_3: Parameter = Parameter(
+ name='asym_empir_3',
+ description='Empirical asymmetry coefficient p3',
+ units='',
+ value_spec=AttributeSpec(
+ default=0.3,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_empir_3']),
+ )
+ self._asym_empir_4: Parameter = Parameter(
+ name='asym_empir_4',
+ description='Empirical asymmetry coefficient p4',
+ units='',
+ value_spec=AttributeSpec(
+ default=0.4,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_empir_4']),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ 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.
+ """
+ return self._asym_empir_1
+
+ @asym_empir_1.setter
+ def asym_empir_1(self, value: float) -> None:
+ self._asym_empir_1.value = value
+
+ @property
+ 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.
+ """
+ return self._asym_empir_2
+
+ @asym_empir_2.setter
+ def asym_empir_2(self, value: float) -> None:
+ self._asym_empir_2.value = value
+
+ @property
+ 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.
+ """
+ return self._asym_empir_3
+
+ @asym_empir_3.setter
+ def asym_empir_3(self, value: float) -> None:
+ self._asym_empir_3.value = value
+
+ @property
+ 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.
+ """
+ return self._asym_empir_4
+
+ @asym_empir_4.setter
+ def asym_empir_4(self, value: float) -> None:
+ self._asym_empir_4.value = value
+
+
+class FcjAsymmetryMixin:
+ """Finger–Cox–Jephcoat (FCJ) asymmetry parameters."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._asym_fcj_1: Parameter = Parameter(
+ name='asym_fcj_1',
+ description='Finger-Cox-Jephcoat asymmetry parameter 1',
+ units='',
+ value_spec=AttributeSpec(
+ default=0.01,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_fcj_1']),
+ )
+ self._asym_fcj_2: Parameter = Parameter(
+ name='asym_fcj_2',
+ description='Finger-Cox-Jephcoat asymmetry parameter 2',
+ units='',
+ value_spec=AttributeSpec(
+ default=0.02,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_fcj_2']),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ 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.
+ """
+ return self._asym_fcj_1
+
+ @asym_fcj_1.setter
+ def asym_fcj_1(self, value: float) -> None:
+ self._asym_fcj_1.value = value
+
+ @property
+ 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.
+ """
+ return self._asym_fcj_2
+
+ @asym_fcj_2.setter
+ def asym_fcj_2(self, value: float) -> None:
+ self._asym_fcj_2.value = value
diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/factory.py b/src/easydiffraction/datablocks/experiment/categories/peak/factory.py
new file mode 100644
index 00000000..ca196748
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/factory.py
@@ -0,0 +1,26 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Peak profile factory — delegates to ``FactoryBase``."""
+
+from easydiffraction.core.factory import FactoryBase
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import PeakProfileTypeEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+
+
+class PeakFactory(FactoryBase):
+ """Factory for creating peak profile objects."""
+
+ _default_rules = {
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('beam_mode', BeamModeEnum.CONSTANT_WAVELENGTH),
+ }): PeakProfileTypeEnum.PSEUDO_VOIGT,
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('beam_mode', BeamModeEnum.TIME_OF_FLIGHT),
+ }): PeakProfileTypeEnum.PSEUDO_VOIGT_IKEDA_CARPENTER,
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.TOTAL),
+ }): PeakProfileTypeEnum.GAUSSIAN_DAMPED_SINC,
+ }
diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py
new file mode 100644
index 00000000..59c0b9e3
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof.py
@@ -0,0 +1,87 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Time-of-flight peak profile classes."""
+
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.categories.peak.base import PeakBase
+from easydiffraction.datablocks.experiment.categories.peak.factory import PeakFactory
+from easydiffraction.datablocks.experiment.categories.peak.tof_mixins import (
+ IkedaCarpenterAsymmetryMixin,
+)
+from easydiffraction.datablocks.experiment.categories.peak.tof_mixins import TofBroadeningMixin
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+
+
+@PeakFactory.register
+class TofPseudoVoigt(
+ PeakBase,
+ TofBroadeningMixin,
+):
+ """Time-of-flight pseudo-Voigt peak shape."""
+
+ 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}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+
+@PeakFactory.register
+class TofPseudoVoigtIkedaCarpenter(
+ PeakBase,
+ TofBroadeningMixin,
+ IkedaCarpenterAsymmetryMixin,
+):
+ """TOF pseudo-Voigt with Ikeda–Carpenter asymmetry."""
+
+ type_info = TypeInfo(
+ tag='pseudo-voigt * ikeda-carpenter',
+ description='Pseudo-Voigt with Ikeda-Carpenter asymmetry correction',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
+
+
+@PeakFactory.register
+class TofPseudoVoigtBackToBack(
+ PeakBase,
+ TofBroadeningMixin,
+ IkedaCarpenterAsymmetryMixin,
+):
+ """TOF back-to-back pseudo-Voigt with asymmetry."""
+
+ type_info = TypeInfo(
+ tag='pseudo-voigt * back-to-back',
+ description='TOF back-to-back pseudo-Voigt with asymmetry',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.CRYSPY, CalculatorEnum.CRYSFML}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py
new file mode 100644
index 00000000..8093d877
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py
@@ -0,0 +1,276 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+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
+module provides classes that add broadening and asymmetry parameters.
+They are composed into concrete peak classes elsewhere via multiple
+inheritance.
+"""
+
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class TofBroadeningMixin:
+ """TOF Gaussian/Lorentz broadening and mixing parameters."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._broad_gauss_sigma_0 = Parameter(
+ name='gauss_sigma_0',
+ description='Gaussian broadening (instrumental resolution)',
+ units='µs²',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.gauss_sigma_0']),
+ )
+ self._broad_gauss_sigma_1 = Parameter(
+ name='gauss_sigma_1',
+ description='Gaussian broadening (dependent on d-spacing)',
+ units='µs/Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.gauss_sigma_1']),
+ )
+ self._broad_gauss_sigma_2 = Parameter(
+ name='gauss_sigma_2',
+ description='Gaussian broadening (instrument-dependent term)',
+ units='µs²/Ų',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.gauss_sigma_2']),
+ )
+ self._broad_lorentz_gamma_0 = Parameter(
+ name='lorentz_gamma_0',
+ description='Lorentzian broadening (microstrain effects)',
+ units='µs',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.lorentz_gamma_0']),
+ )
+ self._broad_lorentz_gamma_1 = Parameter(
+ name='lorentz_gamma_1',
+ description='Lorentzian broadening (dependent on d-spacing)',
+ units='µs/Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.lorentz_gamma_1']),
+ )
+ self._broad_lorentz_gamma_2 = Parameter(
+ name='lorentz_gamma_2',
+ description='Lorentzian broadening (instrument-dependent term)',
+ units='µs²/Ų',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.lorentz_gamma_2']),
+ )
+ self._broad_mix_beta_0 = Parameter(
+ name='mix_beta_0',
+ description='Ratio of Gaussian to Lorentzian contributions',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.mix_beta_0']),
+ )
+ self._broad_mix_beta_1 = Parameter(
+ name='mix_beta_1',
+ description='Ratio of Gaussian to Lorentzian contributions',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.mix_beta_1']),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def broad_gauss_sigma_0(self) -> Parameter:
+ """
+ Gaussian broadening (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:
+ self._broad_gauss_sigma_0.value = value
+
+ @property
+ def broad_gauss_sigma_1(self) -> Parameter:
+ """
+ Gaussian broadening (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:
+ self._broad_gauss_sigma_1.value = value
+
+ @property
+ def broad_gauss_sigma_2(self) -> Parameter:
+ """
+ Gaussian broadening (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:
+ self._broad_gauss_sigma_2.value = value
+
+ @property
+ def broad_lorentz_gamma_0(self) -> Parameter:
+ """
+ Lorentzian broadening (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:
+ self._broad_lorentz_gamma_0.value = value
+
+ @property
+ def broad_lorentz_gamma_1(self) -> Parameter:
+ """
+ Lorentzian broadening (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:
+ self._broad_lorentz_gamma_1.value = value
+
+ @property
+ def broad_lorentz_gamma_2(self) -> Parameter:
+ """
+ Lorentzian broadening (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:
+ self._broad_lorentz_gamma_2.value = value
+
+ @property
+ def broad_mix_beta_0(self) -> Parameter:
+ """
+ Ratio of Gaussian to Lorentzian contributions (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:
+ self._broad_mix_beta_0.value = value
+
+ @property
+ def broad_mix_beta_1(self) -> Parameter:
+ """
+ Ratio of Gaussian to Lorentzian contributions (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:
+ self._broad_mix_beta_1.value = value
+
+
+class IkedaCarpenterAsymmetryMixin:
+ """Ikeda–Carpenter asymmetry parameters."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._asym_alpha_0 = Parameter(
+ name='asym_alpha_0',
+ description='Ikeda-Carpenter asymmetry parameter α₀',
+ units='', # TODO
+ value_spec=AttributeSpec(
+ default=0.01,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_alpha_0']),
+ )
+ self._asym_alpha_1 = Parameter(
+ name='asym_alpha_1',
+ description='Ikeda-Carpenter asymmetry parameter α₁',
+ units='', # TODO
+ value_spec=AttributeSpec(
+ default=0.02,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.asym_alpha_1']),
+ )
+
+ @property
+ 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.
+ """
+ return self._asym_alpha_0
+
+ @asym_alpha_0.setter
+ def asym_alpha_0(self, value: float) -> None:
+ self._asym_alpha_0.value = value
+
+ @property
+ 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.
+ """
+ return self._asym_alpha_1
+
+ @asym_alpha_1.setter
+ def asym_alpha_1(self, value: float) -> None:
+ self._asym_alpha_1.value = value
diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total.py b/src/easydiffraction/datablocks/experiment/categories/peak/total.py
new file mode 100644
index 00000000..61d5e6fd
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/total.py
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Total-scattering (PDF) peak profile classes."""
+
+from easydiffraction.core.metadata import CalculatorSupport
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.categories.peak.base import PeakBase
+from easydiffraction.datablocks.experiment.categories.peak.factory import PeakFactory
+from easydiffraction.datablocks.experiment.categories.peak.total_mixins import TotalBroadeningMixin
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+
+
+@PeakFactory.register
+class TotalGaussianDampedSinc(
+ PeakBase,
+ TotalBroadeningMixin,
+):
+ """Gaussian-damped sinc peak for total scattering (PDF)."""
+
+ type_info = TypeInfo(
+ tag='gaussian-damped-sinc',
+ description='Gaussian-damped sinc for pair distribution function analysis',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.TOTAL}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH, BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+ calculator_support = CalculatorSupport(
+ calculators=frozenset({CalculatorEnum.PDFFIT}),
+ )
+
+ def __init__(self) -> None:
+ super().__init__()
diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py
new file mode 100644
index 00000000..bae2c974
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py
@@ -0,0 +1,170 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+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
+multiple inheritance.
+"""
+
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class TotalBroadeningMixin:
+ """PDF broadening/damping/sharpening parameters."""
+
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._damp_q = Parameter(
+ name='damp_q',
+ description='Q-resolution damping for high-r PDF peak amplitude',
+ units='Å⁻¹',
+ value_spec=AttributeSpec(
+ default=0.05,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.damp_q']),
+ )
+ self._broad_q = Parameter(
+ name='broad_q',
+ description='Quadratic peak broadening from thermal uncertainty',
+ units='Å⁻²',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.broad_q']),
+ )
+ self._cutoff_q = Parameter(
+ name='cutoff_q',
+ description='Q-value cutoff for Fourier transform',
+ units='Å⁻¹',
+ value_spec=AttributeSpec(
+ default=25.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.cutoff_q']),
+ )
+ self._sharp_delta_1 = Parameter(
+ name='sharp_delta_1',
+ description='Peak sharpening coefficient (1/r dependence)',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.sharp_delta_1']),
+ )
+ self._sharp_delta_2 = Parameter(
+ name='sharp_delta_2',
+ description='Peak sharpening coefficient (1/r² dependence)',
+ units='Ų',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.sharp_delta_2']),
+ )
+ self._damp_particle_diameter = Parameter(
+ name='damp_particle_diameter',
+ description='Particle diameter for spherical envelope damping correction',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_peak.damp_particle_diameter']),
+ )
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def damp_q(self) -> Parameter:
+ """
+ Q-resolution damping for 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:
+ self._damp_q.value = value
+
+ @property
+ def broad_q(self) -> Parameter:
+ """
+ Quadratic peak broadening from thermal uncertainty (Å⁻²).
+
+ 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:
+ self._broad_q.value = value
+
+ @property
+ def cutoff_q(self) -> Parameter:
+ """
+ Q-value cutoff for Fourier transform (Å⁻¹).
+
+ 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:
+ self._cutoff_q.value = value
+
+ @property
+ 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.
+ """
+ return self._sharp_delta_1
+
+ @sharp_delta_1.setter
+ def sharp_delta_1(self, value: float) -> None:
+ self._sharp_delta_1.value = value
+
+ @property
+ 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.
+ """
+ return self._sharp_delta_2
+
+ @sharp_delta_2.setter
+ def sharp_delta_2(self, value: float) -> None:
+ self._sharp_delta_2.value = value
+
+ @property
+ def damp_particle_diameter(self) -> Parameter:
+ """
+ Particle diameter for spherical envelope damping correction (Å).
+
+ 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:
+ self._damp_particle_diameter.value = value
diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py
new file mode 100644
index 00000000..fb30d013
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/collection.py
@@ -0,0 +1,157 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Collection of experiment data blocks."""
+
+from typeguard import typechecked
+
+from easydiffraction.core.datablock import DatablockCollection
+from easydiffraction.datablocks.experiment.item.base import ExperimentBase
+from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory
+from easydiffraction.utils.enums import VerbosityEnum
+from easydiffraction.utils.logging import console
+
+
+class Experiments(DatablockCollection):
+ """
+ Collection of Experiment data blocks.
+
+ Provides convenience constructors for common creation patterns and
+ helper methods for simple presentation of collection contents.
+ """
+
+ def __init__(self) -> None:
+ super().__init__(item_type=ExperimentBase)
+
+ # ------------------------------------------------------------------
+ # Public methods
+ # ------------------------------------------------------------------
+
+ # TODO: Make abstract in DatablockCollection?
+ @typechecked
+ def create(
+ self,
+ *,
+ name: str,
+ sample_form: str | None = None,
+ beam_mode: str | None = None,
+ radiation_probe: str | None = None,
+ scattering_type: str | None = None,
+ ) -> None:
+ """
+ Add an experiment without associating a data file.
+
+ Parameters
+ ----------
+ name : str
+ Experiment identifier.
+ sample_form : str | None, default=None
+ Sample form (e.g. ``'powder'``).
+ beam_mode : str | None, default=None
+ Beam mode (e.g. ``'constant wavelength'``).
+ radiation_probe : str | None, default=None
+ Radiation probe (e.g. ``'neutron'``).
+ scattering_type : str | None, default=None
+ Scattering type (e.g. ``'bragg'``).
+ """
+ experiment = ExperimentFactory.from_scratch(
+ name=name,
+ sample_form=sample_form,
+ beam_mode=beam_mode,
+ radiation_probe=radiation_probe,
+ scattering_type=scattering_type,
+ )
+ self.add(experiment)
+
+ # TODO: Move to DatablockCollection?
+ @typechecked
+ def add_from_cif_str(
+ self,
+ cif_str: str,
+ ) -> None:
+ """
+ Add an experiment from a CIF string.
+
+ Parameters
+ ----------
+ cif_str : str
+ Full CIF document as a string.
+ """
+ experiment = ExperimentFactory.from_cif_str(cif_str)
+ self.add(experiment)
+
+ # TODO: Move to DatablockCollection?
+ @typechecked
+ def add_from_cif_path(
+ self,
+ cif_path: str,
+ ) -> None:
+ """
+ Add an experiment from a CIF file path.
+
+ Parameters
+ ----------
+ cif_path : str
+ Path to a CIF document.
+ """
+ experiment = ExperimentFactory.from_cif_path(cif_path)
+ self.add(experiment)
+
+ @typechecked
+ def add_from_data_path(
+ self,
+ *,
+ name: str,
+ data_path: str,
+ sample_form: str | None = None,
+ beam_mode: str | None = None,
+ radiation_probe: str | None = None,
+ scattering_type: str | None = None,
+ verbosity: str | None = None,
+ ) -> None:
+ """
+ Add an experiment from a data file path.
+
+ Parameters
+ ----------
+ name : str
+ Experiment identifier.
+ data_path : str
+ Path to the measured data file.
+ sample_form : str | None, default=None
+ Sample form (e.g. ``'powder'``).
+ beam_mode : str | None, default=None
+ Beam mode (e.g. ``'constant wavelength'``).
+ radiation_probe : str | None, default=None
+ Radiation probe (e.g. ``'neutron'``).
+ scattering_type : str | None, default=None
+ Scattering type (e.g. ``'bragg'``).
+ verbosity : str | None, default=None
+ Console output verbosity: ``'full'`` for multi-line output,
+ ``'short'`` for a one-line status message, or ``'silent'``
+ for no output. When ``None``, uses ``project.verbosity``.
+ """
+ if verbosity is None and self._parent is not None:
+ verbosity = self._parent.verbosity
+ verb = VerbosityEnum(verbosity) if verbosity is not None else VerbosityEnum.FULL
+ experiment = ExperimentFactory.from_data_path(
+ name=name,
+ data_path=data_path,
+ sample_form=sample_form,
+ beam_mode=beam_mode,
+ radiation_probe=radiation_probe,
+ scattering_type=scattering_type,
+ verbosity=verb,
+ )
+ self.add(experiment)
+
+ # TODO: Move to DatablockCollection?
+ def show_names(self) -> None:
+ """List all experiment names in the collection."""
+ console.paragraph('Defined experiments' + ' 🔬')
+ console.print(self.names)
+
+ # TODO: Move to DatablockCollection?
+ def show_params(self) -> None:
+ """Show parameters of all experiments in the collection."""
+ for experiment in self.values():
+ experiment.show_params()
diff --git a/src/easydiffraction/datablocks/experiment/item/__init__.py b/src/easydiffraction/datablocks/experiment/item/__init__.py
new file mode 100644
index 00000000..35a64fb1
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/item/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.experiment.item.base import ExperimentBase
+from easydiffraction.datablocks.experiment.item.base import PdExperimentBase
+from easydiffraction.datablocks.experiment.item.bragg_pd import BraggPdExperiment
+from easydiffraction.datablocks.experiment.item.bragg_sc import CwlScExperiment
+from easydiffraction.datablocks.experiment.item.bragg_sc import TofScExperiment
+from easydiffraction.datablocks.experiment.item.total_pd import TotalPdExperiment
diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py
new file mode 100644
index 00000000..5c426a94
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/item/base.py
@@ -0,0 +1,768 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Base classes for experiment datablock items."""
+
+from __future__ import annotations
+
+from abc import abstractmethod
+from typing import TYPE_CHECKING
+from typing import Any
+from typing import List
+
+from easydiffraction.core.datablock import DatablockItem
+from easydiffraction.datablocks.experiment.categories.data.factory import DataFactory
+from easydiffraction.datablocks.experiment.categories.diffrn.factory import DiffrnFactory
+from easydiffraction.datablocks.experiment.categories.excluded_regions.factory import (
+ ExcludedRegionsFactory,
+)
+from easydiffraction.datablocks.experiment.categories.extinction.factory import ExtinctionFactory
+from easydiffraction.datablocks.experiment.categories.instrument.factory import InstrumentFactory
+from easydiffraction.datablocks.experiment.categories.linked_crystal.factory import (
+ LinkedCrystalFactory,
+)
+from easydiffraction.datablocks.experiment.categories.linked_phases.factory import (
+ LinkedPhasesFactory,
+)
+from easydiffraction.datablocks.experiment.categories.peak.factory import PeakFactory
+from easydiffraction.io.cif.serialize import experiment_to_cif
+from easydiffraction.utils.logging import console
+from easydiffraction.utils.logging import log
+from easydiffraction.utils.utils import render_cif
+
+if TYPE_CHECKING:
+ from easydiffraction.datablocks.experiment.categories.experiment_type import ExperimentType
+ from easydiffraction.datablocks.structure.collection import Structures
+
+
+class ExperimentBase(DatablockItem):
+ """Base class for all experiment datablock items."""
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ type: ExperimentType,
+ ) -> None:
+ super().__init__()
+ self._name = name
+ self._type = type
+ self._calculator = None
+ self._calculator_type: str | None = None
+ self._identity.datablock_entry_name = lambda: self.name
+
+ self._diffrn_type: str = DiffrnFactory.default_tag()
+ self._diffrn = DiffrnFactory.create(self._diffrn_type)
+
+ @property
+ def name(self) -> str:
+ """Human-readable name of the experiment."""
+ return self._name
+
+ @name.setter
+ def name(self, new: str) -> None:
+ """
+ Rename the experiment.
+
+ Parameters
+ ----------
+ new : str
+ New name for this experiment.
+ """
+ self._name = new
+
+ @property
+ def type(self) -> object: # TODO: Consider another name
+ """Experiment type: sample form, probe, beam mode."""
+ return self._type
+
+ # ------------------------------------------------------------------
+ # Diffrn conditions (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def diffrn(self) -> object:
+ """Ambient conditions recorded during measurement."""
+ return self._diffrn
+
+ @property
+ def diffrn_type(self) -> str:
+ """Tag of the active diffraction conditions type."""
+ return self._diffrn_type
+
+ @diffrn_type.setter
+ def diffrn_type(self, new_type: str) -> None:
+ """
+ Switch to a different diffraction conditions type.
+
+ Parameters
+ ----------
+ new_type : str
+ Diffrn conditions tag (e.g. ``'default'``).
+ """
+ supported_tags = DiffrnFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported diffrn type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_diffrn_types()'",
+ )
+ return
+
+ self._diffrn = DiffrnFactory.create(new_type)
+ self._diffrn_type = new_type
+ console.paragraph(f"Diffrn type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_diffrn_types(self) -> None:
+ """Print a table of supported diffraction conditions types."""
+ DiffrnFactory.show_supported()
+
+ def show_current_diffrn_type(self) -> None:
+ """Print the currently used diffraction conditions type."""
+ console.paragraph('Current diffrn type')
+ console.print(self.diffrn_type)
+
+ @property
+ def as_cif(self) -> str:
+ """Serialize this experiment to a CIF fragment."""
+ return experiment_to_cif(self)
+
+ def show_as_cif(self) -> None:
+ """Pretty-print the experiment as CIF text."""
+ experiment_cif = super().as_cif
+ paragraph_title: str = f"Experiment 🔬 '{self.name}' as cif"
+ console.paragraph(paragraph_title)
+ render_cif(experiment_cif)
+
+ @abstractmethod
+ def _load_ascii_data_to_experiment(self, data_path: str) -> None:
+ """
+ Load ASCII data from file into the experiment data category.
+
+ Parameters
+ ----------
+ data_path : str
+ Path to the ASCII file to load.
+
+ Raises
+ ------
+ NotImplementedError
+ Subclasses must implement this method.
+ """
+ raise NotImplementedError()
+
+ # ------------------------------------------------------------------
+ # Calculator (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def calculator(self) -> object:
+ """
+ The active calculator instance for this experiment.
+
+ Auto-resolved on first access from the experiment's data
+ category ``calculator_support`` and
+ ``CalculatorFactory._default_rules``.
+ """
+ if self._calculator is None:
+ self._resolve_calculator()
+ return self._calculator
+
+ @property
+ def calculator_type(self) -> str:
+ """Tag of the active calculator backend (e.g. ``'cryspy'``)."""
+ if self._calculator_type is None:
+ self._resolve_calculator()
+ return self._calculator_type
+
+ @calculator_type.setter
+ def calculator_type(self, tag: str) -> None:
+ """
+ Switch to a different calculator backend.
+
+ Parameters
+ ----------
+ tag : str
+ Calculator tag (e.g. ``'cryspy'``, ``'crysfml'``,
+ ``'pdffit'``).
+ """
+ from easydiffraction.analysis.calculators.factory import CalculatorFactory
+
+ supported = self._supported_calculator_tags()
+ if tag not in supported:
+ log.warning(
+ f"Unsupported calculator '{tag}' for experiment "
+ f"'{self.name}'. Supported: {supported}. "
+ f"For more information, use 'show_supported_calculator_types()'",
+ )
+ return
+ self._calculator = CalculatorFactory.create(tag)
+ self._calculator_type = tag
+ console.paragraph(f"Calculator for experiment '{self.name}' changed to")
+ console.print(tag)
+
+ def show_supported_calculator_types(self) -> None:
+ """Print a table of supported calculator backends."""
+ from easydiffraction.analysis.calculators.factory import CalculatorFactory
+
+ supported_tags = self._supported_calculator_tags()
+ all_classes = CalculatorFactory._supported_map()
+ columns_headers = ['Type', 'Description']
+ columns_alignment = ['left', 'left']
+ columns_data = [
+ [cls.type_info.tag, cls.type_info.description]
+ for tag, cls in all_classes.items()
+ if tag in supported_tags
+ ]
+ from easydiffraction.utils.utils import render_table
+
+ console.paragraph('Supported calculator types')
+ render_table(
+ columns_headers=columns_headers,
+ columns_alignment=columns_alignment,
+ columns_data=columns_data,
+ )
+
+ def show_current_calculator_type(self) -> None:
+ """Print the name of the currently active calculator."""
+ console.paragraph('Current calculator type')
+ console.print(self.calculator_type)
+
+ def _resolve_calculator(self) -> None:
+ """Auto-resolve the default calculator from data category."""
+ from easydiffraction.analysis.calculators.factory import CalculatorFactory
+
+ tag = CalculatorFactory.default_tag(
+ scattering_type=self.type.scattering_type.value,
+ )
+ supported = self._supported_calculator_tags()
+ if supported and tag not in supported:
+ tag = supported[0]
+ self._calculator = CalculatorFactory.create(tag)
+ self._calculator_type = tag
+
+ def _supported_calculator_tags(self) -> list[str]:
+ """
+ Return calculator tags supported by this experiment.
+
+ Intersects the data category's ``calculator_support`` with
+ calculators whose engines are importable.
+ """
+ from easydiffraction.analysis.calculators.factory import CalculatorFactory
+
+ available = CalculatorFactory.supported_tags()
+ data = getattr(self, '_data', None)
+ if data is not None:
+ data_support = getattr(data, 'calculator_support', None)
+ if data_support and data_support.calculators:
+ return [t for t in available if t in data_support.calculators]
+ return available
+
+
+class ScExperimentBase(ExperimentBase):
+ """Base class for all single crystal experiments."""
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ type: ExperimentType,
+ ) -> None:
+ super().__init__(name=name, type=type)
+
+ self._extinction_type: str = ExtinctionFactory.default_tag()
+ self._extinction = ExtinctionFactory.create(self._extinction_type)
+ self._linked_crystal_type: str = LinkedCrystalFactory.default_tag()
+ self._linked_crystal = LinkedCrystalFactory.create(self._linked_crystal_type)
+ self._instrument_type: str = InstrumentFactory.default_tag(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ sample_form=self.type.sample_form.value,
+ )
+ self._instrument = InstrumentFactory.create(self._instrument_type)
+ self._data_type: str = DataFactory.default_tag(
+ sample_form=self.type.sample_form.value,
+ beam_mode=self.type.beam_mode.value,
+ scattering_type=self.type.scattering_type.value,
+ )
+ self._data = DataFactory.create(self._data_type)
+
+ @abstractmethod
+ def _load_ascii_data_to_experiment(self, data_path: str) -> None:
+ """
+ Load single crystal data from an ASCII file.
+
+ Parameters
+ ----------
+ data_path : str
+ Path to data file with columns compatible with the beam
+ mode.
+ """
+ pass
+
+ # ------------------------------------------------------------------
+ # Extinction (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def extinction(self) -> object:
+ """Active extinction correction model."""
+ return self._extinction
+
+ @property
+ def extinction_type(self) -> str:
+ """Tag of the active extinction correction model."""
+ return self._extinction_type
+
+ @extinction_type.setter
+ def extinction_type(self, new_type: str) -> None:
+ """
+ Switch to a different extinction correction model.
+
+ Parameters
+ ----------
+ new_type : str
+ Extinction tag (e.g. ``'shelx'``).
+ """
+ supported_tags = ExtinctionFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported extinction type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_extinction_types()'",
+ )
+ return
+
+ self._extinction = ExtinctionFactory.create(new_type)
+ self._extinction_type = new_type
+ console.paragraph(f"Extinction type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_extinction_types(self) -> None:
+ """Print a table of supported extinction correction models."""
+ ExtinctionFactory.show_supported()
+
+ def show_current_extinction_type(self) -> None:
+ """Print the currently used extinction correction model."""
+ console.paragraph('Current extinction type')
+ console.print(self.extinction_type)
+
+ # ------------------------------------------------------------------
+ # Linked crystal (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def linked_crystal(self) -> object:
+ """Linked crystal model for this experiment."""
+ return self._linked_crystal
+
+ @property
+ def linked_crystal_type(self) -> str:
+ """Tag of the active linked-crystal reference type."""
+ return self._linked_crystal_type
+
+ @linked_crystal_type.setter
+ def linked_crystal_type(self, new_type: str) -> None:
+ """
+ Switch to a different linked-crystal reference type.
+
+ Parameters
+ ----------
+ new_type : str
+ Linked-crystal tag (e.g. ``'default'``).
+ """
+ supported_tags = LinkedCrystalFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported linked crystal type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_linked_crystal_types()'",
+ )
+ return
+
+ self._linked_crystal = LinkedCrystalFactory.create(new_type)
+ self._linked_crystal_type = new_type
+ console.paragraph(f"Linked crystal type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_linked_crystal_types(self) -> None:
+ """Print a table of supported linked-crystal reference types."""
+ LinkedCrystalFactory.show_supported()
+
+ def show_current_linked_crystal_type(self) -> None:
+ """Print the currently used linked-crystal reference type."""
+ console.paragraph('Current linked crystal type')
+ console.print(self.linked_crystal_type)
+
+ # ------------------------------------------------------------------
+ # Instrument (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def instrument(self) -> object:
+ """Active instrument model for this experiment."""
+ return self._instrument
+
+ @property
+ def instrument_type(self) -> str:
+ """Tag of the active instrument type."""
+ return self._instrument_type
+
+ @instrument_type.setter
+ def instrument_type(self, new_type: str) -> None:
+ """
+ Switch to a different instrument type.
+
+ Parameters
+ ----------
+ new_type : str
+ Instrument tag (e.g. ``'cwl-sc'``).
+ """
+ supported = InstrumentFactory.supported_for(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ sample_form=self.type.sample_form.value,
+ )
+ supported_tags = [k.type_info.tag for k in supported]
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported instrument type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_instrument_types()'",
+ )
+ return
+ self._instrument = InstrumentFactory.create(new_type)
+ self._instrument_type = new_type
+ console.paragraph(f"Instrument type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_instrument_types(self) -> None:
+ """Print a table of supported instrument types."""
+ InstrumentFactory.show_supported(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ sample_form=self.type.sample_form.value,
+ )
+
+ def show_current_instrument_type(self) -> None:
+ """Print the currently used instrument type."""
+ console.paragraph('Current instrument type')
+ console.print(self.instrument_type)
+
+ # ------------------------------------------------------------------
+ # Data (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def data(self) -> object:
+ """Data collection for this experiment."""
+ return self._data
+
+ @property
+ def data_type(self) -> str:
+ """Tag of the active data collection type."""
+ return self._data_type
+
+ @data_type.setter
+ def data_type(self, new_type: str) -> None:
+ """
+ Switch to a different data collection type.
+
+ Parameters
+ ----------
+ new_type : str
+ Data tag (e.g. ``'bragg-sc'``).
+ """
+ supported_tags = DataFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported data type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_data_types()'",
+ )
+ return
+ self._data = DataFactory.create(new_type)
+ self._data_type = new_type
+ console.paragraph(f"Data type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_data_types(self) -> None:
+ """Print a table of supported data collection types."""
+ DataFactory.show_supported()
+
+ def show_current_data_type(self) -> None:
+ """Print the currently used data collection type."""
+ console.paragraph('Current data type')
+ console.print(self.data_type)
+
+
+class PdExperimentBase(ExperimentBase):
+ """Base class for all powder experiments."""
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ type: ExperimentType,
+ ) -> None:
+ super().__init__(name=name, type=type)
+
+ self._linked_phases_type: str = LinkedPhasesFactory.default_tag()
+ self._linked_phases = LinkedPhasesFactory.create(self._linked_phases_type)
+ self._excluded_regions_type: str = ExcludedRegionsFactory.default_tag()
+ self._excluded_regions = ExcludedRegionsFactory.create(self._excluded_regions_type)
+ self._peak_profile_type: str = PeakFactory.default_tag(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ )
+ self._data_type: str = DataFactory.default_tag(
+ sample_form=self.type.sample_form.value,
+ beam_mode=self.type.beam_mode.value,
+ scattering_type=self.type.scattering_type.value,
+ )
+ self._data = DataFactory.create(self._data_type)
+ self._peak = PeakFactory.create(self._peak_profile_type)
+
+ def _get_valid_linked_phases(
+ self,
+ structures: Structures,
+ ) -> List[Any]:
+ """
+ Get valid linked phases for this experiment.
+
+ Parameters
+ ----------
+ structures : Structures
+ Collection of structures.
+
+ Returns
+ -------
+ List[Any]
+ A list of valid linked phases.
+ """
+ if not self.linked_phases:
+ print('Warning: No linked phases defined. Returning empty pattern.')
+ return []
+
+ valid_linked_phases = []
+ for linked_phase in self.linked_phases:
+ if linked_phase._identity.category_entry_name not in structures.names:
+ print(
+ f"Warning: Linked phase '{linked_phase.id.value}' not "
+ f'found in Structures {structures.names}. Skipping it.'
+ )
+ continue
+ valid_linked_phases.append(linked_phase)
+
+ if not valid_linked_phases:
+ print(
+ 'Warning: None of the linked phases found in Structures. Returning empty pattern.'
+ )
+
+ return valid_linked_phases
+
+ @abstractmethod
+ def _load_ascii_data_to_experiment(self, data_path: str) -> int:
+ """
+ Load powder diffraction data from an ASCII file.
+
+ Parameters
+ ----------
+ data_path : str
+ Path to data file with columns compatible with the beam mode
+ (e.g. 2θ/I/σ for CWL, TOF/I/σ for TOF).
+
+ Returns
+ -------
+ int
+ Number of loaded data points.
+ """
+ pass
+
+ @property
+ def linked_phases(self) -> object:
+ """Collection of phases linked to this experiment."""
+ return self._linked_phases
+
+ @property
+ def linked_phases_type(self) -> str:
+ """Tag of the active linked-phases collection type."""
+ return self._linked_phases_type
+
+ @linked_phases_type.setter
+ def linked_phases_type(self, new_type: str) -> None:
+ """
+ Switch to a different linked-phases collection type.
+
+ Parameters
+ ----------
+ new_type : str
+ Linked-phases tag (e.g. ``'default'``).
+ """
+ supported_tags = LinkedPhasesFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported linked phases type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_linked_phases_types()'",
+ )
+ return
+
+ self._linked_phases = LinkedPhasesFactory.create(new_type)
+ self._linked_phases_type = new_type
+ console.paragraph(f"Linked phases type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_linked_phases_types(self) -> None:
+ """Print a table of supported linked-phases collection types."""
+ LinkedPhasesFactory.show_supported()
+
+ def show_current_linked_phases_type(self) -> None:
+ """Print the currently used linked-phases collection type."""
+ console.paragraph('Current linked phases type')
+ console.print(self.linked_phases_type)
+
+ @property
+ def excluded_regions(self) -> object:
+ """Collection of excluded regions for the x-grid."""
+ return self._excluded_regions
+
+ @property
+ def excluded_regions_type(self) -> str:
+ """Tag of the active excluded-regions collection type."""
+ return self._excluded_regions_type
+
+ @excluded_regions_type.setter
+ def excluded_regions_type(self, new_type: str) -> None:
+ """
+ Switch to a different excluded-regions collection type.
+
+ Parameters
+ ----------
+ new_type : str
+ Excluded-regions tag (e.g. ``'default'``).
+ """
+ supported_tags = ExcludedRegionsFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported excluded regions type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_excluded_regions_types()'",
+ )
+ return
+
+ self._excluded_regions = ExcludedRegionsFactory.create(new_type)
+ self._excluded_regions_type = new_type
+ console.paragraph(f"Excluded regions type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_excluded_regions_types(self) -> None:
+ """Print a table of supported excluded-regions types."""
+ ExcludedRegionsFactory.show_supported()
+
+ def show_current_excluded_regions_type(self) -> None:
+ """Print the currently used excluded-regions collection type."""
+ console.paragraph('Current excluded regions type')
+ console.print(self.excluded_regions_type)
+
+ # ------------------------------------------------------------------
+ # Data (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def data(self) -> object:
+ """Data collection for this experiment."""
+ return self._data
+
+ @property
+ def data_type(self) -> str:
+ """Tag of the active data collection type."""
+ return self._data_type
+
+ @data_type.setter
+ def data_type(self, new_type: str) -> None:
+ """
+ Switch to a different data collection type.
+
+ Parameters
+ ----------
+ new_type : str
+ Data tag (e.g. ``'bragg-pd-cwl'``).
+ """
+ supported_tags = DataFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported data type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_data_types()'",
+ )
+ return
+ self._data = DataFactory.create(new_type)
+ self._data_type = new_type
+ console.paragraph(f"Data type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_data_types(self) -> None:
+ """Print a table of supported data collection types."""
+ DataFactory.show_supported()
+
+ def show_current_data_type(self) -> None:
+ """Print the currently used data collection type."""
+ console.paragraph('Current data type')
+ console.print(self.data_type)
+
+ @property
+ def peak(self) -> object:
+ """Peak category object with profile parameters and mixins."""
+ return self._peak
+
+ @property
+ 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) -> None:
+ """
+ Change the active peak profile type, if supported.
+
+ Parameters
+ ----------
+ new_type : str
+ New profile type as tag string.
+ """
+ supported = PeakFactory.supported_for(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ )
+ supported_tags = [k.type_info.tag for k in supported]
+
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported peak profile '{new_type}'. "
+ f'Supported peak profiles: {supported_tags}. '
+ f"For more information, use 'show_supported_peak_profile_types()'",
+ )
+ return
+
+ if self._peak is not None:
+ log.warning(
+ 'Switching peak profile type discards existing peak parameters.',
+ )
+
+ self._peak = PeakFactory.create(new_type)
+ self._peak_profile_type = new_type
+ console.paragraph(f"Peak profile type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ 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) -> 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
new file mode 100644
index 00000000..6da9ffe7
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py
@@ -0,0 +1,219 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import numpy as np
+
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.categories.background.factory import BackgroundFactory
+from easydiffraction.datablocks.experiment.categories.instrument.factory import InstrumentFactory
+from easydiffraction.datablocks.experiment.item.base import PdExperimentBase
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory
+from easydiffraction.io.ascii import load_numeric_block
+from easydiffraction.utils.logging import console
+from easydiffraction.utils.logging import log
+
+if TYPE_CHECKING:
+ from easydiffraction.datablocks.experiment.categories.experiment_type import ExperimentType
+
+
+@ExperimentFactory.register
+class BraggPdExperiment(PdExperimentBase):
+ """Standard Bragg powder diffraction experiment."""
+
+ type_info = TypeInfo(
+ tag='bragg-pd',
+ description='Bragg powder diffraction experiment',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH, BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ type: ExperimentType,
+ ) -> None:
+ super().__init__(name=name, type=type)
+
+ self._instrument_type: str = InstrumentFactory.default_tag(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ sample_form=self.type.sample_form.value,
+ )
+ self._instrument = InstrumentFactory.create(self._instrument_type)
+ self._background_type: str = BackgroundFactory.default_tag()
+ self._background = BackgroundFactory.create(self._background_type)
+
+ def _load_ascii_data_to_experiment(
+ self,
+ data_path: str,
+ ) -> int:
+ """
+ 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
+ ``sqrt(y)``.
+
+ If ``sy`` has values smaller than ``0.0001``, they are replaced
+ with ``1.0``.
+
+ Parameters
+ ----------
+ data_path : str
+ Path to the ASCII data file.
+
+ Returns
+ -------
+ int
+ Number of loaded data points.
+ """
+ data = load_numeric_block(data_path)
+
+ if data.shape[1] < 2:
+ log.error(
+ 'Data file must have at least two columns: x and y.',
+ exc_type=ValueError,
+ )
+ return 0
+
+ if data.shape[1] < 3:
+ log.warning('No uncertainty (sy) column provided. Defaulting to sqrt(y).')
+
+ # Extract x, y data
+ x: np.ndarray = data[:, 0]
+ y: np.ndarray = data[:, 1]
+
+ # Round x to 4 decimal places
+ x = np.round(x, 4)
+
+ # Determine sy from column 3 if available, otherwise use sqrt(y)
+ sy: np.ndarray = data[:, 2] if data.shape[1] > 2 else np.sqrt(y)
+
+ # Replace values smaller than 0.0001 with 1.0
+ # TODO: Not used if loading from cif file?
+ sy = np.where(sy < 0.0001, 1.0, sy)
+
+ # Set the experiment data
+ self.data._create_items_set_xcoord_and_id(x)
+ self.data._set_intensity_meas(y)
+ self.data._set_intensity_meas_su(sy)
+
+ return len(x)
+
+ # ------------------------------------------------------------------
+ # Instrument (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def instrument(self) -> object:
+ """Active instrument model for this experiment."""
+ return self._instrument
+
+ @property
+ def instrument_type(self) -> str:
+ """Tag of the active instrument type."""
+ return self._instrument_type
+
+ @instrument_type.setter
+ def instrument_type(self, new_type: str) -> None:
+ """
+ Switch to a different instrument type.
+
+ Parameters
+ ----------
+ new_type : str
+ Instrument tag (e.g. ``'cwl-pd'``).
+ """
+ supported = InstrumentFactory.supported_for(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ sample_form=self.type.sample_form.value,
+ )
+ supported_tags = [k.type_info.tag for k in supported]
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported instrument type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_instrument_types()'",
+ )
+ return
+ self._instrument = InstrumentFactory.create(new_type)
+ self._instrument_type = new_type
+ console.paragraph(f"Instrument type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_instrument_types(self) -> None:
+ """Print a table of supported instrument types."""
+ InstrumentFactory.show_supported(
+ scattering_type=self.type.scattering_type.value,
+ beam_mode=self.type.beam_mode.value,
+ sample_form=self.type.sample_form.value,
+ )
+
+ def show_current_instrument_type(self) -> None:
+ """Print the currently used instrument type."""
+ console.paragraph('Current instrument type')
+ console.print(self.instrument_type)
+
+ # ------------------------------------------------------------------
+ # Background (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def background_type(self) -> object:
+ """Current background type enum value."""
+ return self._background_type
+
+ @background_type.setter
+ 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")
+ console.print(new_type)
+ return
+
+ supported_tags = BackgroundFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported background type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_background_types()'",
+ )
+ return
+
+ if len(self._background) > 0:
+ log.warning(
+ f'Switching background type discards {len(self._background)} '
+ f'existing background point(s).',
+ )
+
+ self._background = BackgroundFactory.create(new_type)
+ self._background_type = new_type
+ console.paragraph(f"Background type for experiment '{self.name}' changed to")
+ console.print(new_type)
+
+ @property
+ def background(self) -> object:
+ """Active background model for this experiment."""
+ return self._background
+
+ def show_supported_background_types(self) -> None:
+ """Print a table of supported background types."""
+ BackgroundFactory.show_supported()
+
+ 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/bragg_sc.py b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py
new file mode 100644
index 00000000..f77d3af6
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py
@@ -0,0 +1,162 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import numpy as np
+
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.item.base import ScExperimentBase
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory
+from easydiffraction.io.ascii import load_numeric_block
+from easydiffraction.utils.logging import log
+
+if TYPE_CHECKING:
+ from easydiffraction.datablocks.experiment.categories.experiment_type import ExperimentType
+
+
+@ExperimentFactory.register
+class CwlScExperiment(ScExperimentBase):
+ """Bragg constant-wavelength single-crystal experiment."""
+
+ type_info = TypeInfo(
+ tag='bragg-sc-cwl',
+ description='Bragg CWL single-crystal experiment',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH}),
+ )
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ type: ExperimentType,
+ ) -> None:
+ super().__init__(name=name, type=type)
+
+ def _load_ascii_data_to_experiment(self, data_path: str) -> int:
+ """
+ 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``.
+
+ Parameters
+ ----------
+ data_path : str
+ Path to the ASCII data file.
+
+ Returns
+ -------
+ int
+ Number of loaded data points.
+ """
+ data = load_numeric_block(data_path)
+
+ if data.shape[1] < 5:
+ log.error(
+ 'Data file must have at least 5 columns: h, k, l, Iobs, sIobs.',
+ exc_type=ValueError,
+ )
+ return 0
+
+ # Extract Miller indices h, k, l
+ indices_h: np.ndarray = data[:, 0].astype(int)
+ indices_k: np.ndarray = data[:, 1].astype(int)
+ indices_l: np.ndarray = data[:, 2].astype(int)
+
+ # Extract intensities and their standard uncertainties
+ integrated_intensities: np.ndarray = data[:, 3]
+ integrated_intensities_su: np.ndarray = data[:, 4]
+
+ # Set the experiment data
+ self.data._create_items_set_hkl_and_id(indices_h, indices_k, indices_l)
+ self.data._set_intensity_meas(integrated_intensities)
+ self.data._set_intensity_meas_su(integrated_intensities_su)
+
+ return len(indices_h)
+
+
+@ExperimentFactory.register
+class TofScExperiment(ScExperimentBase):
+ """Bragg time-of-flight single-crystal experiment."""
+
+ type_info = TypeInfo(
+ tag='bragg-sc-tof',
+ description='Bragg TOF single-crystal experiment',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.BRAGG}),
+ sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}),
+ beam_mode=frozenset({BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ type: ExperimentType,
+ ) -> None:
+ super().__init__(name=name, type=type)
+
+ def _load_ascii_data_to_experiment(self, data_path: str) -> int:
+ """
+ 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``.
+
+ Parameters
+ ----------
+ data_path : str
+ Path to the ASCII data file.
+
+ Returns
+ -------
+ int
+ Number of loaded data points.
+ """
+ try:
+ data = load_numeric_block(data_path)
+ except IOError as e:
+ log.error(
+ f'Failed to read data from {data_path}: {e}',
+ exc_type=IOError,
+ )
+ return 0
+
+ if data.shape[1] < 6:
+ log.error(
+ 'Data file must have at least 6 columns: h, k, l, Iobs, sIobs, wavelength.',
+ exc_type=ValueError,
+ )
+ return 0
+
+ # Extract Miller indices h, k, l
+ indices_h: np.ndarray = data[:, 0].astype(int)
+ indices_k: np.ndarray = data[:, 1].astype(int)
+ indices_l: np.ndarray = data[:, 2].astype(int)
+
+ # Extract intensities and their standard uncertainties
+ integrated_intensities: np.ndarray = data[:, 3]
+ integrated_intensities_su: np.ndarray = data[:, 4]
+
+ # Extract wavelength values
+ wavelength: np.ndarray = data[:, 5]
+
+ # Set the experiment data
+ self.data._create_items_set_hkl_and_id(indices_h, indices_k, indices_l)
+ self.data._set_intensity_meas(integrated_intensities)
+ self.data._set_intensity_meas_su(integrated_intensities_su)
+ self.data._set_wavelength(wavelength)
+
+ return len(indices_h)
diff --git a/src/easydiffraction/experiments/experiment/enums.py b/src/easydiffraction/datablocks/experiment/item/enums.py
similarity index 59%
rename from src/easydiffraction/experiments/experiment/enums.py
rename to src/easydiffraction/datablocks/experiment/item/enums.py
index b15220fe..2375c38e 100644
--- a/src/easydiffraction/experiments/experiment/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)."""
@@ -13,9 +13,25 @@ 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 +46,25 @@ 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 +79,25 @@ 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,15 +113,45 @@ 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:
return 'Time-of-flight (TOF) diffraction.'
+class CalculatorEnum(str, Enum):
+ """Known calculation engine identifiers."""
+
+ CRYSPY = 'cryspy'
+ CRYSFML = 'crysfml'
+ PDFFIT = 'pdffit'
+
+
+# TODO: Can, instead of hardcoding here, this info be auto-extracted
+# from the actual peak profile classes defined in peak/cwl.py, tof.py,
+# total.py? So that their Enum variable, string representation and
+# description are defined in the respective classes?
+# TODO: Can supported values be defined based on the structure of peak/?
+# TODO: Can the same be reused for other enums in this file?
class PeakProfileTypeEnum(str, Enum):
"""Available peak profile types per scattering and beam mode."""
@@ -90,6 +168,23 @@ 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:
@@ -105,6 +200,14 @@ 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/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py
new file mode 100644
index 00000000..5c0b3094
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/item/factory.py
@@ -0,0 +1,277 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+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
+``from_scratch``.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from typeguard import typechecked
+
+from easydiffraction.core.factory import FactoryBase
+from easydiffraction.datablocks.experiment.categories.experiment_type import ExperimentType
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.io.cif.parse import document_from_path
+from easydiffraction.io.cif.parse import document_from_string
+from easydiffraction.io.cif.parse import name_from_block
+from easydiffraction.io.cif.parse import pick_sole_block
+from easydiffraction.utils.enums import VerbosityEnum
+from easydiffraction.utils.logging import console
+from easydiffraction.utils.logging import log
+
+if TYPE_CHECKING:
+ import gemmi
+
+ from easydiffraction.datablocks.experiment.item.base import ExperimentBase
+
+
+class ExperimentFactory(FactoryBase):
+ """Creates Experiment instances with only relevant attributes."""
+
+ _default_rules = {
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('sample_form', SampleFormEnum.POWDER),
+ }): 'bragg-pd',
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.TOTAL),
+ ('sample_form', SampleFormEnum.POWDER),
+ }): 'total-pd',
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('sample_form', SampleFormEnum.SINGLE_CRYSTAL),
+ ('beam_mode', BeamModeEnum.CONSTANT_WAVELENGTH),
+ }): 'bragg-sc-cwl',
+ frozenset({
+ ('scattering_type', ScatteringTypeEnum.BRAGG),
+ ('sample_form', SampleFormEnum.SINGLE_CRYSTAL),
+ ('beam_mode', BeamModeEnum.TIME_OF_FLIGHT),
+ }): 'bragg-sc-tof',
+ }
+
+ # TODO: Add to core/factory.py?
+ def __init__(self) -> None:
+ log.error(
+ 'Experiment objects must be created using class methods such as '
+ '`ExperimentFactory.from_cif_str(...)`, etc.'
+ )
+
+ # ------------------------------------------------------------------
+ # Private helper methods
+ # ------------------------------------------------------------------
+
+ @classmethod
+ @typechecked
+ def _create_experiment_type(
+ cls,
+ sample_form: str | None = None,
+ beam_mode: str | None = None,
+ radiation_probe: str | None = None,
+ scattering_type: str | None = None,
+ ) -> ExperimentType:
+ """Construct ExperimentType with defaults for omitted values."""
+ # Note: validation of input values is done via Descriptor setter
+ # methods
+
+ et = ExperimentType()
+
+ if sample_form is not None:
+ et._set_sample_form(sample_form)
+ if beam_mode is not None:
+ et._set_beam_mode(beam_mode)
+ if radiation_probe is not None:
+ et._set_radiation_probe(radiation_probe)
+ if scattering_type is not None:
+ et._set_scattering_type(scattering_type)
+
+ return et
+
+ @classmethod
+ @typechecked
+ 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,
+ sample_form=expt_type.sample_form.value,
+ beam_mode=expt_type.beam_mode.value,
+ )
+ return cls._supported_map()[tag]
+
+ @classmethod
+ # TODO: @typechecked fails to find gemmi?
+ def _from_gemmi_block(
+ cls,
+ block: gemmi.cif.Block,
+ ) -> ExperimentBase:
+ """Build a model instance from a single CIF block."""
+ name = name_from_block(block)
+
+ expt_type = ExperimentType()
+ for param in expt_type.parameters:
+ param.from_cif(block)
+
+ expt_class = cls._resolve_class(expt_type)
+ expt_obj = expt_class(name=name, type=expt_type)
+
+ for category in expt_obj.categories:
+ category.from_cif(block)
+
+ return expt_obj
+
+ # ------------------------------------------------------------------
+ # Public methods
+ # ------------------------------------------------------------------
+
+ @classmethod
+ @typechecked
+ def from_scratch(
+ cls,
+ *,
+ name: str,
+ sample_form: str | None = None,
+ beam_mode: str | None = None,
+ radiation_probe: str | None = None,
+ scattering_type: str | None = None,
+ ) -> ExperimentBase:
+ """
+ Create an experiment without measured data.
+
+ Parameters
+ ----------
+ name : str
+ Experiment identifier.
+ sample_form : str | None, default=None
+ Sample form (e.g. ``'powder'``).
+ beam_mode : str | None, default=None
+ Beam mode (e.g. ``'constant wavelength'``).
+ radiation_probe : str | None, default=None
+ Radiation probe (e.g. ``'neutron'``).
+ 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(
+ sample_form=sample_form,
+ beam_mode=beam_mode,
+ radiation_probe=radiation_probe,
+ scattering_type=scattering_type,
+ )
+ expt_class = cls._resolve_class(expt_type)
+ expt_obj = expt_class(name=name, type=expt_type)
+ return expt_obj
+
+ # TODO: add minimal default configuration for missing parameters
+ @classmethod
+ @typechecked
+ def from_cif_str(
+ cls,
+ cif_str: str,
+ ) -> ExperimentBase:
+ """
+ Create an experiment from a CIF string.
+
+ Parameters
+ ----------
+ cif_str : str
+ Full CIF document as a string.
+
+ Returns
+ -------
+ ExperimentBase
+ A populated experiment instance.
+ """
+ doc = document_from_string(cif_str)
+ block = pick_sole_block(doc)
+ return cls._from_gemmi_block(block)
+
+ # TODO: Read content and call self.from_cif_str
+ @classmethod
+ @typechecked
+ def from_cif_path(
+ cls,
+ cif_path: str,
+ ) -> ExperimentBase:
+ """
+ Create an experiment from a CIF file path.
+
+ Parameters
+ ----------
+ cif_path : str
+ Path to a CIF file.
+
+ Returns
+ -------
+ ExperimentBase
+ A populated experiment instance.
+ """
+ doc = document_from_path(cif_path)
+ block = pick_sole_block(doc)
+ return cls._from_gemmi_block(block)
+
+ @classmethod
+ @typechecked
+ def from_data_path(
+ cls,
+ *,
+ name: str,
+ data_path: str,
+ sample_form: str | None = None,
+ beam_mode: str | None = None,
+ radiation_probe: str | None = None,
+ scattering_type: str | None = None,
+ verbosity: VerbosityEnum = VerbosityEnum.FULL,
+ ) -> ExperimentBase:
+ """
+ Create an experiment from a raw data ASCII file.
+
+ Parameters
+ ----------
+ name : str
+ Experiment identifier.
+ data_path : str
+ Path to the measured data file.
+ sample_form : str | None, default=None
+ Sample form (e.g. ``'powder'``).
+ beam_mode : str | None, default=None
+ Beam mode (e.g. ``'constant wavelength'``).
+ radiation_probe : str | None, default=None
+ Radiation probe (e.g. ``'neutron'``).
+ scattering_type : str | None, default=None
+ Scattering type (e.g. ``'bragg'``).
+ verbosity : VerbosityEnum, default=VerbosityEnum.FULL
+ Console output verbosity.
+
+ Returns
+ -------
+ ExperimentBase
+ An experiment instance with measured data attached.
+ """
+ expt_obj = cls.from_scratch(
+ name=name,
+ sample_form=sample_form,
+ beam_mode=beam_mode,
+ radiation_probe=radiation_probe,
+ scattering_type=scattering_type,
+ )
+
+ num_points = expt_obj._load_ascii_data_to_experiment(data_path)
+
+ if verbosity is VerbosityEnum.FULL:
+ console.paragraph('Data loaded successfully')
+ console.print(f"Experiment 🔬 '{name}'. Number of data points: {num_points}.")
+ elif verbosity is VerbosityEnum.SHORT:
+ console.print(f"✅ Data loaded: Experiment 🔬 '{name}'. {num_points} points.")
+
+ return expt_obj
diff --git a/src/easydiffraction/datablocks/experiment/item/total_pd.py b/src/easydiffraction/datablocks/experiment/item/total_pd.py
new file mode 100644
index 00000000..2ed856ba
--- /dev/null
+++ b/src/easydiffraction/datablocks/experiment/item/total_pd.py
@@ -0,0 +1,92 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+import numpy as np
+
+from easydiffraction.core.metadata import Compatibility
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.datablocks.experiment.item.base import PdExperimentBase
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
+from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory
+
+if TYPE_CHECKING:
+ from easydiffraction.datablocks.experiment.categories.experiment_type import ExperimentType
+
+
+@ExperimentFactory.register
+class TotalPdExperiment(PdExperimentBase):
+ """PDF experiment class with specific attributes."""
+
+ type_info = TypeInfo(
+ tag='total-pd',
+ description='Total scattering (PDF) powder experiment',
+ )
+ compatibility = Compatibility(
+ scattering_type=frozenset({ScatteringTypeEnum.TOTAL}),
+ sample_form=frozenset({SampleFormEnum.POWDER}),
+ beam_mode=frozenset({BeamModeEnum.CONSTANT_WAVELENGTH, BeamModeEnum.TIME_OF_FLIGHT}),
+ )
+
+ def __init__(
+ self,
+ name: str,
+ type: ExperimentType,
+ ) -> None:
+ super().__init__(name=name, type=type)
+
+ def _load_ascii_data_to_experiment(self, data_path: str) -> int:
+ """
+ Load x, y, sy values from an ASCII file into the experiment.
+
+ The file must be structured as: x y sy
+
+ Parameters
+ ----------
+ data_path : str
+ Path to the ASCII data file.
+
+ Returns
+ -------
+ int
+ Number of loaded data points.
+
+ Raises
+ ------
+ ImportError
+ If the ``diffpy`` package is not installed.
+ IOError
+ If the data file cannot be read.
+ ValueError
+ If the data file has fewer than two columns.
+ """
+ try:
+ from diffpy.utils.parsers.loaddata import loadData
+ except ImportError:
+ raise ImportError('diffpy module not found.') from None
+ try:
+ data = loadData(data_path)
+ except Exception as e:
+ raise IOError(f'Failed to read data from {data_path}: {e}') from e
+
+ if data.shape[1] < 2:
+ raise ValueError('Data file must have at least two columns: x and y.')
+
+ default_sy = 0.03
+ if data.shape[1] < 3:
+ print(f'Warning: No uncertainty (sy) column provided. Defaulting to {default_sy}.')
+
+ x = data[:, 0]
+ y = data[:, 1]
+ sy = data[:, 2] if data.shape[1] > 2 else np.full_like(y, fill_value=default_sy)
+
+ self.data._create_items_set_xcoord_and_id(x)
+ self.data._set_g_r_meas(y)
+ self.data._set_g_r_meas_su(sy)
+
+ return len(x)
diff --git a/src/easydiffraction/datablocks/structure/__init__.py b/src/easydiffraction/datablocks/structure/__init__.py
new file mode 100644
index 00000000..4e798e20
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/__init__.py
@@ -0,0 +1,2 @@
+# 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
new file mode 100644
index 00000000..4e798e20
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/__init__.py
@@ -0,0 +1,2 @@
+# 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
new file mode 100644
index 00000000..7bd7b9ad
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/__init__.py
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.datablocks.structure.categories.atom_sites.default import AtomSite
+from easydiffraction.datablocks.structure.categories.atom_sites.default import AtomSites
diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py
new file mode 100644
index 00000000..265c3c62
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py
@@ -0,0 +1,380 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+Atom site category.
+
+Defines :class:`AtomSite` items and :class:`AtomSites` collection used
+in crystallographic structures.
+"""
+
+from __future__ import annotations
+
+from cryspy.A_functions_base.database import DATABASE
+
+from easydiffraction.core.category import CategoryCollection
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import MembershipValidator
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.validation import RegexValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.crystallography import crystallography as ecr
+from easydiffraction.datablocks.structure.categories.atom_sites.factory import AtomSitesFactory
+from easydiffraction.io.cif.handler import CifHandler
+
+
+class AtomSite(CategoryItem):
+ """
+ Single atom site with fractional coordinates and ADP.
+
+ Attributes are represented by descriptors to support validation and
+ CIF serialization.
+ """
+
+ def __init__(self) -> None:
+ """Initialise the atom site with default descriptor values."""
+ super().__init__()
+
+ self._label = StringDescriptor(
+ name='label',
+ description='Unique identifier for the atom site.',
+ value_spec=AttributeSpec(
+ default='Si',
+ # TODO: the following pattern is valid for dict key
+ # (keywords are not checked). CIF label is less strict.
+ # Do we need conversion between CIF and internal label?
+ validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.label']),
+ )
+ self._type_symbol = StringDescriptor(
+ name='type_symbol',
+ description='Chemical symbol of the atom at this site.',
+ value_spec=AttributeSpec(
+ default='Tb',
+ validator=MembershipValidator(allowed=self._type_symbol_allowed_values),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.type_symbol']),
+ )
+ self._fract_x = Parameter(
+ name='fract_x',
+ description='Fractional x-coordinate of the atom site within the unit cell.',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.fract_x']),
+ )
+ self._fract_y = Parameter(
+ name='fract_y',
+ description='Fractional y-coordinate of the atom site within the unit cell.',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.fract_y']),
+ )
+ self._fract_z = Parameter(
+ name='fract_z',
+ description='Fractional z-coordinate of the atom site within the unit cell.',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.fract_z']),
+ )
+ self._wyckoff_letter = StringDescriptor(
+ name='wyckoff_letter',
+ description='Wyckoff letter indicating the symmetry of the '
+ 'atom site within the space group.',
+ value_spec=AttributeSpec(
+ default=self._wyckoff_letter_default_value,
+ validator=MembershipValidator(allowed=self._wyckoff_letter_allowed_values),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_atom_site.Wyckoff_letter',
+ '_atom_site.Wyckoff_symbol',
+ ]
+ ),
+ )
+ self._occupancy = Parameter(
+ name='occupancy',
+ description='Occupancy of the atom site, representing the '
+ 'fraction of the site occupied by the atom type.',
+ value_spec=AttributeSpec(
+ default=1.0,
+ validator=RangeValidator(),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.occupancy']),
+ )
+ self._b_iso = Parameter(
+ name='b_iso',
+ description='Isotropic atomic displacement parameter (ADP) for the atom site.',
+ units='Ų',
+ value_spec=AttributeSpec(
+ default=0.0,
+ validator=RangeValidator(ge=0.0),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.B_iso_or_equiv']),
+ )
+ self._adp_type = StringDescriptor(
+ name='adp_type',
+ description='Type of atomic displacement parameter (ADP) '
+ 'used (e.g., Biso, Uiso, Uani, Bani).',
+ value_spec=AttributeSpec(
+ default='Biso',
+ validator=MembershipValidator(allowed=['Biso']),
+ ),
+ cif_handler=CifHandler(names=['_atom_site.adp_type']),
+ )
+
+ self._identity.category_code = 'atom_site'
+ self._identity.category_entry_name = lambda: str(self.label.value)
+
+ # ------------------------------------------------------------------
+ # Private helper methods
+ # ------------------------------------------------------------------
+
+ @property
+ def _type_symbol_allowed_values(self) -> list[str]:
+ """
+ Return chemical symbols accepted by *cryspy*.
+
+ 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.
+
+ 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 =
+ # list(SPACE_GROUPS[62, 'cab']['Wyckoff_positions'].keys())
+ # Temporarily return hardcoded list:
+ return ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
+
+ @property
+ def _wyckoff_letter_default_value(self) -> str:
+ """
+ Return the default Wyckoff letter.
+
+ Returns
+ -------
+ str
+ First element of the allowed values list.
+ """
+ # TODO: What to pass as default?
+ return self._wyckoff_letter_allowed_values[0]
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ 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.
+ """
+ return self._label
+
+ @label.setter
+ def label(self, value: str) -> None:
+ self._label.value = value
+
+ @property
+ 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.
+ """
+ return self._type_symbol
+
+ @type_symbol.setter
+ def type_symbol(self, value: str) -> None:
+ self._type_symbol.value = value
+
+ @property
+ def adp_type(self) -> StringDescriptor:
+ """
+ ADP type 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:
+ self._adp_type.value = value
+
+ @property
+ def wyckoff_letter(self) -> StringDescriptor:
+ """
+ Wyckoff letter for the atom site symmetry position.
+
+ 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:
+ self._wyckoff_letter.value = value
+
+ @property
+ def fract_x(self) -> 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:
+ self._fract_x.value = value
+
+ @property
+ def fract_y(self) -> 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:
+ self._fract_y.value = value
+
+ @property
+ def fract_z(self) -> 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:
+ self._fract_z.value = value
+
+ @property
+ def occupancy(self) -> Parameter:
+ """
+ Occupancy fraction of the atom type at this site.
+
+ 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:
+ self._occupancy.value = value
+
+ @property
+ def b_iso(self) -> Parameter:
+ """
+ Isotropic 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:
+ self._b_iso.value = value
+
+
+@AtomSitesFactory.register
+class AtomSites(CategoryCollection):
+ """Collection of :class:`AtomSite` instances."""
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Atom sites collection',
+ )
+
+ def __init__(self) -> None:
+ """Initialise an empty atom-sites collection."""
+ super().__init__(item_type=AtomSite)
+
+ # ------------------------------------------------------------------
+ # Private helper methods
+ # ------------------------------------------------------------------
+
+ def _apply_atomic_coordinates_symmetry_constraints(self) -> None:
+ """
+ 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
+ Wyckoff letter are silently skipped.
+ """
+ structure = self._parent
+ space_group_name = structure.space_group.name_h_m.value
+ space_group_coord_code = structure.space_group.it_coordinate_system_code.value
+ for atom in self._items:
+ dummy_atom = {
+ 'fract_x': atom.fract_x.value,
+ 'fract_y': atom.fract_y.value,
+ 'fract_z': atom.fract_z.value,
+ }
+ wl = atom.wyckoff_letter.value
+ if not wl:
+ # TODO: Decide how to handle this case
+ continue
+ ecr.apply_atom_site_symmetry_constraints(
+ atom_site=dummy_atom,
+ name_hm=space_group_name,
+ coord_code=space_group_coord_code,
+ wyckoff_letter=wl,
+ )
+ atom.fract_x.value = dummy_atom['fract_x']
+ atom.fract_y.value = dummy_atom['fract_y']
+ atom.fract_z.value = dummy_atom['fract_z']
+
+ def _update(
+ self,
+ called_by_minimizer: bool = False,
+ ) -> None:
+ """
+ Recalculate atom sites after a change.
+
+ Parameters
+ ----------
+ called_by_minimizer : bool, default=False
+ Whether the update was triggered by the fitting minimizer.
+ Currently unused.
+ """
+ del called_by_minimizer
+
+ self._apply_atomic_coordinates_symmetry_constraints()
diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py
new file mode 100644
index 00000000..c91b3dda
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Atom-sites factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class AtomSitesFactory(FactoryBase):
+ """Create atom-sites collections by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/structure/categories/cell/__init__.py b/src/easydiffraction/datablocks/structure/categories/cell/__init__.py
new file mode 100644
index 00000000..16f6cab2
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/cell/__init__.py
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
index 00000000..53cc98ee
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py
@@ -0,0 +1,235 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Unit cell parameters category for structures."""
+
+from __future__ import annotations
+
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import RangeValidator
+from easydiffraction.core.variable import Parameter
+from easydiffraction.crystallography import crystallography as ecr
+from easydiffraction.datablocks.structure.categories.cell.factory import CellFactory
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@CellFactory.register
+class Cell(CategoryItem):
+ """
+ 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.
+ """
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Unit cell parameters',
+ )
+
+ def __init__(self) -> None:
+ """Initialise the unit cell with default parameter values."""
+ super().__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']),
+ )
+ self._length_b = Parameter(
+ name='length_b',
+ description='Length of the b axis of the unit cell',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=10.0,
+ validator=RangeValidator(ge=0, le=1000),
+ ),
+ cif_handler=CifHandler(names=['_cell.length_b']),
+ )
+ self._length_c = Parameter(
+ name='length_c',
+ description='Length of the c axis of the unit cell',
+ units='Å',
+ value_spec=AttributeSpec(
+ default=10.0,
+ validator=RangeValidator(ge=0, le=1000),
+ ),
+ cif_handler=CifHandler(names=['_cell.length_c']),
+ )
+ self._angle_alpha = Parameter(
+ name='angle_alpha',
+ description='Angle between edges b and c',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=90.0,
+ validator=RangeValidator(ge=0, le=180),
+ ),
+ cif_handler=CifHandler(names=['_cell.angle_alpha']),
+ )
+ self._angle_beta = Parameter(
+ name='angle_beta',
+ description='Angle between edges a and c',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=90.0,
+ validator=RangeValidator(ge=0, le=180),
+ ),
+ cif_handler=CifHandler(names=['_cell.angle_beta']),
+ )
+ self._angle_gamma = Parameter(
+ name='angle_gamma',
+ description='Angle between edges a and b',
+ units='deg',
+ value_spec=AttributeSpec(
+ default=90.0,
+ validator=RangeValidator(ge=0, le=180),
+ ),
+ cif_handler=CifHandler(names=['_cell.angle_gamma']),
+ )
+
+ self._identity.category_code = 'cell'
+
+ # ------------------------------------------------------------------
+ # Private helper methods
+ # ------------------------------------------------------------------
+
+ def _apply_cell_symmetry_constraints(self) -> None:
+ """
+ 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
+ accordingly.
+ """
+ dummy_cell = {
+ 'lattice_a': self.length_a.value,
+ 'lattice_b': self.length_b.value,
+ 'lattice_c': self.length_c.value,
+ 'angle_alpha': self.angle_alpha.value,
+ 'angle_beta': self.angle_beta.value,
+ 'angle_gamma': self.angle_gamma.value,
+ }
+ space_group_name = self._parent.space_group.name_h_m.value
+
+ ecr.apply_cell_symmetry_constraints(
+ cell=dummy_cell,
+ name_hm=space_group_name,
+ )
+
+ self.length_a.value = dummy_cell['lattice_a']
+ self.length_b.value = dummy_cell['lattice_b']
+ self.length_c.value = dummy_cell['lattice_c']
+ self.angle_alpha.value = dummy_cell['angle_alpha']
+ self.angle_beta.value = dummy_cell['angle_beta']
+ self.angle_gamma.value = dummy_cell['angle_gamma']
+
+ def _update(
+ self,
+ called_by_minimizer: bool = False,
+ ) -> None:
+ """
+ Recalculate cell parameters after a change.
+
+ Parameters
+ ----------
+ called_by_minimizer : bool, default=False
+ Whether the update was triggered by the fitting minimizer.
+ Currently unused.
+ """
+ del called_by_minimizer # TODO: ???
+
+ self._apply_cell_symmetry_constraints()
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ 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.
+ """
+ return self._length_a
+
+ @length_a.setter
+ def length_a(self, value: float) -> None:
+ self._length_a.value = value
+
+ @property
+ 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.
+ """
+ return self._length_b
+
+ @length_b.setter
+ def length_b(self, value: float) -> None:
+ self._length_b.value = value
+
+ @property
+ 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.
+ """
+ return self._length_c
+
+ @length_c.setter
+ def length_c(self, value: float) -> None:
+ self._length_c.value = value
+
+ @property
+ 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.
+ """
+ return self._angle_alpha
+
+ @angle_alpha.setter
+ def angle_alpha(self, value: float) -> None:
+ self._angle_alpha.value = value
+
+ @property
+ 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.
+ """
+ return self._angle_beta
+
+ @angle_beta.setter
+ def angle_beta(self, value: float) -> None:
+ self._angle_beta.value = value
+
+ @property
+ 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.
+ """
+ return self._angle_gamma
+
+ @angle_gamma.setter
+ def angle_gamma(self, value: float) -> None:
+ self._angle_gamma.value = value
diff --git a/src/easydiffraction/datablocks/structure/categories/cell/factory.py b/src/easydiffraction/datablocks/structure/categories/cell/factory.py
new file mode 100644
index 00000000..6817b2d7
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/cell/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Cell factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class CellFactory(FactoryBase):
+ """Create unit-cell categories by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/__init__.py b/src/easydiffraction/datablocks/structure/categories/space_group/__init__.py
new file mode 100644
index 00000000..a8b33e62
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/space_group/__init__.py
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
index 00000000..a91cc554
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py
@@ -0,0 +1,163 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Space group category for crystallographic structures."""
+
+from __future__ import annotations
+
+from cryspy.A_functions_base.function_2_space_group import ACCESIBLE_NAME_HM_SHORT
+from cryspy.A_functions_base.function_2_space_group import (
+ get_it_coordinate_system_codes_by_it_number,
+)
+from cryspy.A_functions_base.function_2_space_group import get_it_number_by_name_hm_short
+
+from easydiffraction.core.category import CategoryItem
+from easydiffraction.core.metadata import TypeInfo
+from easydiffraction.core.validation import AttributeSpec
+from easydiffraction.core.validation import MembershipValidator
+from easydiffraction.core.variable import StringDescriptor
+from easydiffraction.datablocks.structure.categories.space_group.factory import SpaceGroupFactory
+from easydiffraction.io.cif.handler import CifHandler
+
+
+@SpaceGroupFactory.register
+class SpaceGroup(CategoryItem):
+ """
+ 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``).
+ Changing the symbol automatically resets the coordinate-system code
+ to the first allowed value for the new group.
+ """
+
+ type_info = TypeInfo(
+ tag='default',
+ description='Space group symmetry',
+ )
+
+ def __init__(self) -> None:
+ """Initialise the space group with default values."""
+ super().__init__()
+
+ self._name_h_m = StringDescriptor(
+ name='name_h_m',
+ description='Hermann-Mauguin symbol of the space group.',
+ value_spec=AttributeSpec(
+ default='P 1',
+ validator=MembershipValidator(
+ allowed=lambda: self._name_h_m_allowed_values,
+ ),
+ ),
+ cif_handler=CifHandler(
+ # TODO: Keep only version with "." and automate ...
+ names=[
+ '_space_group.name_H-M_alt',
+ '_space_group_name_H-M_alt',
+ '_symmetry.space_group_name_H-M',
+ '_symmetry_space_group_name_H-M',
+ ]
+ ),
+ )
+ self._it_coordinate_system_code = StringDescriptor(
+ name='it_coordinate_system_code',
+ description='A qualifier identifying which setting in IT is used.',
+ value_spec=AttributeSpec(
+ default=lambda: self._it_coordinate_system_code_default_value,
+ validator=MembershipValidator(
+ allowed=lambda: self._it_coordinate_system_code_allowed_values
+ ),
+ ),
+ cif_handler=CifHandler(
+ names=[
+ '_space_group.IT_coordinate_system_code',
+ '_space_group_IT_coordinate_system_code',
+ '_symmetry.IT_coordinate_system_code',
+ '_symmetry_IT_coordinate_system_code',
+ ]
+ ),
+ )
+
+ self._identity.category_code = 'space_group'
+
+ # ------------------------------------------------------------------
+ # Private helper methods
+ # ------------------------------------------------------------------
+
+ def _reset_it_coordinate_system_code(self) -> None:
+ """Reset IT coordinate system code to default for this group."""
+ self._it_coordinate_system_code.value = self._it_coordinate_system_code_default_value
+
+ @property
+ 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*.
+ """
+ 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 group.
+
+ 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)
+ codes = get_it_coordinate_system_codes_by_it_number(it_number)
+ codes = [str(code) for code in codes]
+ return codes if codes else ['']
+
+ @property
+ 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.
+ """
+ return self._it_coordinate_system_code_allowed_values[0]
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ 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.
+ """
+ return self._name_h_m
+
+ @name_h_m.setter
+ def name_h_m(self, value: str) -> None:
+ self._name_h_m.value = value
+ self._reset_it_coordinate_system_code()
+
+ @property
+ 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.
+ """
+ return self._it_coordinate_system_code
+
+ @it_coordinate_system_code.setter
+ def it_coordinate_system_code(self, value: str) -> None:
+ self._it_coordinate_system_code.value = value
diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/factory.py b/src/easydiffraction/datablocks/structure/categories/space_group/factory.py
new file mode 100644
index 00000000..9ef8611d
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/categories/space_group/factory.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Space-group factory — delegates entirely to ``FactoryBase``."""
+
+from __future__ import annotations
+
+from easydiffraction.core.factory import FactoryBase
+
+
+class SpaceGroupFactory(FactoryBase):
+ """Create space-group categories by tag."""
+
+ _default_rules = {
+ frozenset(): 'default',
+ }
diff --git a/src/easydiffraction/datablocks/structure/collection.py b/src/easydiffraction/datablocks/structure/collection.py
new file mode 100644
index 00000000..801b416a
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/collection.py
@@ -0,0 +1,92 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Collection of structure data blocks."""
+
+from typeguard import typechecked
+
+from easydiffraction.core.datablock import DatablockCollection
+from easydiffraction.datablocks.structure.item.base import Structure
+from easydiffraction.datablocks.structure.item.factory import StructureFactory
+from easydiffraction.utils.logging import console
+
+
+class Structures(DatablockCollection):
+ """
+ Ordered collection of :class:`Structure` instances.
+
+ Provides convenience ``add_from_*`` methods that mirror the
+ :class:`StructureFactory` classmethods plus a bare :meth:`add` for
+ inserting pre-built structures.
+ """
+
+ def __init__(self) -> None:
+ """Initialise an empty structures collection."""
+ super().__init__(item_type=Structure)
+
+ # ------------------------------------------------------------------
+ # Public methods
+ # ------------------------------------------------------------------
+
+ # TODO: Make abstract in DatablockCollection?
+ @typechecked
+ def create(
+ self,
+ *,
+ name: str,
+ ) -> None:
+ """
+ Create a minimal structure and add it to the collection.
+
+ Parameters
+ ----------
+ name : str
+ Identifier for the new structure.
+ """
+ structure = StructureFactory.from_scratch(name=name)
+ self.add(structure)
+
+ # TODO: Move to DatablockCollection?
+ @typechecked
+ def add_from_cif_str(
+ self,
+ cif_str: str,
+ ) -> None:
+ """
+ Create a structure from CIF content and add it.
+
+ Parameters
+ ----------
+ cif_str : str
+ CIF file content as a string.
+ """
+ structure = StructureFactory.from_cif_str(cif_str)
+ self.add(structure)
+
+ # TODO: Move to DatablockCollection?
+ @typechecked
+ def add_from_cif_path(
+ self,
+ cif_path: str,
+ ) -> None:
+ """
+ Create a structure from a CIF file and add it.
+
+ Parameters
+ ----------
+ cif_path : str
+ Filesystem path to a CIF file.
+ """
+ structure = StructureFactory.from_cif_path(cif_path)
+ self.add(structure)
+
+ # TODO: Move to DatablockCollection?
+ def show_names(self) -> None:
+ """List all structure names in the collection."""
+ console.paragraph('Defined structures' + ' 🧩')
+ console.print(self.names)
+
+ # TODO: Move to DatablockCollection?
+ def show_params(self) -> None:
+ """Show parameters of all structures in the collection."""
+ for structure in self.values():
+ structure.show_params()
diff --git a/src/easydiffraction/datablocks/structure/item/__init__.py b/src/easydiffraction/datablocks/structure/item/__init__.py
new file mode 100644
index 00000000..4e798e20
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/item/__init__.py
@@ -0,0 +1,2 @@
+# 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
new file mode 100644
index 00000000..80d8f76a
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/item/base.py
@@ -0,0 +1,255 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Structure datablock item."""
+
+from typeguard import typechecked
+
+from easydiffraction.core.datablock import DatablockItem
+from easydiffraction.datablocks.structure.categories.atom_sites import AtomSites
+from easydiffraction.datablocks.structure.categories.atom_sites.factory import AtomSitesFactory
+from easydiffraction.datablocks.structure.categories.cell import Cell
+from easydiffraction.datablocks.structure.categories.cell.factory import CellFactory
+from easydiffraction.datablocks.structure.categories.space_group import SpaceGroup
+from easydiffraction.datablocks.structure.categories.space_group.factory import SpaceGroupFactory
+from easydiffraction.utils.logging import console
+from easydiffraction.utils.logging import log
+from easydiffraction.utils.utils import render_cif
+
+
+class Structure(DatablockItem):
+ """Structure datablock item."""
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ ) -> None:
+ super().__init__()
+ self._name = name
+ self._cell_type: str = CellFactory.default_tag()
+ self._cell = CellFactory.create(self._cell_type)
+ self._space_group_type: str = SpaceGroupFactory.default_tag()
+ self._space_group = SpaceGroupFactory.create(self._space_group_type)
+ self._atom_sites_type: str = AtomSitesFactory.default_tag()
+ self._atom_sites = AtomSitesFactory.create(self._atom_sites_type)
+ self._identity.datablock_entry_name = lambda: self.name
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
+
+ @property
+ def name(self) -> str:
+ """
+ Name identifier for this structure.
+
+ 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.
+
+ Parameters
+ ----------
+ new : str
+ New name string.
+ """
+ self._name = new
+
+ # ------------------------------------------------------------------
+ # Cell (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def cell(self) -> Cell:
+ """Unit-cell category for this structure."""
+ return self._cell
+
+ @cell.setter
+ @typechecked
+ def cell(self, new: Cell) -> None:
+ """
+ Replace the unit-cell category for this structure.
+
+ Parameters
+ ----------
+ new : Cell
+ New unit-cell instance.
+ """
+ self._cell = new
+
+ @property
+ def cell_type(self) -> str:
+ """Tag of the active unit-cell type."""
+ return self._cell_type
+
+ @cell_type.setter
+ def cell_type(self, new_type: str) -> None:
+ """
+ Switch to a different unit-cell type.
+
+ Parameters
+ ----------
+ new_type : str
+ Cell tag (e.g. ``'default'``).
+ """
+ supported_tags = CellFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported cell type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_cell_types()'",
+ )
+ return
+ self._cell = CellFactory.create(new_type)
+ self._cell_type = new_type
+ console.paragraph(f"Cell type for structure '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_cell_types(self) -> None:
+ """Print a table of supported unit-cell types."""
+ CellFactory.show_supported()
+
+ def show_current_cell_type(self) -> None:
+ """Print the currently used unit-cell type."""
+ console.paragraph('Current cell type')
+ console.print(self.cell_type)
+
+ # ------------------------------------------------------------------
+ # Space group (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def space_group(self) -> SpaceGroup:
+ """Space-group category for this structure."""
+ return self._space_group
+
+ @space_group.setter
+ @typechecked
+ def space_group(self, new: SpaceGroup) -> None:
+ """
+ Replace the space-group category for this structure.
+
+ Parameters
+ ----------
+ new : SpaceGroup
+ New space-group instance.
+ """
+ self._space_group = new
+
+ @property
+ def space_group_type(self) -> str:
+ """Tag of the active space-group type."""
+ return self._space_group_type
+
+ @space_group_type.setter
+ def space_group_type(self, new_type: str) -> None:
+ """
+ Switch to a different space-group type.
+
+ Parameters
+ ----------
+ new_type : str
+ Space-group tag (e.g. ``'default'``).
+ """
+ supported_tags = SpaceGroupFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported space group type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_space_group_types()'",
+ )
+ return
+ self._space_group = SpaceGroupFactory.create(new_type)
+ self._space_group_type = new_type
+ console.paragraph(f"Space group type for structure '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_space_group_types(self) -> None:
+ """Print a table of supported space-group types."""
+ SpaceGroupFactory.show_supported()
+
+ def show_current_space_group_type(self) -> None:
+ """Print the currently used space-group type."""
+ console.paragraph('Current space group type')
+ console.print(self.space_group_type)
+
+ # ------------------------------------------------------------------
+ # Atom sites (switchable-category pattern)
+ # ------------------------------------------------------------------
+
+ @property
+ def atom_sites(self) -> AtomSites:
+ """Atom-sites collection for this structure."""
+ return self._atom_sites
+
+ @atom_sites.setter
+ @typechecked
+ def atom_sites(self, new: AtomSites) -> None:
+ """
+ Replace the atom-sites collection for this structure.
+
+ Parameters
+ ----------
+ new : AtomSites
+ New atom-sites collection.
+ """
+ self._atom_sites = new
+
+ @property
+ def atom_sites_type(self) -> str:
+ """Tag of the active atom-sites collection type."""
+ return self._atom_sites_type
+
+ @atom_sites_type.setter
+ def atom_sites_type(self, new_type: str) -> None:
+ """
+ Switch to a different atom-sites collection type.
+
+ Parameters
+ ----------
+ new_type : str
+ Atom-sites tag (e.g. ``'default'``).
+ """
+ supported_tags = AtomSitesFactory.supported_tags()
+ if new_type not in supported_tags:
+ log.warning(
+ f"Unsupported atom sites type '{new_type}'. "
+ f'Supported: {supported_tags}. '
+ f"For more information, use 'show_supported_atom_sites_types()'",
+ )
+ return
+ self._atom_sites = AtomSitesFactory.create(new_type)
+ self._atom_sites_type = new_type
+ console.paragraph(f"Atom sites type for structure '{self.name}' changed to")
+ console.print(new_type)
+
+ def show_supported_atom_sites_types(self) -> None:
+ """Print a table of supported atom-sites collection types."""
+ AtomSitesFactory.show_supported()
+
+ def show_current_atom_sites_type(self) -> None:
+ """Print the currently used atom-sites collection type."""
+ console.paragraph('Current atom sites type')
+ console.print(self.atom_sites_type)
+
+ # ------------------------------------------------------------------
+ # Public methods
+ # ------------------------------------------------------------------
+
+ def show(self) -> None:
+ """Display an ASCII projection of the structure in 2D."""
+ console.paragraph(f"Structure 🧩 '{self.name}'")
+ console.print('Not implemented yet.')
+
+ def show_as_cif(self) -> None:
+ """Render the CIF text for this structure in the terminal."""
+ console.paragraph(f"Structure 🧩 '{self.name}' as cif")
+ render_cif(self.as_cif)
diff --git a/src/easydiffraction/datablocks/structure/item/factory.py b/src/easydiffraction/datablocks/structure/item/factory.py
new file mode 100644
index 00000000..567d26dc
--- /dev/null
+++ b/src/easydiffraction/datablocks/structure/item/factory.py
@@ -0,0 +1,137 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""
+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``.
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from typeguard import typechecked
+
+from easydiffraction.datablocks.structure.item.base import Structure
+from easydiffraction.io.cif.parse import document_from_path
+from easydiffraction.io.cif.parse import document_from_string
+from easydiffraction.io.cif.parse import name_from_block
+from easydiffraction.io.cif.parse import pick_sole_block
+from easydiffraction.utils.logging import log
+
+if TYPE_CHECKING:
+ import gemmi
+
+
+class StructureFactory:
+ """Create :class:`Structure` instances from supported inputs."""
+
+ def __init__(self) -> None:
+ log.error(
+ 'Structure objects must be created using class methods such as '
+ '`StructureFactory.from_cif_str(...)`, etc.'
+ )
+
+ # ------------------------------------------------------------------
+ # Private helper methods
+ # ------------------------------------------------------------------
+
+ @classmethod
+ # TODO: @typechecked fails to find gemmi?
+ def _from_gemmi_block(
+ cls,
+ block: gemmi.cif.Block,
+ ) -> Structure:
+ """
+ Build a structure from a single *gemmi* CIF block.
+
+ Parameters
+ ----------
+ block : gemmi.cif.Block
+ Parsed CIF data block.
+
+ Returns
+ -------
+ Structure
+ A fully populated structure instance.
+ """
+ name = name_from_block(block)
+ structure = Structure(name=name)
+ for category in structure.categories:
+ category.from_cif(block)
+ return structure
+
+ # ------------------------------------------------------------------
+ # Public methods
+ # ------------------------------------------------------------------
+
+ @classmethod
+ @typechecked
+ def from_scratch(
+ cls,
+ *,
+ name: str,
+ ) -> Structure:
+ """
+ Create a minimal default structure.
+
+ Parameters
+ ----------
+ name : str
+ Identifier for the new structure.
+
+ Returns
+ -------
+ Structure
+ An empty structure with default categories.
+ """
+ return Structure(name=name)
+
+ # TODO: add minimal default configuration for missing parameters
+ @classmethod
+ @typechecked
+ def from_cif_str(
+ cls,
+ cif_str: str,
+ ) -> Structure:
+ """
+ Create a structure by parsing a CIF string.
+
+ Parameters
+ ----------
+ cif_str : str
+ Raw CIF content.
+
+ Returns
+ -------
+ Structure
+ A populated structure instance.
+ """
+ doc = document_from_string(cif_str)
+ block = pick_sole_block(doc)
+ return cls._from_gemmi_block(block)
+
+ # TODO: Read content and call self.from_cif_str
+ @classmethod
+ @typechecked
+ def from_cif_path(
+ cls,
+ cif_path: str,
+ ) -> Structure:
+ """
+ Create a structure by reading and parsing a CIF file.
+
+ Parameters
+ ----------
+ cif_path : str
+ Filesystem path to a CIF file.
+
+ Returns
+ -------
+ Structure
+ A populated structure instance.
+ """
+ doc = document_from_path(cif_path)
+ block = pick_sole_block(doc)
+ return cls._from_gemmi_block(block)
diff --git a/src/easydiffraction/display/__init__.py b/src/easydiffraction/display/__init__.py
index 265560b2..25bccda4 100644
--- a/src/easydiffraction/display/__init__.py
+++ b/src/easydiffraction/display/__init__.py
@@ -1,14 +1,15 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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.
+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 c3babcff..6ffc0698 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."""
@@ -6,26 +6,26 @@
from abc import ABC
from abc import abstractmethod
-from typing import Any
from typing import List
from typing import Tuple
import pandas as pd
-from easydiffraction.core.singletons import SingletonBase
+from easydiffraction.core.singleton import SingletonBase
from easydiffraction.utils.logging import console
from easydiffraction.utils.logging import log
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
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)
@@ -43,10 +43,27 @@ 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
@@ -90,18 +107,25 @@ class RendererFactoryBase(ABC):
"""Base factory that manages discovery and creation of backends."""
@classmethod
- def create(cls, engine_name: str) -> Any:
- """Create a backend instance for the given engine.
+ def create(cls, engine_name: str) -> object:
+ """
+ Create a backend instance for the given engine.
- Args:
- engine_name: Identifier of the engine to instantiate as
- listed in ``_registry()``.
+ Parameters
+ ----------
+ engine_name : str
+ Identifier of the engine to instantiate as listed in
+ ``_registry()``.
- Returns:
+ Returns
+ -------
+ object
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:
@@ -117,16 +141,15 @@ 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]
@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 14dae26a..09931ae8 100644
--- a/src/easydiffraction/display/plotters/__init__.py
+++ b/src/easydiffraction/display/plotters/__init__.py
@@ -1,10 +1,11 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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:
-- :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 f9ef8904..32ee45ed 100644
--- a/src/easydiffraction/display/plotters/ascii.py
+++ b/src/easydiffraction/display/plotters/ascii.py
@@ -1,13 +1,15 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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 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
+import numpy as np
from easydiffraction.display.plotters.base import DEFAULT_HEIGHT
from easydiffraction.display.plotters.base import SERIES_CONFIG
@@ -24,16 +26,21 @@
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.
+ def _get_legend_item(self, label: str) -> str:
+ """
+ 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'``).
+ Parameters
+ ----------
+ label : str
+ Series identifier (e.g., ``'meas'``).
- Returns:
+ Returns
+ -------
+ str
A formatted legend string with color escapes.
"""
color_start = DEFAULT_COLORS[label]
@@ -43,25 +50,36 @@ def _get_legend_item(self, label):
item = f'{color_start}{line}{color_end} {name}'
return item
- def plot(
+ def plot_powder(
self,
- x,
- y_series,
- labels,
- axes_labels,
- title,
- height=None,
- ):
- """Render a compact ASCII chart in the terminal.
-
- 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.
+ 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.
+
+ 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
+ ----------
+ x : object
+ 1D array-like of x values (only used for range display).
+ y_series : object
+ Sequence of y arrays to plot.
+ labels : object
+ Series identifiers corresponding to y_series.
+ axes_labels : object
+ Ignored; kept for API compatibility.
+ title : str
+ Figure title printed above the chart.
+ height : int | None, default=None
+ Number of text rows to allocate for the chart.
"""
# Intentionally unused; kept for a consistent display API
del axes_labels
@@ -84,3 +102,105 @@ def plot(
padded = '\n'.join(' ' + line for line in chart.splitlines())
print(padded)
+
+ def plot_single_crystal(
+ self,
+ 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.
+
+ Creates an ASCII scatter plot showing measured vs calculated
+ values with a diagonal reference line.
+
+ Parameters
+ ----------
+ x_calc : object
+ 1D array-like of calculated values (x-axis).
+ y_meas : object
+ 1D array-like of measured values (y-axis).
+ y_meas_su : object
+ 1D array-like of measurement uncertainties (ignored in ASCII
+ mode).
+ axes_labels : object
+ Pair of strings for the x and y titles.
+ title : str
+ Figure title.
+ height : int | None, default=None
+ Number of text rows for the chart (default: 15).
+ """
+ # Intentionally unused; ASCII scatter doesn't show error bars
+ del y_meas_su
+
+ if height is None:
+ height = DEFAULT_HEIGHT
+ width = 60 # TODO: Make width configurable
+
+ # Determine axis limits
+ vmin = float(min(np.min(y_meas), np.min(x_calc)))
+ vmax = float(max(np.max(y_meas), np.max(x_calc)))
+ pad = 0.05 * (vmax - vmin) if vmax > vmin else 1.0
+ vmin -= pad
+ vmax += pad
+
+ # Create empty grid
+ grid = [[' ' for _ in range(width)] for _ in range(height)]
+
+ # Draw diagonal line (calc == meas)
+ for i in range(min(width, height)):
+ row = height - 1 - int(i * height / width)
+ col = i
+ if 0 <= row < height and 0 <= col < width:
+ grid[row][col] = '·'
+
+ # Plot data points
+ for xv, yv in zip(x_calc, y_meas, strict=False):
+ col = int((xv - vmin) / (vmax - vmin) * (width - 1))
+ row = height - 1 - int((yv - vmin) / (vmax - vmin) * (height - 1))
+ if 0 <= row < height and 0 <= col < width:
+ grid[row][col] = '●'
+
+ # Build chart string with axes
+ chart_lines = []
+ for row in grid:
+ label = '│'
+ chart_lines.append(label + ''.join(row))
+
+ # X-axis
+ x_axis = '└' + '─' * width
+
+ # Print output
+ console.paragraph(f'{title}')
+ console.print(f'{axes_labels[1]}')
+ for line in chart_lines:
+ print(f' {line}')
+ print(f' {x_axis}')
+ console.print(f'{" " * (width - 3)}{axes_labels[0]}')
+
+ def plot_scatter(
+ self,
+ x: object,
+ y: object,
+ sy: object,
+ axes_labels: object,
+ title: str,
+ height: int | None = None,
+ ) -> None:
+ """Render a scatter plot with error bars in ASCII."""
+ _ = x, sy # ASCII backend does not use x ticks or error bars
+
+ if height is None:
+ height = DEFAULT_HEIGHT
+
+ config = {'height': height, 'colors': [asciichartpy.blue]}
+ chart = asciichartpy.plot([list(y)], config)
+
+ console.paragraph(f'{title}')
+ console.print(f'{axes_labels[1]} vs {axes_labels[0]}')
+ padded = '\n'.join(' ' + line for line in chart.splitlines())
+ print(padded)
diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py
index 43c22767..67fea11f 100644
--- a/src/easydiffraction/display/plotters/base.py
+++ b/src/easydiffraction/display/plotters/base.py
@@ -1,39 +1,137 @@
-# 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."""
from abc import ABC
from abc import abstractmethod
+from enum import Enum
import numpy as np
-from easydiffraction.experiments.experiment.enums import BeamModeEnum
-from easydiffraction.experiments.experiment.enums import ScatteringTypeEnum
+from easydiffraction.datablocks.experiment.item.enums import BeamModeEnum
+from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum
+from easydiffraction.datablocks.experiment.item.enums import ScatteringTypeEnum
-DEFAULT_HEIGHT = 9
+DEFAULT_HEIGHT = 25
DEFAULT_MIN = -np.inf
DEFAULT_MAX = np.inf
+
+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)``.
+ """
+
+ TWO_THETA = 'two_theta'
+ TIME_OF_FLIGHT = 'time_of_flight'
+ R = 'x'
+
+ INTENSITY_CALC = 'intensity_calc'
+
+ D_SPACING = 'd_spacing'
+ SIN_THETA_OVER_LAMBDA = 'sin_theta_over_lambda'
+
+
+# Map (SampleFormEnum, ScatteringTypeEnum, BeamModeEnum) to default
+# x-axis type
+DEFAULT_X_AXIS = {
+ # Powder Bragg diffraction
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.BRAGG,
+ BeamModeEnum.CONSTANT_WAVELENGTH,
+ ): XAxisType.TWO_THETA,
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.BRAGG,
+ BeamModeEnum.TIME_OF_FLIGHT,
+ ): XAxisType.TIME_OF_FLIGHT,
+ # Powder total scattering (PDF) — always r-space
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.TOTAL,
+ BeamModeEnum.CONSTANT_WAVELENGTH,
+ ): XAxisType.R,
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.TOTAL,
+ BeamModeEnum.TIME_OF_FLIGHT,
+ ): XAxisType.R,
+ # Single crystal Bragg diffraction
+ (
+ SampleFormEnum.SINGLE_CRYSTAL,
+ ScatteringTypeEnum.BRAGG,
+ BeamModeEnum.CONSTANT_WAVELENGTH,
+ ): XAxisType.INTENSITY_CALC,
+ (
+ SampleFormEnum.SINGLE_CRYSTAL,
+ ScatteringTypeEnum.BRAGG,
+ BeamModeEnum.TIME_OF_FLIGHT,
+ ): XAxisType.INTENSITY_CALC,
+}
+
DEFAULT_AXES_LABELS = {
- (ScatteringTypeEnum.BRAGG, BeamModeEnum.CONSTANT_WAVELENGTH): [
+ # Powder Bragg diffraction
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.BRAGG,
+ XAxisType.TWO_THETA,
+ ): [
'2θ (degree)',
'Intensity (arb. units)',
],
- (ScatteringTypeEnum.BRAGG, BeamModeEnum.TIME_OF_FLIGHT): [
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.BRAGG,
+ XAxisType.TIME_OF_FLIGHT,
+ ): [
'TOF (µs)',
'Intensity (arb. units)',
],
- (ScatteringTypeEnum.BRAGG, 'd-spacing'): [
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.BRAGG,
+ XAxisType.D_SPACING,
+ ): [
'd (Å)',
'Intensity (arb. units)',
],
- (ScatteringTypeEnum.TOTAL, BeamModeEnum.CONSTANT_WAVELENGTH): [
- 'r (Å)',
- 'G(r) (Å)',
+ # Powder total scattering (PDF)
+ (
+ SampleFormEnum.POWDER,
+ ScatteringTypeEnum.TOTAL,
+ XAxisType.R,
+ ): [
+ 'r (Å)',
+ 'G(r) (Å)',
+ ],
+ # Single crystal Bragg diffraction
+ (
+ SampleFormEnum.SINGLE_CRYSTAL,
+ ScatteringTypeEnum.BRAGG,
+ XAxisType.INTENSITY_CALC,
+ ): [
+ 'I²calc',
+ 'I²meas',
],
- (ScatteringTypeEnum.TOTAL, BeamModeEnum.TIME_OF_FLIGHT): [
- 'r (Å)',
- 'G(r) (Å)',
+ (
+ SampleFormEnum.SINGLE_CRYSTAL,
+ ScatteringTypeEnum.BRAGG,
+ XAxisType.D_SPACING,
+ ): [
+ 'd (Å)',
+ 'Intensity (arb. units)',
+ ],
+ (
+ SampleFormEnum.SINGLE_CRYSTAL,
+ ScatteringTypeEnum.BRAGG,
+ XAxisType.SIN_THETA_OVER_LAMBDA,
+ ): [
+ 'sin(θ)/λ (Å⁻¹)',
+ 'Intensity (arb. units)',
],
}
@@ -54,30 +152,110 @@
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.
+
+ 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
- def plot(
+ def plot_powder(
self,
- x,
- y_series,
- labels,
- axes_labels,
- title,
- height,
- ):
- """Render a plot.
-
- 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).
+ x: object,
+ y_series: object,
+ labels: object,
+ axes_labels: object,
+ title: str,
+ height: int | None,
+ ) -> None:
+ """
+ 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).
+
+ Parameters
+ ----------
+ x : object
+ 1D array of x-axis values.
+ y_series : object
+ Sequence of y arrays to plot.
+ labels : object
+ Identifiers corresponding to y_series.
+ axes_labels : object
+ Pair of strings for the x and y titles.
+ title : str
+ Figure title.
+ height : int | None
+ Backend-specific height (text rows or pixels).
+ """
+ pass
+
+ @abstractmethod
+ def plot_single_crystal(
+ self,
+ 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.
+
+ Suitable for single crystal diffraction data where measured
+ values are plotted against calculated values with error bars.
+
+ Parameters
+ ----------
+ x_calc : object
+ 1D array of calculated values (x-axis).
+ y_meas : object
+ 1D array of measured values (y-axis).
+ y_meas_su : object
+ 1D array of measurement uncertainties.
+ axes_labels : object
+ Pair of strings for the x and y titles.
+ title : str
+ Figure title.
+ height : int | None
+ Backend-specific height (text rows or pixels).
+ """
+ pass
+
+ @abstractmethod
+ def plot_scatter(
+ self,
+ x: object,
+ y: object,
+ sy: object,
+ axes_labels: object,
+ title: str,
+ height: int | None,
+ ) -> None:
+ """
+ Render a scatter plot with error bars.
+
+ Parameters
+ ----------
+ x : object
+ 1-D array of x-axis values.
+ y : object
+ 1-D array of y-axis values.
+ sy : object
+ 1-D array of y uncertainties.
+ axes_labels : object
+ Pair of strings for x and y axis titles.
+ title : str
+ Figure title.
+ 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 f91156d1..9d559d02 100644
--- a/src/easydiffraction/display/plotters/plotly.py
+++ b/src/easydiffraction/display/plotters/plotly.py
@@ -1,6 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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
@@ -36,16 +37,27 @@ class PlotlyPlotter(PlotterBase):
if in_pycharm():
pio.renderers.default = 'browser'
- def _get_trace(self, x, y, label):
- """Create a Plotly trace for a single data series.
+ def _get_powder_trace(
+ self,
+ x: object,
+ y: object,
+ label: str,
+ ) -> object:
+ """
+ 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 : object
+ 1D array-like of x-axis values.
+ y : object
+ 1D array- like of y-axis values.
+ label : str
+ Series identifier (``'meas'``, ``'calc'``, or ``'resid'``).
- Returns:
+ Returns
+ -------
+ object
A configured :class:`plotly.graph_objects.Scatter` trace.
"""
mode = SERIES_CONFIG[label]['mode']
@@ -63,34 +75,173 @@ def _get_trace(self, x, y, label):
return trace
- def plot(
+ def _get_single_crystal_trace(
self,
- x,
- y_series,
- labels,
- axes_labels,
- title,
- height=None,
- ):
- """Render an interactive Plotly figure.
-
- 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.
+ x_calc: object,
+ y_meas: object,
+ y_meas_su: object,
+ ) -> object:
"""
- # Intentionally unused; accepted for API compatibility
- del height
- data = []
- for idx, y in enumerate(y_series):
- label = labels[idx]
- trace = self._get_trace(x, y, label)
- data.append(trace)
+ Create a Plotly trace for single crystal diffraction data.
+
+ Parameters
+ ----------
+ x_calc : object
+ 1D array-like of calculated values (x-axis).
+ y_meas : object
+ 1D array-like of measured values (y-axis).
+ y_meas_su : object
+ 1D array-like of measurement uncertainties.
+
+ Returns
+ -------
+ object
+ A configured :class:`plotly.graph_objects.Scatter` trace
+ with markers and error bars.
+ """
+ trace = go.Scatter(
+ x=x_calc,
+ y=y_meas,
+ mode='markers',
+ marker=dict(
+ symbol='circle',
+ size=10,
+ line=dict(width=0.5),
+ color=DEFAULT_COLORS['meas'],
+ ),
+ error_y=dict(
+ type='data',
+ array=y_meas_su,
+ visible=True,
+ ),
+ hovertemplate='calc: %{x} meas: %{y} ',
+ )
+
+ return trace
+
+ def _get_diagonal_shape(self) -> dict:
+ """
+ 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
+ -------
+ dict
+ A dict configuring a diagonal line shape.
+ """
+ return dict(
+ type='line',
+ x0=0,
+ y0=0,
+ x1=1,
+ y1=1,
+ xref='paper',
+ yref='paper',
+ layer='below',
+ line=dict(width=0.5),
+ )
+
+ def _get_config(self) -> dict:
+ """
+ Return the Plotly figure configuration.
+
+ Returns
+ -------
+ dict
+ A dict with display and mode bar settings.
+ """
+ return dict(
+ displaylogo=False,
+ modeBarButtonsToRemove=[
+ 'select2d',
+ 'lasso2d',
+ 'zoomIn2d',
+ 'zoomOut2d',
+ 'autoScale2d',
+ ],
+ )
+
+ def _get_figure(
+ self,
+ data: object,
+ layout: object,
+ ) -> object:
+ """
+ Create and configure a Plotly figure.
+
+ Parameters
+ ----------
+ data : object
+ List of traces to include in the figure.
+ layout : object
+ Layout configuration dict.
+
+ Returns
+ -------
+ object
+ A configured :class:`plotly.graph_objects.Figure`.
+ """
+ fig = go.Figure(data=data, layout=layout)
+ # Format axis ticks:
+ # decimals for small numbers, grouped thousands for large
+ fig.update_xaxes(tickformat=',.6~g', separatethousands=True)
+ fig.update_yaxes(tickformat=',.6~g', separatethousands=True)
+ return fig
+
+ def _show_figure(
+ self,
+ fig: object,
+ ) -> None:
+ """
+ Display a Plotly figure.
+
+ Renders the figure using the appropriate method for the current
+ environment (browser for PyCharm, inline HTML for Jupyter).
+
+ Parameters
+ ----------
+ fig : object
+ A :class:`plotly.graph_objects.Figure` to display.
+ """
+ config = self._get_config()
+
+ if in_pycharm() or display is None or HTML is None:
+ fig.show(config=config)
+ else:
+ html_fig = pio.to_html(
+ fig,
+ include_plotlyjs='cdn',
+ full_html=False,
+ config=config,
+ )
+ display(HTML(html_fig))
- layout = go.Layout(
+ def _get_layout(
+ self,
+ title: str,
+ axes_labels: object,
+ **kwargs: object,
+ ) -> object:
+ """
+ Create a Plotly layout configuration.
+
+ Parameters
+ ----------
+ title : str
+ Figure title.
+ axes_labels : object
+ Pair of strings for the x and y titles.
+ **kwargs : object
+ Additional layout parameters (e.g., shapes).
+
+ Returns
+ -------
+ object
+ A configured :class:`plotly.graph_objects.Layout`.
+ """
+ return go.Layout(
margin=dict(
autoexpand=True,
r=30,
@@ -118,38 +269,145 @@ def plot(
mirror=True,
zeroline=False,
),
+ **kwargs,
)
- config = dict(
- displaylogo=False,
- modeBarButtonsToRemove=[
- 'select2d',
- 'lasso2d',
- 'zoomIn2d',
- 'zoomOut2d',
- 'autoScale2d',
- ],
- )
+ def plot_powder(
+ self,
+ 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.
- fig = go.Figure(
- data=data,
- layout=layout,
+ Suitable for powder diffraction data where intensity is plotted
+ against an x-axis variable (2θ, TOF, d-spacing).
+
+ Parameters
+ ----------
+ x : object
+ 1D array-like of x-axis values.
+ y_series : object
+ Sequence of y arrays to plot.
+ labels : object
+ Series identifiers corresponding to y_series.
+ axes_labels : object
+ Pair of strings for the x and y titles.
+ title : str
+ Figure title.
+ height : int | None, default=None
+ Ignored; Plotly auto-sizes based on renderer.
+ """
+ # Intentionally unused; accepted for API compatibility
+ del height
+
+ data = []
+ for idx, y in enumerate(y_series):
+ label = labels[idx]
+ trace = self._get_powder_trace(x, y, label)
+ data.append(trace)
+
+ layout = self._get_layout(
+ title,
+ axes_labels,
)
- # Format the axes ticks.
- # Keeps decimals for small numbers; groups thousands for large
- # ones
- fig.update_xaxes(tickformat=',.6~g', separatethousands=True)
- fig.update_yaxes(tickformat=',.6~g', separatethousands=True)
+ fig = self._get_figure(data, layout)
+ self._show_figure(fig)
- # Show the figure
- if in_pycharm() or display is None or HTML is None:
- fig.show(config=config)
- else:
- html_fig = pio.to_html(
- fig,
- include_plotlyjs='cdn',
- full_html=False,
- config=config,
+ def plot_single_crystal(
+ self,
+ 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.
+
+ Suitable for single crystal diffraction data where measured
+ values are plotted against calculated values with error bars and
+ a diagonal reference line.
+
+ Parameters
+ ----------
+ x_calc : object
+ 1D array-like of calculated values (x-axis).
+ y_meas : object
+ 1D array-like of measured values (y-axis).
+ y_meas_su : object
+ 1D array-like of measurement uncertainties.
+ axes_labels : object
+ Pair of strings for the x and y titles.
+ title : str
+ Figure title.
+ height : int | None, default=None
+ Ignored; Plotly auto-sizes based on renderer.
+ """
+ # Intentionally unused; accepted for API compatibility
+ del height
+
+ data = [
+ self._get_single_crystal_trace(
+ x_calc,
+ y_meas,
+ y_meas_su,
)
- display(HTML(html_fig))
+ ]
+
+ layout = self._get_layout(
+ title,
+ axes_labels,
+ shapes=[self._get_diagonal_shape()],
+ )
+
+ fig = self._get_figure(data, layout)
+ self._show_figure(fig)
+
+ def plot_scatter(
+ self,
+ x: object,
+ y: object,
+ sy: object,
+ axes_labels: object,
+ title: str,
+ height: int | None = None,
+ ) -> None:
+ """Render a scatter plot with error bars via Plotly."""
+ _ = height # not used by Plotly backend
+
+ trace = go.Scatter(
+ x=x,
+ y=y,
+ mode='markers+lines',
+ marker=dict(
+ symbol='circle',
+ size=10,
+ line=dict(width=0.5),
+ color=DEFAULT_COLORS['meas'],
+ ),
+ line=dict(
+ width=1,
+ color=DEFAULT_COLORS['meas'],
+ ),
+ error_y=dict(
+ type='data',
+ array=sy,
+ visible=True,
+ ),
+ hovertemplate='x: %{x} y: %{y} ',
+ )
+
+ layout = self._get_layout(
+ title,
+ axes_labels,
+ )
+
+ fig = self._get_figure(trace, layout)
+ self._show_figure(fig)
diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py
index c0c98deb..e4b1ad34 100644
--- a/src/easydiffraction/display/plotting.py
+++ b/src/easydiffraction/display/plotting.py
@@ -1,6 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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.
@@ -18,6 +19,8 @@
from easydiffraction.display.plotters.base import DEFAULT_HEIGHT
from easydiffraction.display.plotters.base import DEFAULT_MAX
from easydiffraction.display.plotters.base import DEFAULT_MIN
+from easydiffraction.display.plotters.base import DEFAULT_X_AXIS
+from easydiffraction.display.plotters.base import XAxisType
from easydiffraction.display.plotters.plotly import PlotlyPlotter
from easydiffraction.display.tables import TableRenderer
from easydiffraction.utils.environment import in_jupyter
@@ -26,6 +29,8 @@
class PlotterEngineEnum(str, Enum):
+ """Available plotting engine backends."""
+
ASCII = 'asciichartpy'
PLOTLY = 'plotly'
@@ -50,7 +55,11 @@ def description(self) -> str:
class Plotter(RendererBase):
"""User-facing plotting facade backed by concrete plotters."""
- def __init__(self):
+ # ------------------------------------------------------------------
+ # Private special methods
+ # ------------------------------------------------------------------
+
+ def __init__(self) -> None:
super().__init__()
# X-axis limits
self._x_min = DEFAULT_MIN
@@ -58,6 +67,10 @@ def __init__(self):
# Chart height
self.height = DEFAULT_HEIGHT
+ # ------------------------------------------------------------------
+ # Private class methods
+ # ------------------------------------------------------------------
+
@classmethod
def _factory(cls) -> type[RendererFactoryBase]: # type: ignore[override]
return PlotterFactory
@@ -66,32 +79,231 @@ def _factory(cls) -> type[RendererFactoryBase]: # type: ignore[override]
def _default_engine(cls) -> str:
return PlotterEngineEnum.default().value
- def show_config(self):
- """Display the current plotting configuration."""
- headers = [
- ('Parameter', 'left'),
- ('Value', 'left'),
- ]
- rows = [
- ['Plotting engine', self.engine],
- ['x-axis limits', f'[{self.x_min}, {self.x_max}]'],
- ['Chart height', self.height],
- ]
- df = pd.DataFrame(rows, columns=pd.MultiIndex.from_tuples(headers))
- console.paragraph('Current plotter configuration')
- TableRenderer.get().render(df)
+ # ------------------------------------------------------------------
+ # Private helper methods
+ # ------------------------------------------------------------------
+
+ 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.
+
+ Parameters
+ ----------
+ pattern : object
+ Data pattern object (needs ``intensity_meas``).
+ x_array : object
+ Full x-axis array.
+ x_min : object
+ Current minimum (may be ``None``).
+ 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):
+ max_intensity_pos = np.argmax(pattern.intensity_meas)
+ half_range = 50
+ start = max(0, max_intensity_pos - half_range)
+ end = min(len(x_array) - 1, max_intensity_pos + half_range)
+ x_min = x_array[start]
+ x_max = x_array[end]
+ return x_min, x_max
+
+ def _filtered_y_array(
+ self,
+ y_array: object,
+ x_array: object,
+ x_min: object,
+ x_max: object,
+ ) -> object:
+ """
+ Filter an array by the inclusive x-range limits.
+
+ Parameters
+ ----------
+ y_array : object
+ 1D array-like of y values.
+ x_array : object
+ 1D array-like of x values (same length as ``y_array``).
+ x_min : object
+ Minimum x limit (or ``None`` to use default).
+ 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]``.
+ """
+ if x_min is None:
+ x_min = self.x_min
+ if x_max is None:
+ x_max = self.x_max
+
+ mask = (x_array >= x_min) & (x_array <= x_max)
+ filtered_y_array = y_array[mask]
+
+ return filtered_y_array
+
+ def _get_axes_labels(
+ self,
+ sample_form: object,
+ scattering_type: object,
+ x_axis: object,
+ ) -> list:
+ """Look up axis labels for the experiment / x-axis."""
+ return DEFAULT_AXES_LABELS[(sample_form, scattering_type, x_axis)]
+
+ def _prepare_powder_data(
+ self,
+ 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.
+
+ Parameters
+ ----------
+ pattern : object
+ Data pattern object with intensity arrays.
+ expt_name : str
+ Experiment name for error messages.
+ expt_type : object
+ Experiment type with sample_form, scattering, and beam
+ enums.
+ x_min : object
+ Optional minimum x-axis limit.
+ x_max : object
+ Optional maximum x-axis limit.
+ x : object
+ Explicit x-axis type or ``None``.
+ need_meas : bool, default=False
+ Whether ``intensity_meas`` is required.
+ need_calc : bool, default=False
+ Whether ``intensity_calc`` is required.
+ 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.
+ """
+ x_axis, x_name, sample_form, scattering_type, _ = self._resolve_x_axis(expt_type, x)
+
+ # Get x-array from pattern
+ x_array = getattr(pattern, x_axis, None)
+ if x_array is None:
+ log.error(f'No {x_name} data available for experiment {expt_name}')
+ return None
+
+ # Validate required intensities
+ if need_meas and pattern.intensity_meas is None:
+ log.error(f'No measured data available for experiment {expt_name}')
+ return None
+ if need_calc and pattern.intensity_calc is None:
+ log.error(f'No calculated data available for experiment {expt_name}')
+ return None
+
+ # Auto-range for ASCII engine
+ x_min, x_max = self._auto_x_range_for_ascii(pattern, x_array, x_min, x_max)
+
+ # Filter x
+ x_filtered = self._filtered_y_array(x_array, x_array, x_min, x_max)
+
+ # Filter y arrays and build series / labels
+ y_series = []
+ y_labels = []
+
+ y_meas = None
+ if need_meas:
+ y_meas = self._filtered_y_array(pattern.intensity_meas, x_array, x_min, x_max)
+ y_series.append(y_meas)
+ y_labels.append('meas')
+
+ y_calc = None
+ if need_calc:
+ y_calc = self._filtered_y_array(pattern.intensity_calc, x_array, x_min, x_max)
+ y_series.append(y_calc)
+ y_labels.append('calc')
+
+ if show_residual and y_meas is not None and y_calc is not None:
+ y_resid = y_meas - y_calc
+ y_series.append(y_resid)
+ y_labels.append('resid')
+
+ axes_labels = self._get_axes_labels(sample_form, scattering_type, x_axis)
+
+ return {
+ 'x_filtered': x_filtered,
+ 'y_series': y_series,
+ 'y_labels': y_labels,
+ 'axes_labels': axes_labels,
+ 'x_axis': x_axis,
+ }
+
+ def _resolve_x_axis(self, expt_type: object, x: object) -> tuple:
+ """
+ Determine the x-axis type from experiment metadata.
+
+ Parameters
+ ----------
+ expt_type : object
+ Experiment type with sample_form, scattering_type, and
+ beam_mode enums.
+ 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)``.
+ """
+ sample_form = expt_type.sample_form.value
+ scattering_type = expt_type.scattering_type.value
+ beam_mode = expt_type.beam_mode.value
+ x_axis = DEFAULT_X_AXIS[(sample_form, scattering_type, beam_mode)] if x is None else x
+ x_name = getattr(x_axis, 'value', x_axis)
+ return x_axis, x_name, sample_form, scattering_type, beam_mode
+
+ # ------------------------------------------------------------------
+ # Public properties
+ # ------------------------------------------------------------------
@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):
- """Set the minimum x-axis limit.
+ def x_min(self, value: object) -> None:
+ """
+ Set the minimum x-axis limit.
- Args:
- value: Minimum limit or ``None`` to reset to default.
+ Parameters
+ ----------
+ value : object
+ Minimum limit or ``None`` to reset to default.
"""
if value is not None:
self._x_min = value
@@ -99,16 +311,19 @@ 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):
- """Set the maximum x-axis limit.
+ def x_max(self, value: object) -> None:
+ """
+ Set the maximum x-axis limit.
- Args:
- value: Maximum limit or ``None`` to reset to default.
+ Parameters
+ ----------
+ value : object
+ Maximum limit or ``None`` to reset to default.
"""
if value is not None:
self._x_max = value
@@ -116,311 +331,344 @@ 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):
- """Set plot height.
+ def height(self, value: object) -> None:
+ """
+ Set plot height.
- Args:
- value: Height value or ``None`` to reset to default.
+ Parameters
+ ----------
+ value : object
+ Height value or ``None`` to reset to default.
"""
if value is not None:
self._height = value
else:
self._height = DEFAULT_HEIGHT
+ # ------------------------------------------------------------------
+ # Public methods
+ # ------------------------------------------------------------------
+
+ def show_config(self) -> None:
+ """Display the current plotting configuration."""
+ headers = [
+ ('Parameter', 'left'),
+ ('Value', 'left'),
+ ]
+ rows = [
+ ['Plotting engine', self.engine],
+ ['x-axis limits', f'[{self.x_min}, {self.x_max}]'],
+ ['Chart height', self.height],
+ ]
+ df = pd.DataFrame(rows, columns=pd.MultiIndex.from_tuples(headers))
+ console.paragraph('Current plotter configuration')
+ TableRenderer.get().render(df)
+
def plot_meas(
self,
- pattern,
- expt_name,
- expt_type,
- x_min=None,
- x_max=None,
- d_spacing=False,
- ):
- """Plot measured pattern using the current engine.
-
- Args:
- pattern: Object with ``x`` and ``meas`` arrays (and
- ``d`` when ``d_spacing`` is true).
- 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.
- d_spacing: If ``True``, plot against d-spacing values.
+ pattern: object,
+ expt_name: str,
+ expt_type: object,
+ x_min: object = None,
+ x_max: object = None,
+ x: object = None,
+ ) -> None:
"""
- if pattern.x is None:
- log.error(f'No data available for experiment {expt_name}')
- return
- if pattern.meas is None:
- log.error(f'No measured data available for experiment {expt_name}')
- return
-
- # Select x-axis data based on d-spacing or original x values
- x_array = pattern.d if d_spacing else pattern.x
-
- # For asciichartpy, if x_min or x_max is not provided, center
- # around the maximum intensity peak
- if self._engine == 'asciichartpy' and (x_min is None or x_max is None):
- max_intensity_pos = np.argmax(pattern.meas)
- half_range = 50
- start = max(0, max_intensity_pos - half_range)
- end = min(len(x_array) - 1, max_intensity_pos + half_range)
- x_min = x_array[start]
- x_max = x_array[end]
-
- # Filter x, y_meas, and y_calc based on x_min and x_max
- x = self._filtered_y_array(
- y_array=x_array,
- x_array=x_array,
- x_min=x_min,
- x_max=x_max,
- )
- y_meas = self._filtered_y_array(
- y_array=pattern.meas,
- x_array=x_array,
- x_min=x_min,
- x_max=x_max,
+ Plot measured pattern using the current engine.
+
+ Parameters
+ ----------
+ pattern : object
+ 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
+ Experiment type with scattering/beam enums.
+ x_min : object, default=None
+ Optional minimum x-axis limit.
+ x_max : object, default=None
+ Optional maximum x-axis limit.
+ x : object, default=None
+ X-axis type (``'two_theta'``, ``'time_of_flight'``, or
+ ``'d_spacing'``). If ``None``, auto-detected from beam mode.
+ """
+ ctx = self._prepare_powder_data(
+ pattern,
+ expt_name,
+ expt_type,
+ x_min,
+ x_max,
+ x,
+ need_meas=True,
)
+ if ctx is None:
+ return
- y_series = [y_meas]
- y_labels = ['meas']
-
- if d_spacing:
- axes_labels = DEFAULT_AXES_LABELS[
- (
- expt_type.scattering_type.value,
- 'd-spacing',
- )
- ]
- else:
- axes_labels = DEFAULT_AXES_LABELS[
- (
- expt_type.scattering_type.value,
- expt_type.beam_mode.value,
- )
- ]
-
- # TODO: Before, it was self._plotter.plot. Check what is better.
- self._backend.plot(
- x=x,
- y_series=y_series,
- labels=y_labels,
- axes_labels=axes_labels,
+ self._backend.plot_powder(
+ x=ctx['x_filtered'],
+ y_series=ctx['y_series'],
+ labels=ctx['y_labels'],
+ axes_labels=ctx['axes_labels'],
title=f"Measured data for experiment 🔬 '{expt_name}'",
height=self.height,
)
def plot_calc(
self,
- pattern,
- expt_name,
- expt_type,
- x_min=None,
- x_max=None,
- d_spacing=False,
- ):
- """Plot calculated pattern using the current engine.
-
- Args:
- pattern: Object with ``x`` and ``calc`` arrays (and
- ``d`` when ``d_spacing`` is true).
- 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.
- d_spacing: If ``True``, plot against d-spacing values.
+ pattern: object,
+ expt_name: str,
+ expt_type: object,
+ x_min: object = None,
+ x_max: object = None,
+ x: object = None,
+ ) -> None:
"""
- if pattern.x is None:
- log.error(f'No data available for experiment {expt_name}')
- return
- if pattern.calc is None:
- log.error(f'No calculated data available for experiment {expt_name}')
- return
-
- # Select x-axis data based on d-spacing or original x values
- x_array = pattern.d if d_spacing else pattern.x
-
- # For asciichartpy, if x_min or x_max is not provided, center
- # around the maximum intensity peak
- if self._engine == 'asciichartpy' and (x_min is None or x_max is None):
- max_intensity_pos = np.argmax(pattern.meas)
- half_range = 50
- start = max(0, max_intensity_pos - half_range)
- end = min(len(x_array) - 1, max_intensity_pos + half_range)
- x_min = x_array[start]
- x_max = x_array[end]
-
- # Filter x, y_meas, and y_calc based on x_min and x_max
- x = self._filtered_y_array(
- y_array=x_array,
- x_array=x_array,
- x_min=x_min,
- x_max=x_max,
- )
- y_calc = self._filtered_y_array(
- y_array=pattern.calc,
- x_array=x_array,
- x_min=x_min,
- x_max=x_max,
+ Plot calculated pattern using the current engine.
+
+ Parameters
+ ----------
+ pattern : object
+ 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
+ Experiment type with scattering/beam enums.
+ x_min : object, default=None
+ Optional minimum x-axis limit.
+ x_max : object, default=None
+ Optional maximum x-axis limit.
+ x : object, default=None
+ X-axis type (``'two_theta'``, ``'time_of_flight'``, or
+ ``'d_spacing'``). If ``None``, auto-detected from beam mode.
+ """
+ ctx = self._prepare_powder_data(
+ pattern,
+ expt_name,
+ expt_type,
+ x_min,
+ x_max,
+ x,
+ need_calc=True,
)
+ if ctx is None:
+ return
- y_series = [y_calc]
- y_labels = ['calc']
-
- if d_spacing:
- axes_labels = DEFAULT_AXES_LABELS[
- (
- expt_type.scattering_type.value,
- 'd-spacing',
- )
- ]
- else:
- axes_labels = DEFAULT_AXES_LABELS[
- (
- expt_type.scattering_type.value,
- expt_type.beam_mode.value,
- )
- ]
-
- self._backend.plot(
- x=x,
- y_series=y_series,
- labels=y_labels,
- axes_labels=axes_labels,
+ self._backend.plot_powder(
+ x=ctx['x_filtered'],
+ y_series=ctx['y_series'],
+ labels=ctx['y_labels'],
+ axes_labels=ctx['axes_labels'],
title=f"Calculated data for experiment 🔬 '{expt_name}'",
height=self.height,
)
def plot_meas_vs_calc(
self,
- pattern,
- expt_name,
- expt_type,
- x_min=None,
- x_max=None,
- show_residual=False,
- d_spacing=False,
- ):
- """Plot measured and calculated series and optional residual.
-
- Args:
- pattern: Object with ``x``, ``meas`` and ``calc`` arrays
- (and ``d`` when ``d_spacing`` is true).
- 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.
- show_residual: If ``True``, add residual series.
- d_spacing: If ``True``, plot against d-spacing values.
+ pattern: object,
+ expt_name: str,
+ expt_type: object,
+ x_min: object = None,
+ x_max: object = None,
+ show_residual: bool = False,
+ x: object = None,
+ ) -> None:
"""
- if pattern.x is None:
- log.error(f'No data available for experiment {expt_name}')
- return
- if pattern.meas is None:
+ Plot measured and calculated series and optional residual.
+
+ 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
+
+ Parameters
+ ----------
+ pattern : object
+ Data pattern object with meas/calc arrays.
+ expt_name : str
+ Experiment name for the title.
+ expt_type : object
+ Experiment type with sample_form, scattering, and beam
+ enums.
+ x_min : object, default=None
+ Optional minimum x-axis limit.
+ x_max : object, default=None
+ Optional maximum x-axis limit.
+ show_residual : bool, default=False
+ If ``True``, add residual series (powder only).
+ x : object, default=None
+ 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)
+
+ # Validate required data (before x-array check, matching
+ # original behavior for plot_meas_vs_calc)
+ if pattern.intensity_meas is None:
log.error(f'No measured data available for experiment {expt_name}')
return
- if pattern.calc is None:
+ if pattern.intensity_calc is None:
log.error(f'No calculated data available for experiment {expt_name}')
return
- # Select x-axis data based on d-spacing or original x values
- x_array = pattern.d if d_spacing else pattern.x
-
- # For asciichartpy, if x_min or x_max is not provided, center
- # around the maximum intensity peak
- if self._engine == 'asciichartpy' and (x_min is None or x_max is None):
- max_intensity_pos = np.argmax(pattern.meas)
- half_range = 50
- start = max(0, max_intensity_pos - half_range)
- end = min(len(x_array) - 1, max_intensity_pos + half_range)
- x_min = x_array[start]
- x_max = x_array[end]
+ title = f"Measured vs Calculated data for experiment 🔬 '{expt_name}'"
+
+ # Single crystal scatter plot (I²calc vs I²meas)
+ if x_axis == XAxisType.INTENSITY_CALC or x_axis == 'intensity_calc':
+ axes_labels = self._get_axes_labels(sample_form, scattering_type, x_axis)
+
+ if pattern.intensity_meas_su is None:
+ log.warning(f'No measurement uncertainties for experiment {expt_name}')
+ meas_su = np.zeros_like(pattern.intensity_meas)
+ else:
+ meas_su = pattern.intensity_meas_su
+
+ self._backend.plot_single_crystal(
+ x_calc=pattern.intensity_calc,
+ y_meas=pattern.intensity_meas,
+ y_meas_su=meas_su,
+ axes_labels=axes_labels,
+ title=f"Measured vs Calculated data for experiment 🔬 '{expt_name}'",
+ height=self.height,
+ )
+ return
- # Filter x, y_meas, and y_calc based on x_min and x_max
- x = self._filtered_y_array(
- y_array=x_array,
- x_array=x_array,
- x_min=x_min,
- x_max=x_max,
- )
- y_meas = self._filtered_y_array(
- y_array=pattern.meas,
- x_array=x_array,
- x_min=x_min,
- x_max=x_max,
- )
- y_calc = self._filtered_y_array(
- y_array=pattern.calc,
- x_array=x_array,
- x_min=x_min,
- x_max=x_max,
+ # Line plot (PD or SC with d_spacing/sin_theta_over_lambda)
+ # TODO: Rename from _prepare_powder_data as it also supports
+ # single crystal line plots
+ ctx = self._prepare_powder_data(
+ pattern,
+ expt_name,
+ expt_type,
+ x_min,
+ x_max,
+ x,
+ need_meas=True,
+ need_calc=True,
+ show_residual=show_residual,
)
+ if ctx is None:
+ return
- y_series = [y_meas, y_calc]
- y_labels = ['meas', 'calc']
-
- if d_spacing:
- axes_labels = DEFAULT_AXES_LABELS[
- (
- expt_type.scattering_type.value,
- 'd-spacing',
- )
- ]
- else:
- axes_labels = DEFAULT_AXES_LABELS[
- (
- expt_type.scattering_type.value,
- expt_type.beam_mode.value,
- )
- ]
-
- if show_residual:
- y_resid = y_meas - y_calc
- y_series.append(y_resid)
- y_labels.append('resid')
+ self._backend.plot_powder(
+ x=ctx['x_filtered'],
+ y_series=ctx['y_series'],
+ labels=ctx['y_labels'],
+ axes_labels=ctx['axes_labels'],
+ title=title,
+ height=self.height,
+ )
- self._backend.plot(
+ def plot_param_series(
+ self,
+ unique_name: str,
+ versus_name: str | None,
+ experiments: object,
+ parameter_snapshots: dict[str, dict[str, dict]],
+ ) -> None:
+ """
+ Plot a parameter's value across sequential fit results.
+
+ Parameters
+ ----------
+ unique_name : str
+ Unique name of the parameter to plot.
+ versus_name : str | None
+ Name of the diffrn descriptor to use as the x-axis (e.g.
+ ``'ambient_temperature'``). When ``None``, the experiment
+ sequence index is used instead.
+ experiments : object
+ Experiments collection for accessing diffrn conditions.
+ parameter_snapshots : dict[str, dict[str, dict]]
+ Per-experiment parameter value snapshots keyed by experiment
+ name, then by parameter unique name.
+ """
+ x = []
+ y = []
+ sy = []
+ axes_labels = []
+ title = ''
+
+ for idx, expt_name in enumerate(parameter_snapshots, start=1):
+ experiment = experiments[expt_name]
+ diffrn = experiment.diffrn
+
+ x_axis_param = self._resolve_diffrn_descriptor(diffrn, versus_name)
+
+ if x_axis_param is not None and x_axis_param.value is not None:
+ value = x_axis_param.value
+ else:
+ value = idx
+ x.append(value)
+
+ param_data = parameter_snapshots[expt_name][unique_name]
+ y.append(param_data['value'])
+ sy.append(param_data['uncertainty'])
+
+ if x_axis_param is not None:
+ axes_labels = [
+ x_axis_param.description or x_axis_param.name,
+ f'Parameter value ({param_data["units"]})',
+ ]
+ else:
+ axes_labels = [
+ 'Experiment No.',
+ f'Parameter value ({param_data["units"]})',
+ ]
+
+ title = f"Parameter '{unique_name}' across fit results"
+
+ self._backend.plot_scatter(
x=x,
- y_series=y_series,
- labels=y_labels,
+ y=y,
+ sy=sy,
axes_labels=axes_labels,
- title=f"Measured vs Calculated data for experiment 🔬 '{expt_name}'",
+ title=title,
height=self.height,
)
- def _filtered_y_array(
- self,
- y_array,
- x_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).
-
- Returns:
- Filtered ``y_array`` values where ``x_array`` lies within
- ``[x_min, x_max]``.
+ @staticmethod
+ def _resolve_diffrn_descriptor(
+ diffrn: object,
+ name: str | None,
+ ) -> object | None:
"""
- if x_min is None:
- x_min = self.x_min
- if x_max is None:
- x_max = self.x_max
-
- mask = (x_array >= x_min) & (x_array <= x_max)
- filtered_y_array = y_array[mask]
-
- return filtered_y_array
+ Return the diffrn descriptor matching *name*, or ``None``.
+
+ Parameters
+ ----------
+ diffrn : object
+ The diffrn category of an experiment.
+ name : str | None
+ Descriptor name (e.g. ``'ambient_temperature'``).
+
+ Returns
+ -------
+ object | None
+ The matching ``NumericDescriptor``, or ``None`` when *name*
+ is ``None`` or unrecognised.
+ """
+ if name is None:
+ return None
+ if name == 'ambient_temperature':
+ return diffrn.ambient_temperature
+ if name == 'ambient_pressure':
+ return diffrn.ambient_pressure
+ if name == 'ambient_magnetic_field':
+ return diffrn.ambient_magnetic_field
+ if name == 'ambient_electric_field':
+ return diffrn.ambient_electric_field
+ return None
class PlotterFactory(RendererFactoryBase):
diff --git a/src/easydiffraction/display/tablers/__init__.py b/src/easydiffraction/display/tablers/__init__.py
index 93d84417..6471fbff 100644
--- a/src/easydiffraction/display/tablers/__init__.py
+++ b/src/easydiffraction/display/tablers/__init__.py
@@ -1,10 +1,11 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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:
+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 2b710d8f..869c5a17 100644
--- a/src/easydiffraction/display/tablers/base.py
+++ b/src/easydiffraction/display/tablers/base.py
@@ -1,6 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# 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.
@@ -10,7 +11,6 @@
from abc import ABC
from abc import abstractmethod
-from typing import Any
from IPython import get_ipython
from rich.color import Color
@@ -19,11 +19,11 @@
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.
+ Subclasses implement the ``render`` method which receives an index-
+ aware pandas DataFrame and the alignment for each column header.
"""
FLOAT_PRECISION = 5
@@ -34,20 +34,26 @@ def __init__(self) -> None:
super().__init__()
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.
+ def _format_value(self, value: object) -> object:
+ """
+ Format floats with fixed precision and others as strings.
- Args:
- value: Cell value to format.
+ Parameters
+ ----------
+ value : object
+ Cell value to format.
- Returns:
+ Returns
+ -------
+ object
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)
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).
"""
@@ -62,14 +68,18 @@ 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.
+ def _rich_to_hex(self, color: str) -> str:
+ """
+ Convert a Rich color name to a CSS-style hex string.
- Args:
- color: Rich color name or specification parsable by
- :mod:`rich`.
+ Parameters
+ ----------
+ color : str
+ Rich color name or specification parsable by :mod:`rich`.
- Returns:
+ Returns
+ -------
+ str
Hex color string in the form ``#RRGGBB``.
"""
c = Color.parse(color)
@@ -90,21 +100,27 @@ def _pandas_border_color(self) -> str:
@abstractmethod
def render(
self,
- alignments,
- df,
- display_handle: Any | None = None,
- ) -> 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.
-
- Returns:
+ alignments: object,
+ df: object,
+ display_handle: object | None = None,
+ ) -> object:
+ """
+ Render the provided DataFrame with backend-specific styling.
+
+ Parameters
+ ----------
+ alignments : object
+ Iterable of column justifications (e.g., ``'left'`` or
+ ``'center'``) corresponding to the data columns.
+ df : object
+ Index-aware DataFrame with data to render.
+ 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 da05b5e8..4efef148 100644
--- a/src/easydiffraction/display/tablers/pandas.py
+++ b/src/easydiffraction/display/tablers/pandas.py
@@ -1,11 +1,9 @@
-# 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."""
from __future__ import annotations
-from typing import Any
-
try:
from IPython.display import HTML
from IPython.display import display
@@ -22,13 +20,18 @@ 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 : str
+ CSS color value (e.g., ``#RRGGBB``) to use for borders and
+ header accents.
- Returns:
+ Returns
+ -------
+ list[dict]
A list of ``Styler.set_table_styles`` dictionaries.
"""
return [
@@ -76,15 +79,21 @@ 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:
+ def _build_header_alignment_styles(self, df: object, alignments: object) -> list[dict]:
+ """
+ Generate header cell alignment styles per column.
+
+ Parameters
+ ----------
+ df : object
+ DataFrame whose columns are being rendered.
+ 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 [
@@ -95,15 +104,22 @@ 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):
- """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:
+ def _apply_styling(self, df: object, alignments: object, color: str) -> object:
+ """
+ Build a configured Styler with alignments and base styles.
+
+ Parameters
+ ----------
+ df : object
+ DataFrame to style.
+ alignments : object
+ Iterable of text alignment values for columns.
+ 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)
@@ -120,17 +136,20 @@ def _apply_styling(self, df, alignments, color: str):
)
return styler
- def _update_display(self, styler, display_handle) -> None:
- """Single, consistent update path for Jupyter.
+ 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()``.
- Args:
- styler: Configured DataFrame Styler to be rendered.
- display_handle: Optional IPython DisplayHandle used for
- in-place updates.
+ Parameters
+ ----------
+ styler : object
+ Configured DataFrame Styler to be rendered.
+ display_handle : object
+ Optional IPython DisplayHandle used for in-place updates.
"""
# Handle with update() method
if display_handle is not None and hasattr(display_handle, 'update'):
@@ -152,17 +171,27 @@ def _update_display(self, styler, display_handle) -> None:
def render(
self,
- alignments,
- 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.
+ alignments: object,
+ df: object,
+ display_handle: object | None = None,
+ ) -> object:
+ """
+ Render a styled DataFrame.
+
+ Parameters
+ ----------
+ alignments : object
+ Iterable of column justifications (e.g. 'left').
+ 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.
+
+ 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 e7a8afbb..fba2a400 100644
--- a/src/easydiffraction/display/tablers/rich.py
+++ b/src/easydiffraction/display/tablers/rich.py
@@ -1,11 +1,10 @@
-# 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."""
from __future__ import annotations
import io
-from typing import Any
from rich.box import Box
from rich.console import Console
@@ -41,16 +40,20 @@ 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.
+ 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.
+ Parameters
+ ----------
+ table : Table
+ Rich :class:`~rich.table.Table` to export.
- Returns:
+ Returns
+ -------
+ str
HTML string with inline styles for notebook display.
"""
tmp = Console(force_jupyter=False, record=True, file=io.StringIO())
@@ -63,15 +66,22 @@ 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:
+ def _build_table(self, df: object, alignments: object, color: str) -> Table:
+ """
+ Construct a Rich Table with formatted data and alignment.
+
+ Parameters
+ ----------
+ df : object
+ DataFrame-like object providing rows to render.
+ alignments : object
+ Iterable of text alignment values for columns.
+ color : str
+ Rich color name used for borders/index style.
+
+ Returns
+ -------
+ Table
A :class:`~rich.table.Table` configured for display.
"""
table = Table(
@@ -96,20 +106,23 @@ 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.
-
- - 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).
+ 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.
+
+ Parameters
+ ----------
+ table : Table
+ Rich :class:`~rich.table.Table` to display.
+ display_handle : object
+ 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'):
@@ -136,17 +149,26 @@ def _update_display(self, table: Table, display_handle) -> None:
def render(
self,
- alignments,
- 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.
+ alignments: object,
+ df: object,
+ display_handle: object = None,
+ ) -> object:
+ """
+ Render a styled table using Rich.
+
+ Parameters
+ ----------
+ alignments : object
+ Iterable of text-align values for columns.
+ df : object
+ Index-aware DataFrame to render.
+ 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 e149609d..1ae39594 100644
--- a/src/easydiffraction/display/tables.py
+++ b/src/easydiffraction/display/tables.py
@@ -1,11 +1,10 @@
-# 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)."""
from __future__ import annotations
from enum import Enum
-from typing import Any
import pandas as pd
@@ -19,12 +18,15 @@
class TableEngineEnum(str, Enum):
+ """Available table rendering backends."""
+
RICH = 'rich'
PANDAS = 'pandas'
@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.
"""
@@ -35,6 +37,14 @@ 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:
@@ -65,17 +75,23 @@ def show_config(self) -> None:
console.paragraph('Current tabler configuration')
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:
+ def render(self, df: object, display_handle: object | None = None) -> object:
+ """
+ Render a DataFrame as a table using the active backend.
+
+ Parameters
+ ----------
+ df : object
+ 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).
+
+ Returns
+ -------
+ object
Backend-specific return value (usually ``None``).
"""
# Work on a copy to avoid mutating the original DataFrame
@@ -98,11 +114,11 @@ 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).
+ - 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/display/utils.py b/src/easydiffraction/display/utils.py
index 8c06f384..17c6fa94 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
@@ -18,9 +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/experiments/__init__.py b/src/easydiffraction/experiments/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/experiments/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/experiments/categories/__init__.py b/src/easydiffraction/experiments/categories/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/experiments/categories/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/experiments/categories/background/__init__.py b/src/easydiffraction/experiments/categories/background/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/experiments/categories/background/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/experiments/categories/background/factory.py b/src/easydiffraction/experiments/categories/background/factory.py
deleted file mode 100644
index 716260f4..00000000
--- a/src/easydiffraction/experiments/categories/background/factory.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Background collection entry point (public facade).
-
-End users should import Background classes from this module. Internals
-live under the package
-`easydiffraction.experiments.category_collections.background_types`
-and are re-exported here for a stable and readable API.
-"""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-from typing import Optional
-
-from easydiffraction.experiments.categories.background.enums import BackgroundTypeEnum
-
-if TYPE_CHECKING:
- from easydiffraction.experiments.categories.background import BackgroundBase
-
-
-class BackgroundFactory:
- """Create background collections by type."""
-
- BT = BackgroundTypeEnum
-
- @classmethod
- def _supported_map(cls) -> dict:
- """Return mapping of enum values to concrete background
- classes.
- """
- # Lazy import to avoid circulars
- from easydiffraction.experiments.categories.background.chebyshev import (
- ChebyshevPolynomialBackground,
- )
- from easydiffraction.experiments.categories.background.line_segment import (
- LineSegmentBackground,
- )
-
- return {
- cls.BT.LINE_SEGMENT: LineSegmentBackground,
- cls.BT.CHEBYSHEV: ChebyshevPolynomialBackground,
- }
-
- @classmethod
- def create(
- cls,
- background_type: Optional[BackgroundTypeEnum] = None,
- ) -> BackgroundBase:
- """Instantiate a background collection of requested type.
-
- If type is None, the default enum value is used.
- """
- if background_type is None:
- background_type = BackgroundTypeEnum.default()
-
- supported = cls._supported_map()
- if background_type not in supported:
- supported_types = list(supported.keys())
- raise ValueError(
- f"Unsupported background type: '{background_type}'. "
- f'Supported background types: {[bt.value for bt in supported_types]}'
- )
-
- background_class = supported[background_type]
- return background_class()
diff --git a/src/easydiffraction/experiments/categories/data/bragg_pd.py b/src/easydiffraction/experiments/categories/data/bragg_pd.py
deleted file mode 100644
index da92da30..00000000
--- a/src/easydiffraction/experiments/categories/data/bragg_pd.py
+++ /dev/null
@@ -1,455 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-import numpy as np
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import MembershipValidator
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.core.validation import RegexValidator
-from easydiffraction.io.cif.handler import CifHandler
-from easydiffraction.utils.utils import tof_to_d
-from easydiffraction.utils.utils import twotheta_to_d
-
-
-class PdDataPointBaseMixin:
- """Single base data point mixin for powder diffraction data."""
-
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
-
- self._point_id = StringDescriptor(
- name='point_id',
- description='Identifier for this data point in the dataset.',
- value_spec=AttributeSpec(
- type_=DataTypes.STRING,
- default='0',
- # TODO: the following pattern is valid for dict key
- # (keywords are not checked). CIF label is less strict.
- # Do we need conversion between CIF and internal label?
- content_validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_data.point_id',
- ]
- ),
- )
- self._d_spacing = NumericDescriptor(
- name='d_spacing',
- description='d-spacing value corresponding to this data point.',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_proc.d_spacing',
- ]
- ),
- )
- self._intensity_meas = NumericDescriptor(
- name='intensity_meas',
- description='Intensity recorded at each measurement point as a function of angle/time',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_meas.intensity_total',
- '_pd_proc.intensity_norm',
- ]
- ),
- )
- self._intensity_meas_su = NumericDescriptor(
- name='intensity_meas_su',
- description='Standard uncertainty of the measured intensity at this data point.',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=1.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_meas.intensity_total_su',
- '_pd_proc.intensity_norm_su',
- ]
- ),
- )
- self._intensity_calc = NumericDescriptor(
- name='intensity_calc',
- description='Intensity value for a computed diffractogram at this data point.',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_calc.intensity_total',
- ]
- ),
- )
- self._intensity_bkg = NumericDescriptor(
- name='intensity_bkg',
- description='Intensity value for a computed background at this data point.',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_calc.intensity_bkg',
- ]
- ),
- )
- self._calc_status = StringDescriptor(
- name='calc_status',
- description='Status code of the data point in the calculation process.',
- value_spec=AttributeSpec(
- type_=DataTypes.STRING,
- default='incl', # TODO: Make Enum
- content_validator=MembershipValidator(allowed=['incl', 'excl']),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_data.refinement_status', # TODO: rename to calc_status
- ]
- ),
- )
-
- @property
- def point_id(self) -> StringDescriptor:
- return self._point_id
-
- @property
- def d_spacing(self) -> NumericDescriptor:
- return self._d_spacing
-
- @property
- def intensity_meas(self) -> NumericDescriptor:
- return self._intensity_meas
-
- @property
- def intensity_meas_su(self) -> NumericDescriptor:
- return self._intensity_meas_su
-
- @property
- def intensity_calc(self) -> NumericDescriptor:
- return self._intensity_calc
-
- @property
- def intensity_bkg(self) -> NumericDescriptor:
- return self._intensity_bkg
-
- @property
- def calc_status(self) -> StringDescriptor:
- return self._calc_status
-
-
-class PdCwlDataPointMixin:
- """Mixin for powder diffraction data points with constant
- wavelength.
- """
-
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- self._two_theta = NumericDescriptor(
- name='two_theta',
- description='Measured 2θ diffraction angle.',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0, le=180),
- ),
- units='deg',
- cif_handler=CifHandler(
- names=[
- '_pd_proc.2theta_scan',
- '_pd_meas.2theta_scan',
- ]
- ),
- )
-
- @property
- def two_theta(self) -> NumericDescriptor:
- return self._two_theta
-
-
-class PdTofDataPointMixin:
- """Mixin for powder diffraction data points with time-of-flight."""
-
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- self._time_of_flight = NumericDescriptor(
- name='time_of_flight',
- description='Measured time for time-of-flight neutron measurement.',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- units='µs',
- cif_handler=CifHandler(
- names=[
- '_pd_meas.time_of_flight',
- ]
- ),
- )
-
- @property
- def time_of_flight(self) -> NumericDescriptor:
- return self._time_of_flight
-
-
-class PdCwlDataPoint(
- PdDataPointBaseMixin,
- PdCwlDataPointMixin,
- CategoryItem, # Must be last to ensure mixins initialized first
-):
- """Powder diffraction data point for constant-wavelength
- experiments.
- """
-
- def __init__(self) -> None:
- super().__init__()
- self._identity.category_code = 'pd_data'
- self._identity.category_entry_name = lambda: str(self.point_id.value)
-
-
-class PdTofDataPoint(
- PdDataPointBaseMixin,
- PdTofDataPointMixin,
- CategoryItem, # Must be last to ensure mixins initialized first
-):
- """Powder diffraction data point for time-of-flight experiments."""
-
- def __init__(self) -> None:
- super().__init__()
- self._identity.category_code = 'pd_data'
- self._identity.category_entry_name = lambda: str(self.point_id.value)
-
-
-class PdDataBase(CategoryCollection):
- # TODO: ???
-
- # Redefine update priority to ensure data updated after other
- # categories. Higher number = runs later. Default for other
- # categories, e.g., background and excluded regions are 10 by
- # default
- _update_priority = 100
-
- # Should be set only once
-
- def _set_point_id(self, values) -> None:
- """Helper method to set point IDs."""
- for p, v in zip(self._items, values, strict=True):
- p.point_id._value = v
-
- def _set_meas(self, values) -> None:
- """Helper method to set measured intensity."""
- for p, v in zip(self._items, values, strict=True):
- p.intensity_meas._value = v
-
- def _set_meas_su(self, values) -> 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
-
- # Can be set multiple times
-
- def _set_d_spacing(self, values) -> 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_calc(self, values) -> 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_bkg(self, values) -> 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:
- """Helper method to set refinement status."""
- for p, v in zip(self._items, values, strict=True):
- if v:
- p.calc_status._value = 'incl'
- elif not v:
- p.calc_status._value = 'excl'
- else:
- raise ValueError(
- f'Invalid refinement status value: {v}. Expected boolean True/False.'
- )
-
- @property
- def _calc_mask(self) -> np.ndarray:
- return self.calc_status == 'incl'
-
- @property
- def _calc_items(self):
- """Get only the items included in calculations."""
- return [item for item, mask in zip(self._items, self._calc_mask, strict=False) if mask]
-
- @property
- def calc_status(self) -> np.ndarray:
- return np.fromiter((p.calc_status.value for p in self._items), dtype=object)
-
- @property
- def d(self) -> np.ndarray:
- return np.fromiter((p.d_spacing.value for p in self._calc_items), dtype=float)
-
- @property
- def meas(self) -> np.ndarray:
- return np.fromiter((p.intensity_meas.value for p in self._calc_items), dtype=float)
-
- @property
- def meas_su(self) -> np.ndarray:
- # 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
- # cause fitting algorithms to fail.
- # The current implementation is inefficient.
- # In the future, we should extend the functionality of
- # the NumericDescriptor to automatically replace the value
- # outside of the valid range (`content_validator`) with a
- # default value (`default`), when the value is set.
- # BraggPdExperiment._load_ascii_data_to_experiment() handles
- # this for ASCII data, but we also need to handle CIF data and
- # come up with a consistent approach for both data sources.
- original = np.fromiter((p.intensity_meas_su.value for p in self._calc_items), dtype=float)
- # Replace values smaller than 0.0001 with 1.0
- modified = np.where(original < 0.0001, 1.0, original)
- return modified
-
- @property
- def calc(self) -> np.ndarray:
- return np.fromiter((p.intensity_calc.value for p in self._calc_items), dtype=float)
-
- @property
- def bkg(self) -> np.ndarray:
- return np.fromiter((p.intensity_bkg.value for p in self._calc_items), dtype=float)
-
- def _update(self, called_by_minimizer=False):
- experiment = self._parent
- experiments = experiment._parent
- project = experiments._parent
- sample_models = project.sample_models
- # calculator = experiment.calculator # TODO: move from analysis
- calculator = project.analysis.calculator
-
- initial_calc = np.zeros_like(self.x)
- calc = initial_calc
- for linked_phase in experiment._get_valid_linked_phases(sample_models):
- sample_model_id = linked_phase._identity.category_entry_name
- sample_model_scale = linked_phase.scale.value
- sample_model = sample_models[sample_model_id]
-
- sample_model_calc = calculator.calculate_pattern(
- sample_model,
- experiment,
- called_by_minimizer=called_by_minimizer,
- )
-
- sample_model_scaled_calc = sample_model_scale * sample_model_calc
- calc += sample_model_scaled_calc
-
- self._set_calc(calc + self.bkg)
-
-
-class PdCwlData(PdDataBase):
- # TODO: ???
- # _description: str = 'Powder diffraction data points for
- # constant-wavelength experiments.'
-
- def __init__(self):
- super().__init__(item_type=PdCwlDataPoint)
-
- # Should be set only once
-
- def _set_x(self, values) -> None:
- """Helper method to set 2θ values."""
- # TODO: split into multiple methods
- self._items = [self._item_type() for _ in range(values.size)]
- for p, v in zip(self._items, values, strict=True):
- p.two_theta._value = v
- self._set_point_id([str(i + 1) for i in range(values.size)])
-
- @property
- def all_x(self) -> np.ndarray:
- """Get the 2θ values for all data points in this collection."""
- return np.fromiter((p.two_theta.value for p in self._items), dtype=float)
-
- @property
- def x(self) -> np.ndarray:
- """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)
-
- def _update(self, called_by_minimizer=False):
- super()._update(called_by_minimizer)
-
- experiment = self._parent
- d_spacing = twotheta_to_d(
- self.x,
- experiment.instrument.setup_wavelength.value,
- )
- self._set_d_spacing(d_spacing)
-
-
-class PdTofData(PdDataBase):
- # TODO: ???
- # _description: str = 'Powder diffraction data points for
- # time-of-flight experiments.'
-
- def __init__(self):
- super().__init__(item_type=PdTofDataPoint)
-
- def _set_x(self, values) -> None:
- """Helper method to set time-of-flight values."""
- # TODO: split into multiple methods
- self._items = [self._item_type() for _ in range(values.size)]
- for p, v in zip(self._items, values, strict=True):
- p.time_of_flight._value = v
- self._set_point_id([str(i + 1) for i in range(values.size)])
-
- @property
- def all_x(self) -> np.ndarray:
- """Get the TOF values for all data points in this collection."""
- return np.fromiter((p.time_of_flight.value for p in self._items), dtype=float)
-
- @property
- def x(self) -> np.ndarray:
- """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)
-
- def _update(self, called_by_minimizer=False):
- super()._update(called_by_minimizer)
-
- experiment = self._parent
- d_spacing = tof_to_d(
- self.x,
- experiment.instrument.calib_d_to_tof_offset.value,
- experiment.instrument.calib_d_to_tof_linear.value,
- experiment.instrument.calib_d_to_tof_quad.value,
- )
- self._set_d_spacing(d_spacing)
diff --git a/src/easydiffraction/experiments/categories/data/bragg_sc.py b/src/easydiffraction/experiments/categories/data/bragg_sc.py
deleted file mode 100644
index f738b2df..00000000
--- a/src/easydiffraction/experiments/categories/data/bragg_sc.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class Refln:
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
-
- self._index_h = StringDescriptor(
- name='index_h',
- description='...',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_refln.index_h',
- ]
- ),
- )
- self._index_k = StringDescriptor(
- name='index_k',
- description='...',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_refln.index_k',
- ]
- ),
- )
- self._index_l = StringDescriptor(
- name='index_l',
- description='...',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_refln.index_l',
- ]
- ),
- )
- self._intensity_meas = NumericDescriptor(
- name='intensity_meas',
- description='...',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_refln.intensity_meas',
- ]
- ),
- )
- self._intensity_meas_su = NumericDescriptor(
- name='intensity_meas_su',
- description='...',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_refln.intensity_meas_su',
- ]
- ),
- )
-
- class ReflnData(CategoryCollection):
- """..."""
-
- _update_priority = 100
-
- def __init__(self):
- super().__init__(item_type=Refln)
diff --git a/src/easydiffraction/experiments/categories/data/factory.py b/src/easydiffraction/experiments/categories/data/factory.py
deleted file mode 100644
index 2d60560c..00000000
--- a/src/easydiffraction/experiments/categories/data/factory.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-from typing import Optional
-
-from easydiffraction.experiments.categories.data.bragg_pd import PdCwlData
-from easydiffraction.experiments.categories.data.bragg_pd import PdTofData
-from easydiffraction.experiments.categories.data.total import TotalData
-from easydiffraction.experiments.experiment.enums import BeamModeEnum
-from easydiffraction.experiments.experiment.enums import SampleFormEnum
-from easydiffraction.experiments.experiment.enums import ScatteringTypeEnum
-
-if TYPE_CHECKING:
- from easydiffraction.core.category import CategoryCollection
-
-
-class DataFactory:
- """Factory for creating powder diffraction data collections."""
-
- _supported = {
- SampleFormEnum.POWDER: {
- ScatteringTypeEnum.BRAGG: {
- BeamModeEnum.CONSTANT_WAVELENGTH: PdCwlData,
- BeamModeEnum.TIME_OF_FLIGHT: PdTofData,
- },
- ScatteringTypeEnum.TOTAL: {
- BeamModeEnum.CONSTANT_WAVELENGTH: TotalData,
- BeamModeEnum.TIME_OF_FLIGHT: TotalData,
- },
- },
- }
-
- @classmethod
- def create(
- cls,
- *,
- sample_form: Optional[SampleFormEnum] = None,
- beam_mode: Optional[BeamModeEnum] = None,
- scattering_type: Optional[ScatteringTypeEnum] = None,
- ) -> CategoryCollection:
- """Create a data collection for the given configuration."""
- if sample_form is None:
- sample_form = SampleFormEnum.default()
- if beam_mode is None:
- beam_mode = BeamModeEnum.default()
- if scattering_type is None:
- scattering_type = ScatteringTypeEnum.default()
-
- supported_sample_forms = list(cls._supported.keys())
- if sample_form not in supported_sample_forms:
- raise ValueError(
- f"Unsupported sample form: '{sample_form}'.\n"
- f'Supported sample forms: {supported_sample_forms}'
- )
-
- supported_scattering_types = list(cls._supported[sample_form].keys())
- if scattering_type not in supported_scattering_types:
- raise ValueError(
- f"Unsupported scattering type: '{scattering_type}' for sample form: "
- f"'{sample_form}'.\n Supported scattering types: '{supported_scattering_types}'"
- )
- supported_beam_modes = list(cls._supported[sample_form][scattering_type].keys())
- if beam_mode not in supported_beam_modes:
- raise ValueError(
- f"Unsupported beam mode: '{beam_mode}' for sample form: "
- f"'{sample_form}' and scattering type '{scattering_type}'.\n"
- f"Supported beam modes: '{supported_beam_modes}'"
- )
-
- data_class = cls._supported[sample_form][scattering_type][beam_mode]
- data_obj = data_class()
-
- return data_obj
diff --git a/src/easydiffraction/experiments/categories/data/total.py b/src/easydiffraction/experiments/categories/data/total.py
deleted file mode 100644
index 5dc69b9d..00000000
--- a/src/easydiffraction/experiments/categories/data/total.py
+++ /dev/null
@@ -1,267 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Data categories for total scattering (PDF) experiments."""
-
-from __future__ import annotations
-
-import numpy as np
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import NumericDescriptor
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import MembershipValidator
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.core.validation import RegexValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class TotalDataPoint(CategoryItem):
- """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.
- """
-
- def __init__(self) -> None:
- super().__init__()
-
- self._point_id = StringDescriptor(
- name='point_id',
- description='Identifier for this data point in the dataset.',
- value_spec=AttributeSpec(
- type_=DataTypes.STRING,
- default='0',
- content_validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_data.point_id', # TODO: Use total scattering CIF names
- ]
- ),
- )
- self._r = NumericDescriptor(
- name='r',
- description='Interatomic distance in real space.',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- units='Å',
- cif_handler=CifHandler(
- names=[
- '_pd_proc.r', # TODO: Use PDF-specific CIF names
- ]
- ),
- )
- self._g_r_meas = NumericDescriptor(
- name='g_r_meas',
- description='Measured pair distribution function G(r).',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_meas.intensity_total', # TODO: Use PDF-specific CIF names
- ]
- ),
- )
- self._g_r_meas_su = NumericDescriptor(
- name='g_r_meas_su',
- description='Standard uncertainty of measured G(r).',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_meas.intensity_total_su', # TODO: Use PDF-specific CIF names
- ]
- ),
- )
- self._g_r_calc = NumericDescriptor(
- name='g_r_calc',
- description='Calculated pair distribution function G(r).',
- value_spec=AttributeSpec(
- type_=DataTypes.NUMERIC,
- default=0.0,
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_calc.intensity_total', # TODO: Use PDF-specific CIF names
- ]
- ),
- )
- self._calc_status = StringDescriptor(
- name='calc_status',
- description='Status code of the data point in calculation.',
- value_spec=AttributeSpec(
- type_=DataTypes.STRING,
- default='incl',
- content_validator=MembershipValidator(allowed=['incl', 'excl']),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_data.refinement_status', # TODO: Use PDF-specific CIF names
- ]
- ),
- )
-
- self._identity.category_code = 'total_data'
- self._identity.category_entry_name = lambda: str(self.point_id.value)
-
- @property
- def point_id(self) -> StringDescriptor:
- return self._point_id
-
- @property
- def r(self) -> NumericDescriptor:
- return self._r
-
- @property
- def g_r_meas(self) -> NumericDescriptor:
- return self._g_r_meas
-
- @property
- def g_r_meas_su(self) -> NumericDescriptor:
- return self._g_r_meas_su
-
- @property
- def g_r_calc(self) -> NumericDescriptor:
- return self._g_r_calc
-
- @property
- def calc_status(self) -> StringDescriptor:
- return self._calc_status
-
-
-class TotalDataBase(CategoryCollection):
- """Base class for total scattering data collections."""
-
- _update_priority = 100
-
- # Should be set only once
-
- def _set_point_id(self, values) -> None:
- """Helper method to set point IDs."""
- for p, v in zip(self._items, values, strict=True):
- p.point_id._value = v
-
- def _set_meas(self, values) -> 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_meas_su(self, values) -> None:
- """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
-
- # Can be set multiple times
-
- def _set_calc(self, values) -> 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:
- """Helper method to set calculation status."""
- for p, v in zip(self._items, values, strict=True):
- if v:
- p.calc_status._value = 'incl'
- elif not v:
- p.calc_status._value = 'excl'
- else:
- raise ValueError(
- f'Invalid calculation status value: {v}. Expected boolean True/False.'
- )
-
- @property
- def _calc_mask(self) -> np.ndarray:
- return self.calc_status == 'incl'
-
- @property
- def _calc_items(self):
- """Get only the items included in calculations."""
- return [item for item, mask in zip(self._items, self._calc_mask, strict=False) if mask]
-
- @property
- def calc_status(self) -> np.ndarray:
- return np.fromiter((p.calc_status.value for p in self._items), dtype=object)
-
- @property
- def meas(self) -> np.ndarray:
- return np.fromiter((p.g_r_meas.value for p in self._calc_items), dtype=float)
-
- @property
- def meas_su(self) -> np.ndarray:
- return np.fromiter((p.g_r_meas_su.value for p in self._calc_items), dtype=float)
-
- @property
- def calc(self) -> np.ndarray:
- return np.fromiter((p.g_r_calc.value for p in self._calc_items), dtype=float)
-
- @property
- def bkg(self) -> np.ndarray:
- """Background is always zero for PDF data."""
- return np.zeros_like(self.calc)
-
- def _update(self, called_by_minimizer=False):
- experiment = self._parent
- experiments = experiment._parent
- project = experiments._parent
- sample_models = project.sample_models
- calculator = project.analysis.calculator
-
- initial_calc = np.zeros_like(self.x)
- calc = initial_calc
- for linked_phase in experiment._get_valid_linked_phases(sample_models):
- sample_model_id = linked_phase._identity.category_entry_name
- sample_model_scale = linked_phase.scale.value
- sample_model = sample_models[sample_model_id]
-
- sample_model_calc = calculator.calculate_pattern(
- sample_model,
- experiment,
- called_by_minimizer=called_by_minimizer,
- )
-
- sample_model_scaled_calc = sample_model_scale * sample_model_calc
- calc += sample_model_scaled_calc
-
- self._set_calc(calc)
-
-
-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.
- """
-
- def __init__(self):
- super().__init__(item_type=TotalDataPoint)
-
- def _set_x(self, values) -> None:
- """Helper method to set r values."""
- self._items = [self._item_type() for _ in range(values.size)]
- for p, v in zip(self._items, values, strict=True):
- p.r._value = v
- self._set_point_id([str(i + 1) for i in range(values.size)])
-
- @property
- def all_x(self) -> np.ndarray:
- """Get the r values for all data points."""
- return np.fromiter((p.r.value for p in self._items), dtype=float)
-
- @property
- def x(self) -> np.ndarray:
- """Get the r values for data points included in calculations."""
- return np.fromiter((p.r.value for p in self._calc_items), dtype=float)
diff --git a/src/easydiffraction/experiments/categories/experiment_type.py b/src/easydiffraction/experiments/categories/experiment_type.py
deleted file mode 100644
index babe82ee..00000000
--- a/src/easydiffraction/experiments/categories/experiment_type.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# 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
-``CifHandler``.
-"""
-
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import MembershipValidator
-from easydiffraction.experiments.experiment.enums import BeamModeEnum
-from easydiffraction.experiments.experiment.enums import RadiationProbeEnum
-from easydiffraction.experiments.experiment.enums import SampleFormEnum
-from easydiffraction.experiments.experiment.enums import ScatteringTypeEnum
-from easydiffraction.io.cif.handler import CifHandler
-
-
-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.
- """
-
- def __init__(
- self,
- *,
- sample_form=None,
- beam_mode=None,
- radiation_probe=None,
- scattering_type=None,
- ):
- super().__init__()
-
- self._sample_form: StringDescriptor = StringDescriptor(
- name='sample_form',
- description='Specifies whether the diffraction data corresponds to '
- 'powder diffraction or single crystal diffraction',
- value_spec=AttributeSpec(
- value=sample_form,
- type_=DataTypes.STRING,
- default=SampleFormEnum.default().value,
- content_validator=MembershipValidator(
- allowed=[member.value for member in SampleFormEnum]
- ),
- ),
- cif_handler=CifHandler(
- names=[
- '_expt_type.sample_form',
- ]
- ),
- )
-
- self._beam_mode: StringDescriptor = StringDescriptor(
- name='beam_mode',
- description='Defines whether the measurement is performed with a '
- 'constant wavelength (CW) or time-of-flight (TOF) method',
- value_spec=AttributeSpec(
- value=beam_mode,
- type_=DataTypes.STRING,
- default=BeamModeEnum.default().value,
- content_validator=MembershipValidator(
- allowed=[member.value for member in BeamModeEnum]
- ),
- ),
- cif_handler=CifHandler(
- names=[
- '_expt_type.beam_mode',
- ]
- ),
- )
- self._radiation_probe: StringDescriptor = StringDescriptor(
- name='radiation_probe',
- description='Specifies whether the measurement uses neutrons or X-rays',
- value_spec=AttributeSpec(
- value=radiation_probe,
- type_=DataTypes.STRING,
- default=RadiationProbeEnum.default().value,
- content_validator=MembershipValidator(
- allowed=[member.value for member in RadiationProbeEnum]
- ),
- ),
- cif_handler=CifHandler(
- names=[
- '_expt_type.radiation_probe',
- ]
- ),
- )
- self._scattering_type: StringDescriptor = 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)',
- value_spec=AttributeSpec(
- value=scattering_type,
- type_=DataTypes.STRING,
- default=ScatteringTypeEnum.default().value,
- content_validator=MembershipValidator(
- allowed=[member.value for member in ScatteringTypeEnum]
- ),
- ),
- cif_handler=CifHandler(
- names=[
- '_expt_type.scattering_type',
- ]
- ),
- )
-
- self._identity.category_code = 'expt_type'
-
- @property
- def sample_form(self):
- """Sample form descriptor (powder/single crystal)."""
- return self._sample_form
-
- @sample_form.setter
- def sample_form(self, value):
- """Set sample form value."""
- self._sample_form.value = value
-
- @property
- def beam_mode(self):
- """Beam mode descriptor (CW/TOF)."""
- return self._beam_mode
-
- @beam_mode.setter
- def beam_mode(self, value):
- """Set beam mode value."""
- self._beam_mode.value = value
-
- @property
- def radiation_probe(self):
- """Radiation probe descriptor (neutrons/X-rays)."""
- return self._radiation_probe
-
- @radiation_probe.setter
- def radiation_probe(self, value):
- """Set radiation probe value."""
- self._radiation_probe.value = value
-
- @property
- def scattering_type(self):
- """Scattering type descriptor (Bragg/Total)."""
- return self._scattering_type
-
- @scattering_type.setter
- def scattering_type(self, value):
- """Set scattering type value."""
- self._scattering_type.value = value
diff --git a/src/easydiffraction/experiments/categories/instrument/__init__.py b/src/easydiffraction/experiments/categories/instrument/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/experiments/categories/instrument/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/experiments/categories/instrument/cwl.py b/src/easydiffraction/experiments/categories/instrument/cwl.py
deleted file mode 100644
index 0653f93c..00000000
--- a/src/easydiffraction/experiments/categories/instrument/cwl.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.experiments.categories.instrument.base import InstrumentBase
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class CwlInstrument(InstrumentBase):
- def __init__(
- self,
- *,
- setup_wavelength=None,
- calib_twotheta_offset=None,
- ) -> None:
- super().__init__()
-
- self._setup_wavelength: Parameter = Parameter(
- name='wavelength',
- description='Incident neutron or X-ray wavelength',
- value_spec=AttributeSpec(
- value=setup_wavelength,
- type_=DataTypes.NUMERIC,
- default=1.5406,
- content_validator=RangeValidator(),
- ),
- units='Å',
- cif_handler=CifHandler(
- names=[
- '_instr.wavelength',
- ]
- ),
- )
- self._calib_twotheta_offset: Parameter = Parameter(
- name='twotheta_offset',
- description='Instrument misalignment offset',
- value_spec=AttributeSpec(
- value=calib_twotheta_offset,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='deg',
- cif_handler=CifHandler(
- names=[
- '_instr.2theta_offset',
- ]
- ),
- )
-
- @property
- def setup_wavelength(self):
- """Incident wavelength parameter (Å)."""
- return self._setup_wavelength
-
- @setup_wavelength.setter
- def setup_wavelength(self, value):
- """Set incident wavelength value (Å)."""
- self._setup_wavelength.value = value
-
- @property
- def calib_twotheta_offset(self):
- """Instrument misalignment two-theta offset (deg)."""
- return self._calib_twotheta_offset
-
- @calib_twotheta_offset.setter
- def calib_twotheta_offset(self, value):
- """Set two-theta offset value (deg)."""
- self._calib_twotheta_offset.value = value
diff --git a/src/easydiffraction/experiments/categories/instrument/factory.py b/src/easydiffraction/experiments/categories/instrument/factory.py
deleted file mode 100644
index 443957a1..00000000
--- a/src/easydiffraction/experiments/categories/instrument/factory.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Factory for instrument category items.
-
-Provides a stable entry point for creating instrument objects from the
-experiment's scattering type and beam mode.
-"""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-from typing import Optional
-from typing import Type
-
-from easydiffraction.experiments.experiment.enums import BeamModeEnum
-from easydiffraction.experiments.experiment.enums import ScatteringTypeEnum
-
-if TYPE_CHECKING:
- from easydiffraction.experiments.categories.instrument.base import InstrumentBase
-
-
-class InstrumentFactory:
- """Create instrument instances for supported modes.
-
- The factory hides implementation details and lazy-loads concrete
- instrument classes to avoid circular imports.
- """
-
- ST = ScatteringTypeEnum
- BM = BeamModeEnum
-
- @classmethod
- def _supported_map(cls) -> dict:
- # Lazy import to avoid circulars
- from easydiffraction.experiments.categories.instrument.cwl import CwlInstrument
- from easydiffraction.experiments.categories.instrument.tof import TofInstrument
-
- return {
- cls.ST.BRAGG: {
- cls.BM.CONSTANT_WAVELENGTH: CwlInstrument,
- cls.BM.TIME_OF_FLIGHT: TofInstrument,
- }
- }
-
- @classmethod
- def create(
- cls,
- scattering_type: Optional[ScatteringTypeEnum] = None,
- beam_mode: Optional[BeamModeEnum] = None,
- ) -> InstrumentBase:
- if beam_mode is None:
- beam_mode = BeamModeEnum.default()
- if scattering_type is None:
- scattering_type = ScatteringTypeEnum.default()
-
- supported = cls._supported_map()
-
- supported_scattering_types = list(supported.keys())
- if scattering_type not in supported_scattering_types:
- raise ValueError(
- f"Unsupported scattering type: '{scattering_type}'.\n "
- f'Supported scattering types: {supported_scattering_types}'
- )
-
- supported_beam_modes = list(supported[scattering_type].keys())
- if beam_mode not in supported_beam_modes:
- raise ValueError(
- f"Unsupported beam mode: '{beam_mode}' for scattering type: "
- f"'{scattering_type}'.\n "
- f'Supported beam modes: {supported_beam_modes}'
- )
-
- instrument_class: Type[InstrumentBase] = supported[scattering_type][beam_mode]
- return instrument_class()
diff --git a/src/easydiffraction/experiments/categories/instrument/tof.py b/src/easydiffraction/experiments/categories/instrument/tof.py
deleted file mode 100644
index ede52d3c..00000000
--- a/src/easydiffraction/experiments/categories/instrument/tof.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.experiments.categories.instrument.base import InstrumentBase
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class TofInstrument(InstrumentBase):
- def __init__(
- self,
- *,
- setup_twotheta_bank=None,
- calib_d_to_tof_offset=None,
- calib_d_to_tof_linear=None,
- calib_d_to_tof_quad=None,
- calib_d_to_tof_recip=None,
- ) -> None:
- super().__init__()
-
- self._setup_twotheta_bank: Parameter = Parameter(
- name='twotheta_bank',
- description='Detector bank position',
- value_spec=AttributeSpec(
- value=setup_twotheta_bank,
- type_=DataTypes.NUMERIC,
- default=150.0,
- content_validator=RangeValidator(),
- ),
- units='deg',
- cif_handler=CifHandler(
- names=[
- '_instr.2theta_bank',
- ]
- ),
- )
- self._calib_d_to_tof_offset: Parameter = Parameter(
- name='d_to_tof_offset',
- description='TOF offset',
- value_spec=AttributeSpec(
- value=calib_d_to_tof_offset,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs',
- cif_handler=CifHandler(
- names=[
- '_instr.d_to_tof_offset',
- ]
- ),
- )
- self._calib_d_to_tof_linear: Parameter = Parameter(
- name='d_to_tof_linear',
- description='TOF linear conversion',
- value_spec=AttributeSpec(
- value=calib_d_to_tof_linear,
- type_=DataTypes.NUMERIC,
- default=10000.0,
- content_validator=RangeValidator(),
- ),
- units='µs/Å',
- cif_handler=CifHandler(
- names=[
- '_instr.d_to_tof_linear',
- ]
- ),
- )
- self._calib_d_to_tof_quad: Parameter = Parameter(
- name='d_to_tof_quad',
- description='TOF quadratic correction',
- value_spec=AttributeSpec(
- value=calib_d_to_tof_quad,
- type_=DataTypes.NUMERIC,
- default=-0.00001,
- content_validator=RangeValidator(),
- ),
- units='µs/Ų',
- cif_handler=CifHandler(
- names=[
- '_instr.d_to_tof_quad',
- ]
- ),
- )
- self._calib_d_to_tof_recip: Parameter = Parameter(
- name='d_to_tof_recip',
- description='TOF reciprocal velocity correction',
- value_spec=AttributeSpec(
- value=calib_d_to_tof_recip,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs·Å',
- cif_handler=CifHandler(
- names=[
- '_instr.d_to_tof_recip',
- ]
- ),
- )
-
- @property
- def setup_twotheta_bank(self):
- """Detector bank two-theta position (deg)."""
- return self._setup_twotheta_bank
-
- @setup_twotheta_bank.setter
- def setup_twotheta_bank(self, value):
- """Set detector bank two-theta position (deg)."""
- self._setup_twotheta_bank.value = value
-
- @property
- def calib_d_to_tof_offset(self):
- """TOF offset calibration parameter (µs)."""
- return self._calib_d_to_tof_offset
-
- @calib_d_to_tof_offset.setter
- def calib_d_to_tof_offset(self, value):
- """Set TOF offset (µs)."""
- self._calib_d_to_tof_offset.value = value
-
- @property
- def calib_d_to_tof_linear(self):
- """Linear d to TOF conversion coefficient (µs/Å)."""
- return self._calib_d_to_tof_linear
-
- @calib_d_to_tof_linear.setter
- def calib_d_to_tof_linear(self, value):
- """Set linear d to TOF coefficient (µs/Å)."""
- self._calib_d_to_tof_linear.value = value
-
- @property
- def calib_d_to_tof_quad(self):
- """Quadratic d to TOF correction coefficient (µs/Ų)."""
- return self._calib_d_to_tof_quad
-
- @calib_d_to_tof_quad.setter
- def calib_d_to_tof_quad(self, value):
- """Set quadratic d to TOF correction (µs/Ų)."""
- self._calib_d_to_tof_quad.value = value
-
- @property
- def calib_d_to_tof_recip(self):
- """Reciprocal-velocity d to TOF correction (µs·Å)."""
- return self._calib_d_to_tof_recip
-
- @calib_d_to_tof_recip.setter
- def calib_d_to_tof_recip(self, value):
- """Set reciprocal-velocity d to TOF correction (µs·Å)."""
- self._calib_d_to_tof_recip.value = value
diff --git a/src/easydiffraction/experiments/categories/linked_phases.py b/src/easydiffraction/experiments/categories/linked_phases.py
deleted file mode 100644
index 3ff827d5..00000000
--- a/src/easydiffraction/experiments/categories/linked_phases.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Linked phases allow combining phases with scale factors."""
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.core.validation import RegexValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class LinkedPhase(CategoryItem):
- """Link to a phase by id with a scale factor."""
-
- def __init__(
- self,
- *,
- id=None, # TODO: need new name instead of id
- scale=None,
- ):
- super().__init__()
-
- self._id = StringDescriptor(
- name='id',
- description='Identifier of the linked phase.',
- value_spec=AttributeSpec(
- value=id,
- type_=DataTypes.STRING,
- default='Si',
- content_validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_phase_block.id',
- ]
- ),
- )
- self._scale = Parameter(
- name='scale',
- description='Scale factor of the linked phase.',
- value_spec=AttributeSpec(
- value=scale,
- type_=DataTypes.NUMERIC,
- default=1.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_pd_phase_block.scale',
- ]
- ),
- )
- self._identity.category_code = 'linked_phases'
- self._identity.category_entry_name = lambda: str(self.id.value)
-
- @property
- def id(self) -> StringDescriptor:
- """Identifier of the linked phase."""
- return self._id
-
- @id.setter
- def id(self, value: str):
- """Set the linked phase identifier."""
- self._id.value = value
-
- @property
- def scale(self) -> Parameter:
- """Scale factor parameter."""
- return self._scale
-
- @scale.setter
- def scale(self, value: float):
- """Set scale factor value."""
- self._scale.value = value
-
-
-class LinkedPhases(CategoryCollection):
- """Collection of LinkedPhase instances."""
-
- def __init__(self):
- """Create an empty collection of linked phases."""
- super().__init__(item_type=LinkedPhase)
diff --git a/src/easydiffraction/experiments/categories/peak/__init__.py b/src/easydiffraction/experiments/categories/peak/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/experiments/categories/peak/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/experiments/categories/peak/cwl.py b/src/easydiffraction/experiments/categories/peak/cwl.py
deleted file mode 100644
index 76777a44..00000000
--- a/src/easydiffraction/experiments/categories/peak/cwl.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Constant-wavelength peak profile classes."""
-
-from easydiffraction.experiments.categories.peak.base import PeakBase
-from easydiffraction.experiments.categories.peak.cwl_mixins import CwlBroadeningMixin
-from easydiffraction.experiments.categories.peak.cwl_mixins import EmpiricalAsymmetryMixin
-from easydiffraction.experiments.categories.peak.cwl_mixins import FcjAsymmetryMixin
-
-
-class CwlPseudoVoigt(
- PeakBase,
- CwlBroadeningMixin,
-):
- """Constant-wavelength pseudo-Voigt peak shape."""
-
- def __init__(self) -> None:
- super().__init__()
- self._add_constant_wavelength_broadening()
-
-
-class CwlSplitPseudoVoigt(
- PeakBase,
- CwlBroadeningMixin,
- EmpiricalAsymmetryMixin,
-):
- """Split pseudo-Voigt (empirical asymmetry) for CWL mode."""
-
- def __init__(self) -> None:
- super().__init__()
- self._add_constant_wavelength_broadening()
- self._add_empirical_asymmetry()
-
-
-class CwlThompsonCoxHastings(
- PeakBase,
- CwlBroadeningMixin,
- FcjAsymmetryMixin,
-):
- """Thompson–Cox–Hastings with FCJ asymmetry for CWL mode."""
-
- def __init__(self) -> None:
- super().__init__()
- self._add_constant_wavelength_broadening()
- self._add_fcj_asymmetry()
diff --git a/src/easydiffraction/experiments/categories/peak/cwl_mixins.py b/src/easydiffraction/experiments/categories/peak/cwl_mixins.py
deleted file mode 100644
index 47d48636..00000000
--- a/src/easydiffraction/experiments/categories/peak/cwl_mixins.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Constant-wavelength (CWL) peak-profile mixins.
-
-This module provides mixins that add broadening and asymmetry parameters
-for constant-wavelength powder diffraction peak profiles. They are
-composed into concrete peak classes elsewhere.
-"""
-
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class CwlBroadeningMixin:
- """Mixin that adds CWL Gaussian and Lorentz broadening
- parameters.
- """
-
- # TODO: Rename to cwl. Check other mixins for naming consistency.
- def _add_constant_wavelength_broadening(self) -> None:
- """Create CWL broadening parameters and attach them to the
- class.
-
- Defines Gaussian (U, V, W) and Lorentz (X, Y) terms
- often used in the TCH formulation. Values are stored as
- ``Parameter`` objects.
- """
- self._broad_gauss_u: Parameter = Parameter(
- name='broad_gauss_u',
- description='Gaussian broadening coefficient (dependent on '
- 'sample size and instrument resolution)',
- value_spec=AttributeSpec(
- value=0.01,
- type_=DataTypes.NUMERIC,
- default=0.01,
- content_validator=RangeValidator(),
- ),
- units='deg²',
- cif_handler=CifHandler(
- names=[
- '_peak.broad_gauss_u',
- ]
- ),
- )
- self._broad_gauss_v: Parameter = Parameter(
- name='broad_gauss_v',
- description='Gaussian broadening coefficient (instrumental broadening contribution)',
- value_spec=AttributeSpec(
- value=-0.01,
- type_=DataTypes.NUMERIC,
- default=-0.01,
- content_validator=RangeValidator(),
- ),
- units='deg²',
- cif_handler=CifHandler(
- names=[
- '_peak.broad_gauss_v',
- ]
- ),
- )
- self._broad_gauss_w: Parameter = Parameter(
- name='broad_gauss_w',
- description='Gaussian broadening coefficient (instrumental broadening contribution)',
- value_spec=AttributeSpec(
- value=0.02,
- type_=DataTypes.NUMERIC,
- default=0.02,
- content_validator=RangeValidator(),
- ),
- units='deg²',
- cif_handler=CifHandler(
- names=[
- '_peak.broad_gauss_w',
- ]
- ),
- )
- self._broad_lorentz_x: Parameter = Parameter(
- name='broad_lorentz_x',
- description='Lorentzian broadening coefficient (dependent on sample strain effects)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='deg',
- cif_handler=CifHandler(
- names=[
- '_peak.broad_lorentz_x',
- ]
- ),
- )
- self._broad_lorentz_y: Parameter = Parameter(
- name='broad_lorentz_y',
- description='Lorentzian broadening coefficient (dependent on '
- 'microstructural defects and strain)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='deg',
- cif_handler=CifHandler(
- names=[
- '_peak.broad_lorentz_y',
- ]
- ),
- )
-
- @property
- def broad_gauss_u(self) -> Parameter:
- """Get Gaussian U broadening parameter."""
- return self._broad_gauss_u
-
- @broad_gauss_u.setter
- def broad_gauss_u(self, value: float) -> None:
- """Set Gaussian U broadening parameter."""
- self._broad_gauss_u.value = value
-
- @property
- def broad_gauss_v(self) -> Parameter:
- """Get Gaussian V broadening parameter."""
- return self._broad_gauss_v
-
- @broad_gauss_v.setter
- def broad_gauss_v(self, value: float) -> None:
- """Set Gaussian V broadening parameter."""
- self._broad_gauss_v.value = value
-
- @property
- def broad_gauss_w(self) -> Parameter:
- """Get Gaussian W broadening parameter."""
- return self._broad_gauss_w
-
- @broad_gauss_w.setter
- def broad_gauss_w(self, value: float) -> None:
- """Set Gaussian W broadening parameter."""
- self._broad_gauss_w.value = value
-
- @property
- def broad_lorentz_x(self) -> Parameter:
- """Get Lorentz X broadening parameter."""
- return self._broad_lorentz_x
-
- @broad_lorentz_x.setter
- def broad_lorentz_x(self, value: float) -> None:
- """Set Lorentz X broadening parameter."""
- self._broad_lorentz_x.value = value
-
- @property
- def broad_lorentz_y(self) -> Parameter:
- """Get Lorentz Y broadening parameter."""
- return self._broad_lorentz_y
-
- @broad_lorentz_y.setter
- def broad_lorentz_y(self, value: float) -> None:
- """Set Lorentz Y broadening parameter."""
- self._broad_lorentz_y.value = value
-
-
-class EmpiricalAsymmetryMixin:
- """Mixin that adds empirical CWL peak asymmetry parameters."""
-
- def _add_empirical_asymmetry(self) -> None:
- """Create empirical asymmetry parameters p1..p4."""
- self._asym_empir_1: Parameter = Parameter(
- name='asym_empir_1',
- description='Empirical asymmetry coefficient p1',
- value_spec=AttributeSpec(
- value=0.1,
- type_=DataTypes.NUMERIC,
- default=0.1,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_empir_1',
- ]
- ),
- )
- self._asym_empir_2: Parameter = Parameter(
- name='asym_empir_2',
- description='Empirical asymmetry coefficient p2',
- value_spec=AttributeSpec(
- value=0.2,
- type_=DataTypes.NUMERIC,
- default=0.2,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_empir_2',
- ]
- ),
- )
- self._asym_empir_3: Parameter = Parameter(
- name='asym_empir_3',
- description='Empirical asymmetry coefficient p3',
- value_spec=AttributeSpec(
- value=0.3,
- type_=DataTypes.NUMERIC,
- default=0.3,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_empir_3',
- ]
- ),
- )
- self._asym_empir_4: Parameter = Parameter(
- name='asym_empir_4',
- description='Empirical asymmetry coefficient p4',
- value_spec=AttributeSpec(
- value=0.4,
- type_=DataTypes.NUMERIC,
- default=0.4,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_empir_4',
- ]
- ),
- )
-
- @property
- def asym_empir_1(self) -> Parameter:
- """Get empirical asymmetry coefficient p1."""
- return self._asym_empir_1
-
- @asym_empir_1.setter
- def asym_empir_1(self, value: float) -> None:
- """Set empirical asymmetry coefficient p1."""
- self._asym_empir_1.value = value
-
- @property
- def asym_empir_2(self) -> Parameter:
- """Get empirical asymmetry coefficient p2."""
- return self._asym_empir_2
-
- @asym_empir_2.setter
- def asym_empir_2(self, value: float) -> None:
- """Set empirical asymmetry coefficient p2."""
- self._asym_empir_2.value = value
-
- @property
- def asym_empir_3(self) -> Parameter:
- """Get empirical asymmetry coefficient p3."""
- return self._asym_empir_3
-
- @asym_empir_3.setter
- def asym_empir_3(self, value: float) -> None:
- """Set empirical asymmetry coefficient p3."""
- self._asym_empir_3.value = value
-
- @property
- def asym_empir_4(self) -> Parameter:
- """Get empirical asymmetry coefficient p4."""
- return self._asym_empir_4
-
- @asym_empir_4.setter
- def asym_empir_4(self, value: float) -> None:
- """Set empirical asymmetry coefficient p4."""
- self._asym_empir_4.value = value
-
-
-class FcjAsymmetryMixin:
- """Mixin that adds Finger–Cox–Jephcoat (FCJ) asymmetry params."""
-
- def _add_fcj_asymmetry(self) -> None:
- """Create FCJ asymmetry parameters."""
- self._asym_fcj_1: Parameter = Parameter(
- name='asym_fcj_1',
- description='Finger-Cox-Jephcoat asymmetry parameter 1',
- value_spec=AttributeSpec(
- value=0.01,
- type_=DataTypes.NUMERIC,
- default=0.01,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_fcj_1',
- ]
- ),
- )
- self._asym_fcj_2: Parameter = Parameter(
- name='asym_fcj_2',
- description='Finger-Cox-Jephcoat asymmetry parameter 2',
- value_spec=AttributeSpec(
- value=0.02,
- type_=DataTypes.NUMERIC,
- default=0.02,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_fcj_2',
- ]
- ),
- )
-
- @property
- def asym_fcj_1(self) -> Parameter:
- """Get FCJ asymmetry parameter 1."""
- return self._asym_fcj_1
-
- @asym_fcj_1.setter
- def asym_fcj_1(self, value: float) -> None:
- """Set FCJ asymmetry parameter 1."""
- self._asym_fcj_1.value = value
-
- @property
- def asym_fcj_2(self) -> Parameter:
- """Get FCJ asymmetry parameter 2."""
- return self._asym_fcj_2
-
- @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/experiments/categories/peak/factory.py b/src/easydiffraction/experiments/categories/peak/factory.py
deleted file mode 100644
index a0aabf10..00000000
--- a/src/easydiffraction/experiments/categories/peak/factory.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from typing import Optional
-
-from easydiffraction.experiments.experiment.enums import BeamModeEnum
-from easydiffraction.experiments.experiment.enums import PeakProfileTypeEnum
-from easydiffraction.experiments.experiment.enums import ScatteringTypeEnum
-
-
-# TODO: Consider inheriting from FactoryBase
-class PeakFactory:
- """Factory for creating peak profile objects.
-
- Lazily imports implementations to avoid circular dependencies and
- selects the appropriate class based on scattering type, beam mode
- and requested profile type.
- """
-
- ST = ScatteringTypeEnum
- BM = BeamModeEnum
- PPT = PeakProfileTypeEnum
- _supported = None # type: ignore[var-annotated]
-
- @classmethod
- def _supported_map(cls):
- """Return nested mapping of supported profile classes.
-
- Structure:
- ``{ScatteringType: {BeamMode: {ProfileType: Class}}}``.
- """
- # Lazy import to avoid circular imports between
- # base and cw/tof/pdf modules
- if cls._supported is None:
- from easydiffraction.experiments.categories.peak.cwl import CwlPseudoVoigt as CwPv
- from easydiffraction.experiments.categories.peak.cwl import (
- CwlSplitPseudoVoigt as CwSpv,
- )
- from easydiffraction.experiments.categories.peak.cwl import (
- CwlThompsonCoxHastings as CwTch,
- )
- from easydiffraction.experiments.categories.peak.tof import TofPseudoVoigt as TofPv
- from easydiffraction.experiments.categories.peak.tof import (
- TofPseudoVoigtBackToBack as TofBtb,
- )
- from easydiffraction.experiments.categories.peak.tof import (
- TofPseudoVoigtIkedaCarpenter as TofIc,
- )
- from easydiffraction.experiments.categories.peak.total import (
- TotalGaussianDampedSinc as PdfGds,
- )
-
- cls._supported = {
- cls.ST.BRAGG: {
- cls.BM.CONSTANT_WAVELENGTH: {
- cls.PPT.PSEUDO_VOIGT: CwPv,
- cls.PPT.SPLIT_PSEUDO_VOIGT: CwSpv,
- cls.PPT.THOMPSON_COX_HASTINGS: CwTch,
- },
- cls.BM.TIME_OF_FLIGHT: {
- cls.PPT.PSEUDO_VOIGT: TofPv,
- cls.PPT.PSEUDO_VOIGT_IKEDA_CARPENTER: TofIc,
- cls.PPT.PSEUDO_VOIGT_BACK_TO_BACK: TofBtb,
- },
- },
- cls.ST.TOTAL: {
- cls.BM.CONSTANT_WAVELENGTH: {
- cls.PPT.GAUSSIAN_DAMPED_SINC: PdfGds,
- },
- cls.BM.TIME_OF_FLIGHT: {
- cls.PPT.GAUSSIAN_DAMPED_SINC: PdfGds,
- },
- },
- }
- return cls._supported
-
- @classmethod
- def create(
- cls,
- scattering_type: Optional[ScatteringTypeEnum] = None,
- beam_mode: Optional[BeamModeEnum] = None,
- profile_type: Optional[PeakProfileTypeEnum] = None,
- ):
- """Instantiate a peak profile for the given configuration.
-
- Args:
- scattering_type: Bragg or Total. Defaults to library
- default.
- beam_mode: CW or TOF. Defaults to library default.
- profile_type: Concrete profile within the mode. If omitted,
- a sensible default is chosen based on the other args.
-
- Returns:
- A newly created peak profile object.
-
- Raises:
- ValueError: If a requested option is not supported.
- """
- if beam_mode is None:
- beam_mode = BeamModeEnum.default()
- if scattering_type is None:
- scattering_type = ScatteringTypeEnum.default()
- if profile_type is None:
- profile_type = PeakProfileTypeEnum.default(scattering_type, beam_mode)
- supported = cls._supported_map()
- supported_scattering_types = list(supported.keys())
- if scattering_type not in supported_scattering_types:
- raise ValueError(
- f"Unsupported scattering type: '{scattering_type}'.\n"
- f'Supported scattering types: {supported_scattering_types}'
- )
-
- supported_beam_modes = list(supported[scattering_type].keys())
- if beam_mode not in supported_beam_modes:
- raise ValueError(
- f"Unsupported beam mode: '{beam_mode}' for scattering type: "
- f"'{scattering_type}'.\n Supported beam modes: '{supported_beam_modes}'"
- )
-
- supported_profile_types = list(supported[scattering_type][beam_mode].keys())
- if profile_type not in supported_profile_types:
- raise ValueError(
- f"Unsupported profile type '{profile_type}' for beam mode '{beam_mode}'.\n"
- f'Supported profile types: {supported_profile_types}'
- )
-
- peak_class = supported[scattering_type][beam_mode][profile_type]
- peak_obj = peak_class()
-
- return peak_obj
diff --git a/src/easydiffraction/experiments/categories/peak/tof.py b/src/easydiffraction/experiments/categories/peak/tof.py
deleted file mode 100644
index b38c4548..00000000
--- a/src/easydiffraction/experiments/categories/peak/tof.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Time-of-flight peak profile classes."""
-
-from easydiffraction.experiments.categories.peak.base import PeakBase
-from easydiffraction.experiments.categories.peak.tof_mixins import IkedaCarpenterAsymmetryMixin
-from easydiffraction.experiments.categories.peak.tof_mixins import TofBroadeningMixin
-
-
-class TofPseudoVoigt(
- PeakBase,
- TofBroadeningMixin,
-):
- """Time-of-flight pseudo-Voigt peak shape."""
-
- def __init__(self) -> None:
- super().__init__()
- self._add_time_of_flight_broadening()
-
-
-class TofPseudoVoigtIkedaCarpenter(
- PeakBase,
- TofBroadeningMixin,
- IkedaCarpenterAsymmetryMixin,
-):
- """TOF pseudo-Voigt with Ikeda–Carpenter asymmetry."""
-
- def __init__(self) -> None:
- super().__init__()
- self._add_time_of_flight_broadening()
- self._add_ikeda_carpenter_asymmetry()
-
-
-class TofPseudoVoigtBackToBack(
- PeakBase,
- TofBroadeningMixin,
- IkedaCarpenterAsymmetryMixin,
-):
- """TOF back-to-back pseudo-Voigt with asymmetry."""
-
- def __init__(self) -> None:
- super().__init__()
- self._add_time_of_flight_broadening()
- self._add_ikeda_carpenter_asymmetry()
diff --git a/src/easydiffraction/experiments/categories/peak/tof_mixins.py b/src/easydiffraction/experiments/categories/peak/tof_mixins.py
deleted file mode 100644
index ca459754..00000000
--- a/src/easydiffraction/experiments/categories/peak/tof_mixins.py
+++ /dev/null
@@ -1,293 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Time-of-flight (TOF) peak-profile mixins.
-
-Defines mixins that add Gaussian/Lorentz broadening, mixing, and
-Ikeda–Carpenter asymmetry parameters used by TOF peak shapes.
-"""
-
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class TofBroadeningMixin:
- """Mixin that adds TOF Gaussian/Lorentz broadening and mixing
- terms.
- """
-
- def _add_time_of_flight_broadening(self) -> None:
- """Create TOF broadening and mixing parameters."""
- self._broad_gauss_sigma_0: Parameter = Parameter(
- name='gauss_sigma_0',
- description='Gaussian broadening coefficient (instrumental resolution)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs²',
- cif_handler=CifHandler(
- names=[
- '_peak.gauss_sigma_0',
- ]
- ),
- )
- self._broad_gauss_sigma_1: Parameter = Parameter(
- name='gauss_sigma_1',
- description='Gaussian broadening coefficient (dependent on d-spacing)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs/Å',
- cif_handler=CifHandler(
- names=[
- '_peak.gauss_sigma_1',
- ]
- ),
- )
- self._broad_gauss_sigma_2: Parameter = Parameter(
- name='gauss_sigma_2',
- description='Gaussian broadening coefficient (instrument-dependent term)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs²/Ų',
- cif_handler=CifHandler(
- names=[
- '_peak.gauss_sigma_2',
- ]
- ),
- )
- self._broad_lorentz_gamma_0: Parameter = Parameter(
- name='lorentz_gamma_0',
- description='Lorentzian broadening coefficient (dependent on microstrain effects)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs',
- cif_handler=CifHandler(
- names=[
- '_peak.lorentz_gamma_0',
- ]
- ),
- )
- self._broad_lorentz_gamma_1: Parameter = Parameter(
- name='lorentz_gamma_1',
- description='Lorentzian broadening coefficient (dependent on d-spacing)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs/Å',
- cif_handler=CifHandler(
- names=[
- '_peak.lorentz_gamma_1',
- ]
- ),
- )
- self._broad_lorentz_gamma_2: Parameter = Parameter(
- name='lorentz_gamma_2',
- description='Lorentzian broadening coefficient (instrument-dependent term)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='µs²/Ų',
- cif_handler=CifHandler(
- names=[
- '_peak.lorentz_gamma_2',
- ]
- ),
- )
- self._broad_mix_beta_0: Parameter = Parameter(
- name='mix_beta_0',
- description='Mixing parameter. Defines the ratio of Gaussian '
- 'to Lorentzian contributions in TOF profiles',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='deg',
- cif_handler=CifHandler(
- names=[
- '_peak.mix_beta_0',
- ]
- ),
- )
- self._broad_mix_beta_1: Parameter = Parameter(
- name='mix_beta_1',
- description='Mixing parameter. Defines the ratio of Gaussian '
- 'to Lorentzian contributions in TOF profiles',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='deg',
- cif_handler=CifHandler(
- names=[
- '_peak.mix_beta_1',
- ]
- ),
- )
-
- @property
- def broad_gauss_sigma_0(self) -> Parameter:
- """Get Gaussian sigma_0 parameter."""
- return self._broad_gauss_sigma_0
-
- @broad_gauss_sigma_0.setter
- def broad_gauss_sigma_0(self, value: float) -> None:
- """Set Gaussian sigma_0 parameter."""
- self._broad_gauss_sigma_0.value = value
-
- @property
- def broad_gauss_sigma_1(self) -> Parameter:
- """Get Gaussian sigma_1 parameter."""
- return self._broad_gauss_sigma_1
-
- @broad_gauss_sigma_1.setter
- def broad_gauss_sigma_1(self, value: float) -> None:
- """Set Gaussian sigma_1 parameter."""
- self._broad_gauss_sigma_1.value = value
-
- @property
- def broad_gauss_sigma_2(self) -> Parameter:
- """Get Gaussian sigma_2 parameter."""
- return self._broad_gauss_sigma_2
-
- @broad_gauss_sigma_2.setter
- def broad_gauss_sigma_2(self, value: float) -> None:
- """Set Gaussian sigma_2 parameter."""
- self._broad_gauss_sigma_2.value = value
-
- @property
- def broad_lorentz_gamma_0(self) -> Parameter:
- """Get Lorentz gamma_0 parameter."""
- return self._broad_lorentz_gamma_0
-
- @broad_lorentz_gamma_0.setter
- def broad_lorentz_gamma_0(self, value: float) -> None:
- """Set Lorentz gamma_0 parameter."""
- self._broad_lorentz_gamma_0.value = value
-
- @property
- def broad_lorentz_gamma_1(self) -> Parameter:
- """Get Lorentz gamma_1 parameter."""
- return self._broad_lorentz_gamma_1
-
- @broad_lorentz_gamma_1.setter
- def broad_lorentz_gamma_1(self, value: float) -> None:
- """Set Lorentz gamma_1 parameter."""
- self._broad_lorentz_gamma_1.value = value
-
- @property
- def broad_lorentz_gamma_2(self) -> Parameter:
- """Get Lorentz gamma_2 parameter."""
- return self._broad_lorentz_gamma_2
-
- @broad_lorentz_gamma_2.setter
- def broad_lorentz_gamma_2(self, value: float) -> None:
- """Set Lorentz gamma_2 parameter."""
- self._broad_lorentz_gamma_2.value = value
-
- @property
- def broad_mix_beta_0(self) -> Parameter:
- """Get mixing parameter beta_0."""
- return self._broad_mix_beta_0
-
- @broad_mix_beta_0.setter
- def broad_mix_beta_0(self, value: float) -> None:
- """Set mixing parameter beta_0."""
- self._broad_mix_beta_0.value = value
-
- @property
- def broad_mix_beta_1(self) -> Parameter:
- """Get mixing parameter beta_1."""
- return self._broad_mix_beta_1
-
- @broad_mix_beta_1.setter
- def broad_mix_beta_1(self, value: float) -> None:
- """Set mixing parameter beta_1."""
- self._broad_mix_beta_1.value = value
-
-
-class IkedaCarpenterAsymmetryMixin:
- """Mixin that adds Ikeda–Carpenter asymmetry parameters."""
-
- def _add_ikeda_carpenter_asymmetry(self) -> None:
- """Create Ikeda–Carpenter asymmetry parameters alpha_0 and
- alpha_1.
- """
- self._asym_alpha_0: Parameter = Parameter(
- name='asym_alpha_0',
- description='Ikeda-Carpenter asymmetry parameter α₀',
- value_spec=AttributeSpec(
- value=0.01,
- type_=DataTypes.NUMERIC,
- default=0.01,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_alpha_0',
- ]
- ),
- )
- self._asym_alpha_1: Parameter = Parameter(
- name='asym_alpha_1',
- description='Ikeda-Carpenter asymmetry parameter α₁',
- value_spec=AttributeSpec(
- value=0.02,
- type_=DataTypes.NUMERIC,
- default=0.02,
- content_validator=RangeValidator(),
- ),
- units='',
- cif_handler=CifHandler(
- names=[
- '_peak.asym_alpha_1',
- ]
- ),
- )
-
- @property
- def asym_alpha_0(self) -> Parameter:
- """Get Ikeda–Carpenter asymmetry alpha_0."""
- return self._asym_alpha_0
-
- @asym_alpha_0.setter
- def asym_alpha_0(self, value: float) -> None:
- """Set Ikeda–Carpenter asymmetry alpha_0."""
- self._asym_alpha_0.value = value
-
- @property
- def asym_alpha_1(self) -> Parameter:
- """Get Ikeda–Carpenter asymmetry alpha_1."""
- return self._asym_alpha_1
-
- @asym_alpha_1.setter
- def asym_alpha_1(self, value: float) -> None:
- """Set Ikeda–Carpenter asymmetry alpha_1."""
- self._asym_alpha_1.value = value
diff --git a/src/easydiffraction/experiments/categories/peak/total.py b/src/easydiffraction/experiments/categories/peak/total.py
deleted file mode 100644
index e1f7c28c..00000000
--- a/src/easydiffraction/experiments/categories/peak/total.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Total-scattering (PDF) peak profile classes."""
-
-from easydiffraction.experiments.categories.peak.base import PeakBase
-from easydiffraction.experiments.categories.peak.total_mixins import TotalBroadeningMixin
-
-
-class TotalGaussianDampedSinc(
- PeakBase,
- TotalBroadeningMixin,
-):
- """Gaussian-damped sinc peak for total scattering (PDF)."""
-
- def __init__(self) -> None:
- super().__init__()
- self._add_pair_distribution_function_broadening()
diff --git a/src/easydiffraction/experiments/categories/peak/total_mixins.py b/src/easydiffraction/experiments/categories/peak/total_mixins.py
deleted file mode 100644
index 03907ffa..00000000
--- a/src/easydiffraction/experiments/categories/peak/total_mixins.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Total scattering/PDF peak-profile mixins.
-
-Adds damping, broadening, sharpening and envelope parameters used in
-pair distribution function (PDF) modeling.
-"""
-
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class TotalBroadeningMixin:
- """Mixin adding PDF broadening/damping/sharpening parameters."""
-
- def _add_pair_distribution_function_broadening(self):
- """Create PDF parameters: damp_q, broad_q, cutoff_q,
- sharp deltas, and particle diameter envelope.
- """
- self._damp_q: Parameter = Parameter(
- name='damp_q',
- description='Instrumental Q-resolution damping factor '
- '(affects high-r PDF peak amplitude)',
- value_spec=AttributeSpec(
- value=0.05,
- type_=DataTypes.NUMERIC,
- default=0.05,
- content_validator=RangeValidator(),
- ),
- units='Å⁻¹',
- cif_handler=CifHandler(
- names=[
- '_peak.damp_q',
- ]
- ),
- )
- self._broad_q: Parameter = Parameter(
- name='broad_q',
- description='Quadratic PDF peak broadening coefficient '
- '(thermal and model uncertainty contribution)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='Å⁻²',
- cif_handler=CifHandler(
- names=[
- '_peak.broad_q',
- ]
- ),
- )
- self._cutoff_q: Parameter = Parameter(
- name='cutoff_q',
- description='Q-value cutoff applied to model PDF for Fourier '
- 'transform (controls real-space resolution)',
- value_spec=AttributeSpec(
- value=25.0,
- type_=DataTypes.NUMERIC,
- default=25.0,
- content_validator=RangeValidator(),
- ),
- units='Å⁻¹',
- cif_handler=CifHandler(
- names=[
- '_peak.cutoff_q',
- ]
- ),
- )
- self._sharp_delta_1: Parameter = Parameter(
- name='sharp_delta_1',
- description='PDF peak sharpening coefficient (1/r dependence)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='Å',
- cif_handler=CifHandler(
- names=[
- '_peak.sharp_delta_1',
- ]
- ),
- )
- self._sharp_delta_2: Parameter = Parameter(
- name='sharp_delta_2',
- description='PDF peak sharpening coefficient (1/r² dependence)',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='Ų',
- cif_handler=CifHandler(
- names=[
- '_peak.sharp_delta_2',
- ]
- ),
- )
- self._damp_particle_diameter: Parameter = Parameter(
- name='damp_particle_diameter',
- description='Particle diameter for spherical envelope damping correction in PDF',
- value_spec=AttributeSpec(
- value=0.0,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- units='Å',
- cif_handler=CifHandler(
- names=[
- '_peak.damp_particle_diameter',
- ]
- ),
- )
-
- @property
- def damp_q(self) -> Parameter:
- """Get Q-resolution damping factor."""
- return self._damp_q
-
- @damp_q.setter
- def damp_q(self, value: float) -> None:
- """Set Q-resolution damping factor."""
- self._damp_q.value = value
-
- @property
- def broad_q(self) -> Parameter:
- """Get quadratic PDF broadening coefficient."""
- return self._broad_q
-
- @broad_q.setter
- def broad_q(self, value: float) -> None:
- """Set quadratic PDF broadening coefficient."""
- self._broad_q.value = value
-
- @property
- def cutoff_q(self) -> Parameter:
- """Get Q cutoff used for Fourier transform."""
- return self._cutoff_q
-
- @cutoff_q.setter
- def cutoff_q(self, value: float) -> None:
- """Set Q cutoff used for Fourier transform."""
- self._cutoff_q.value = value
-
- @property
- def sharp_delta_1(self) -> Parameter:
- """Get sharpening coefficient with 1/r dependence."""
- return self._sharp_delta_1
-
- @sharp_delta_1.setter
- def sharp_delta_1(self, value: float) -> None:
- """Set sharpening coefficient with 1/r dependence."""
- self._sharp_delta_1.value = value
-
- @property
- def sharp_delta_2(self) -> Parameter:
- """Get sharpening coefficient with 1/r^2 dependence."""
- return self._sharp_delta_2
-
- @sharp_delta_2.setter
- def sharp_delta_2(self, value: float) -> None:
- """Set sharpening coefficient with 1/r^2 dependence."""
- self._sharp_delta_2.value = value
-
- @property
- def damp_particle_diameter(self) -> Parameter:
- """Get particle diameter for spherical envelope damping."""
- return self._damp_particle_diameter
-
- @damp_particle_diameter.setter
- def damp_particle_diameter(self, value: float) -> None:
- """Set particle diameter for spherical envelope damping."""
- self._damp_particle_diameter.value = value
diff --git a/src/easydiffraction/experiments/experiment/__init__.py b/src/easydiffraction/experiments/experiment/__init__.py
deleted file mode 100644
index a2a39eea..00000000
--- a/src/easydiffraction/experiments/experiment/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from easydiffraction.experiments.experiment.base import ExperimentBase
-from easydiffraction.experiments.experiment.base import PdExperimentBase
-from easydiffraction.experiments.experiment.bragg_pd import BraggPdExperiment
-from easydiffraction.experiments.experiment.bragg_sc import BraggScExperiment
-from easydiffraction.experiments.experiment.total_pd import TotalPdExperiment
-
-__all__ = [
- 'ExperimentBase',
- 'PdExperimentBase',
- 'BraggPdExperiment',
- 'TotalPdExperiment',
- 'BraggScExperiment',
-]
diff --git a/src/easydiffraction/experiments/experiment/base.py b/src/easydiffraction/experiments/experiment/base.py
deleted file mode 100644
index 35ed7b1d..00000000
--- a/src/easydiffraction/experiments/experiment/base.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-from abc import abstractmethod
-from typing import TYPE_CHECKING
-from typing import Any
-from typing import List
-
-from easydiffraction.core.datablock import DatablockItem
-from easydiffraction.experiments.categories.data.factory import DataFactory
-from easydiffraction.experiments.categories.excluded_regions import ExcludedRegions
-from easydiffraction.experiments.categories.linked_phases import LinkedPhases
-from easydiffraction.experiments.categories.peak.factory import PeakFactory
-from easydiffraction.experiments.categories.peak.factory import PeakProfileTypeEnum
-from easydiffraction.io.cif.serialize import experiment_to_cif
-from easydiffraction.utils.logging import console
-from easydiffraction.utils.logging import log
-from easydiffraction.utils.utils import render_cif
-from easydiffraction.utils.utils import render_table
-
-if TYPE_CHECKING:
- from easydiffraction.experiments.categories.experiment_type import ExperimentType
- from easydiffraction.sample_models.sample_models import SampleModels
-
-
-class ExperimentBase(DatablockItem):
- """Base class for all experiments with only core attributes.
-
- Wraps experiment type and instrument.
- """
-
- def __init__(
- self,
- *,
- name: str,
- type: ExperimentType,
- ):
- super().__init__()
- self._name = name
- self._type = type
- # TODO: Should return default calculator based on experiment
- # type
- from easydiffraction.analysis.calculators.factory import CalculatorFactory
-
- self._calculator = CalculatorFactory.create_calculator('cryspy')
- self._identity.datablock_entry_name = lambda: self.name
-
- @property
- def name(self) -> str:
- """Human-readable name of the experiment."""
- return self._name
-
- @name.setter
- def name(self, new: str) -> None:
- """Rename the experiment.
-
- Args:
- new: New name for this experiment.
- """
- self._name = new
-
- @property
- def type(self): # TODO: Consider another name
- """Experiment type descriptor (sample form, probe, beam
- mode).
- """
- return self._type
-
- @property
- def calculator(self):
- """Calculator engine used for pattern calculations."""
- return self._calculator
-
- @property
- def as_cif(self) -> str:
- """Serialize this experiment to a CIF fragment."""
- return experiment_to_cif(self)
-
- def show_as_cif(self) -> None:
- """Pretty-print the experiment as CIF text."""
- experiment_cif = super().as_cif
- paragraph_title: str = f"Experiment 🔬 '{self.name}' as cif"
- console.paragraph(paragraph_title)
- render_cif(experiment_cif)
-
- @abstractmethod
- 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.
- """
- raise NotImplementedError()
-
-
-class PdExperimentBase(ExperimentBase):
- """Base class for all powder experiments."""
-
- def __init__(
- self,
- *,
- name: str,
- type: ExperimentType,
- ) -> None:
- super().__init__(name=name, type=type)
-
- self._linked_phases: LinkedPhases = LinkedPhases()
- self._excluded_regions: ExcludedRegions = ExcludedRegions()
-
- self._peak_profile_type: PeakProfileTypeEnum = PeakProfileTypeEnum.default(
- self.type.scattering_type.value,
- self.type.beam_mode.value,
- )
- self._peak = PeakFactory.create(
- scattering_type=self.type.scattering_type.value,
- beam_mode=self.type.beam_mode.value,
- profile_type=self._peak_profile_type,
- )
-
- self._data = DataFactory.create(
- sample_form=self.type.sample_form.value,
- beam_mode=self.type.beam_mode.value,
- scattering_type=self.type.scattering_type.value,
- )
-
- def _get_valid_linked_phases(
- self,
- sample_models: SampleModels,
- ) -> List[Any]:
- """Get valid linked phases for this experiment.
-
- Args:
- sample_models: Collection of sample models.
-
- Returns:
- A list of valid linked phases.
- """
- if not self.linked_phases:
- print('Warning: No linked phases defined. Returning empty pattern.')
- return []
-
- valid_linked_phases = []
- for linked_phase in self.linked_phases:
- if linked_phase._identity.category_entry_name not in sample_models.names:
- print(
- f"Warning: Linked phase '{linked_phase.id.value}' not "
- f'found in Sample Models {sample_models.names}. Skipping it.'
- )
- continue
- valid_linked_phases.append(linked_phase)
-
- if not valid_linked_phases:
- print(
- 'Warning: None of the linked phases found in Sample '
- 'Models. Returning empty pattern.'
- )
-
- return valid_linked_phases
-
- @abstractmethod
- 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).
- """
- pass
-
- @property
- def linked_phases(self):
- """Collection of phases linked to this experiment."""
- return self._linked_phases
-
- @property
- def excluded_regions(self):
- """Collection of excluded regions for the x-grid."""
- return self._excluded_regions
-
- @property
- def peak(self) -> str:
- """Peak category object with profile parameters and mixins."""
- return self._peak
-
- @peak.setter
- def peak(self, value):
- """Replace the peak model used for this powder experiment.
-
- Args:
- value: New peak object created by the `PeakFactory`.
- """
- self._peak = value
-
- @property
- def data(self):
- return self._data
-
- @property
- def peak_profile_type(self):
- """Currently selected peak profile type enum."""
- return self._peak_profile_type
-
- @peak_profile_type.setter
- def peak_profile_type(self, new_type: str | PeakProfileTypeEnum):
- """Change the active peak profile type, if supported.
-
- Args:
- new_type: New profile type as enum or its string value.
- """
- if isinstance(new_type, str):
- try:
- new_type = PeakProfileTypeEnum(new_type)
- except ValueError:
- log.warning(f"Unknown peak profile type '{new_type}'")
- return
-
- supported_types = list(
- PeakFactory._supported[self.type.scattering_type.value][
- self.type.beam_mode.value
- ].keys()
- )
-
- if new_type not in supported_types:
- log.warning(
- f"Unsupported peak profile '{new_type.value}', "
- f'Supported peak profiles: {supported_types}',
- "For more information, use 'show_supported_peak_profile_types()'",
- )
- return
-
- self._peak = PeakFactory.create(
- scattering_type=self.type.scattering_type.value,
- beam_mode=self.type.beam_mode.value,
- profile_type=new_type,
- )
- self._peak_profile_type = new_type
- console.paragraph(f"Peak profile type for experiment '{self.name}' changed to")
- console.print(new_type.value)
-
- def show_supported_peak_profile_types(self):
- """Print available peak profile types for this experiment."""
- columns_headers = ['Peak profile type', 'Description']
- columns_alignment = ['left', 'left']
- columns_data = []
-
- scattering_type = self.type.scattering_type.value
- beam_mode = self.type.beam_mode.value
-
- for profile_type in PeakFactory._supported[scattering_type][beam_mode]:
- columns_data.append([profile_type.value, profile_type.description()])
-
- console.paragraph('Supported peak profile types')
- render_table(
- columns_headers=columns_headers,
- columns_alignment=columns_alignment,
- columns_data=columns_data,
- )
-
- def show_current_peak_profile_type(self):
- """Print the currently selected peak profile type."""
- console.paragraph('Current peak profile type')
- console.print(self.peak_profile_type)
diff --git a/src/easydiffraction/experiments/experiment/bragg_pd.py b/src/easydiffraction/experiments/experiment/bragg_pd.py
deleted file mode 100644
index 3821419a..00000000
--- a/src/easydiffraction/experiments/experiment/bragg_pd.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-import numpy as np
-
-from easydiffraction.experiments.categories.background.enums import BackgroundTypeEnum
-from easydiffraction.experiments.categories.background.factory import BackgroundFactory
-from easydiffraction.experiments.experiment.base import PdExperimentBase
-from easydiffraction.experiments.experiment.instrument_mixin import InstrumentMixin
-from easydiffraction.utils.logging import console
-from easydiffraction.utils.logging import log
-from easydiffraction.utils.utils import render_table
-
-if TYPE_CHECKING:
- from easydiffraction.experiments.categories.experiment_type import ExperimentType
-
-
-class BraggPdExperiment(InstrumentMixin, PdExperimentBase):
- """Powder diffraction experiment.
-
- Wraps background model, peak profile and linked phases for Bragg PD.
- """
-
- def __init__(
- self,
- *,
- name: str,
- type: ExperimentType,
- ) -> None:
- super().__init__(name=name, type=type)
-
- self._background_type: BackgroundTypeEnum = BackgroundTypeEnum.default()
- self._background = BackgroundFactory.create(background_type=self.background_type)
-
- @property
- def background(self):
- return self._background
-
- @background.setter
- def background(self, value):
- self._background = value
-
- # -------------
- # Measured data
- # -------------
-
- def _load_ascii_data_to_experiment(self, data_path: str) -> None:
- """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
- ``sqrt(y)`` with small values clamped to ``1.0``.
- """
- try:
- data = np.loadtxt(data_path)
- except Exception as e:
- raise IOError(f'Failed to read data from {data_path}: {e}') from e
-
- if data.shape[1] < 2:
- raise ValueError('Data file must have at least two columns: x and y.')
-
- if data.shape[1] < 3:
- print('Warning: No uncertainty (sy) column provided. Defaulting to sqrt(y).')
-
- # Extract x, y data
- x: np.ndarray = data[:, 0]
- y: np.ndarray = data[:, 1]
-
- # Round x to 4 decimal places
- x = np.round(x, 4)
-
- # Determine sy from column 3 if available, otherwise use sqrt(y)
- sy: np.ndarray = data[:, 2] if data.shape[1] > 2 else np.sqrt(y)
-
- # Replace values smaller than 0.0001 with 1.0
- sy = np.where(sy < 0.0001, 1.0, sy)
-
- # Set the experiment data
- self.data._set_x(x)
- self.data._set_meas(y)
- self.data._set_meas_su(sy)
-
- console.paragraph('Data loaded successfully')
- console.print(f"Experiment 🔬 '{self.name}'. Number of data points: {len(x)}")
-
- @property
- def background_type(self):
- """Current background type enum value."""
- return self._background_type
-
- @background_type.setter
- def background_type(self, new_type):
- """Set and apply a new background type.
-
- Falls back to printing supported types if the new value is not
- supported.
- """
- if new_type not in BackgroundFactory._supported_map():
- supported_types = list(BackgroundFactory._supported_map().keys())
- log.warning(
- f"Unknown background type '{new_type}'. "
- f'Supported background types: {[bt.value for bt in supported_types]}. '
- f"For more information, use 'show_supported_background_types()'"
- )
- return
- self.background = BackgroundFactory.create(new_type)
- self._background_type = new_type
- console.paragraph(f"Background type for experiment '{self.name}' changed to")
- console.print(new_type)
-
- def show_supported_background_types(self):
- """Print a table of supported background types."""
- columns_headers = ['Background type', 'Description']
- columns_alignment = ['left', 'left']
- columns_data = []
- for bt in BackgroundFactory._supported_map():
- columns_data.append([bt.value, bt.description()])
-
- console.paragraph('Supported background types')
- render_table(
- columns_headers=columns_headers,
- columns_alignment=columns_alignment,
- columns_data=columns_data,
- )
-
- def show_current_background_type(self):
- """Print the currently used background type."""
- console.paragraph('Current background type')
- console.print(self.background_type)
diff --git a/src/easydiffraction/experiments/experiment/bragg_sc.py b/src/easydiffraction/experiments/experiment/bragg_sc.py
deleted file mode 100644
index 6b95d559..00000000
--- a/src/easydiffraction/experiments/experiment/bragg_sc.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Single crystal experiment types and helpers."""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from easydiffraction.experiments.experiment.base import ExperimentBase
-
-if TYPE_CHECKING:
- from easydiffraction.experiments.categories.experiment_type import ExperimentType
-
-
-class BraggScExperiment(ExperimentBase):
- """Single crystal experiment class with specific attributes."""
-
- def __init__(
- self,
- *,
- name: str,
- type: ExperimentType,
- ) -> None:
- super().__init__(name=name, type=type)
- self.linked_crystal = None
-
- def show_meas_chart(self) -> None:
- print('Showing measured data chart is not implemented yet.')
diff --git a/src/easydiffraction/experiments/experiment/factory.py b/src/easydiffraction/experiments/experiment/factory.py
deleted file mode 100644
index 0db5e347..00000000
--- a/src/easydiffraction/experiments/experiment/factory.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from easydiffraction.core.factory import FactoryBase
-from easydiffraction.experiments.categories.experiment_type import ExperimentType
-from easydiffraction.experiments.experiment import BraggPdExperiment
-from easydiffraction.experiments.experiment import BraggScExperiment
-from easydiffraction.experiments.experiment import TotalPdExperiment
-from easydiffraction.experiments.experiment.enums import BeamModeEnum
-from easydiffraction.experiments.experiment.enums import RadiationProbeEnum
-from easydiffraction.experiments.experiment.enums import SampleFormEnum
-from easydiffraction.experiments.experiment.enums import ScatteringTypeEnum
-from easydiffraction.io.cif.parse import document_from_path
-from easydiffraction.io.cif.parse import document_from_string
-from easydiffraction.io.cif.parse import name_from_block
-from easydiffraction.io.cif.parse import pick_sole_block
-
-if TYPE_CHECKING:
- import gemmi
-
- from easydiffraction.experiments.experiment.base import ExperimentBase
-
-
-class ExperimentFactory(FactoryBase):
- """Creates Experiment instances with only relevant attributes."""
-
- _ALLOWED_ARG_SPECS = [
- {'required': ['cif_path'], 'optional': []},
- {'required': ['cif_str'], 'optional': []},
- {
- 'required': ['name', 'data_path'],
- 'optional': ['sample_form', 'beam_mode', 'radiation_probe', 'scattering_type'],
- },
- {
- 'required': ['name'],
- 'optional': ['sample_form', 'beam_mode', 'radiation_probe', 'scattering_type'],
- },
- ]
-
- _SUPPORTED = {
- ScatteringTypeEnum.BRAGG: {
- SampleFormEnum.POWDER: BraggPdExperiment,
- SampleFormEnum.SINGLE_CRYSTAL: BraggScExperiment,
- },
- ScatteringTypeEnum.TOTAL: {
- SampleFormEnum.POWDER: TotalPdExperiment,
- },
- }
-
- @classmethod
- def _make_experiment_type(cls, kwargs):
- """Helper to construct an ExperimentType from keyword arguments,
- using defaults as needed.
- """
- # TODO: Defaults are already in the experiment type...
- # TODO: Merging with experiment_type_from_block from
- # io.cif.parse
- return ExperimentType(
- sample_form=kwargs.get('sample_form', SampleFormEnum.default().value),
- beam_mode=kwargs.get('beam_mode', BeamModeEnum.default().value),
- radiation_probe=kwargs.get('radiation_probe', RadiationProbeEnum.default().value),
- scattering_type=kwargs.get('scattering_type', ScatteringTypeEnum.default().value),
- )
-
- # TODO: Move to a common CIF utility module? io.cif.parse?
- @classmethod
- def _create_from_gemmi_block(
- cls,
- block: gemmi.cif.Block,
- ) -> ExperimentBase:
- """Build a model instance from a single CIF block."""
- name = name_from_block(block)
-
- # TODO: move to io.cif.parse?
- expt_type = ExperimentType()
- for param in expt_type.parameters:
- param.from_cif(block)
-
- # Create experiment instance of appropriate class
- # TODO: make helper method to create experiment from type
- scattering_type = expt_type.scattering_type.value
- sample_form = expt_type.sample_form.value
- expt_class = cls._SUPPORTED[scattering_type][sample_form]
- expt_obj = expt_class(name=name, type=expt_type)
-
- # Read all categories from CIF block
- # TODO: move to io.cif.parse?
- for category in expt_obj.categories:
- category.from_cif(block)
-
- return expt_obj
-
- @classmethod
- def _create_from_cif_path(
- cls,
- cif_path: str,
- ) -> ExperimentBase:
- """Create an experiment from a CIF file path."""
- doc = document_from_path(cif_path)
- block = pick_sole_block(doc)
- return cls._create_from_gemmi_block(block)
-
- @classmethod
- def _create_from_cif_str(
- cls,
- cif_str: str,
- ) -> ExperimentBase:
- """Create an experiment from a CIF string."""
- doc = document_from_string(cif_str)
- block = pick_sole_block(doc)
- return cls._create_from_gemmi_block(block)
-
- @classmethod
- def _create_from_data_path(cls, kwargs):
- """Create an experiment from a raw data ASCII file.
-
- Loads the experiment and attaches measured data from the
- specified file.
- """
- expt_type = cls._make_experiment_type(kwargs)
- scattering_type = expt_type.scattering_type.value
- sample_form = expt_type.sample_form.value
- expt_class = cls._SUPPORTED[scattering_type][sample_form]
- expt_name = kwargs['name']
- expt_obj = expt_class(name=expt_name, type=expt_type)
- data_path = kwargs['data_path']
- expt_obj._load_ascii_data_to_experiment(data_path)
- return expt_obj
-
- @classmethod
- def _create_without_data(cls, kwargs):
- """Create an experiment without measured data.
-
- Returns an experiment instance with only metadata and
- configuration.
- """
- expt_type = cls._make_experiment_type(kwargs)
- scattering_type = expt_type.scattering_type.value
- sample_form = expt_type.sample_form.value
- expt_class = cls._SUPPORTED[scattering_type][sample_form]
- expt_name = kwargs['name']
- expt_obj = expt_class(name=expt_name, type=expt_type)
- return expt_obj
-
- @classmethod
- def create(cls, **kwargs):
- """Create an `ExperimentBase` using a validated argument
- combination.
- """
- # TODO: move to FactoryBase
- # Check for valid argument combinations
- user_args = {k for k, v in kwargs.items() if v is not None}
- cls._validate_args(
- present=user_args,
- allowed_specs=cls._ALLOWED_ARG_SPECS,
- factory_name=cls.__name__, # TODO: move to FactoryBase
- )
-
- # Validate enum arguments if provided
- if 'sample_form' in kwargs:
- SampleFormEnum(kwargs['sample_form'])
- if 'beam_mode' in kwargs:
- BeamModeEnum(kwargs['beam_mode'])
- if 'radiation_probe' in kwargs:
- RadiationProbeEnum(kwargs['radiation_probe'])
- if 'scattering_type' in kwargs:
- ScatteringTypeEnum(kwargs['scattering_type'])
-
- # Dispatch to the appropriate creation method
- if 'cif_path' in kwargs:
- return cls._create_from_cif_path(kwargs['cif_path'])
- elif 'cif_str' in kwargs:
- return cls._create_from_cif_str(kwargs['cif_str'])
- elif 'data_path' in kwargs:
- return cls._create_from_data_path(kwargs)
- elif 'name' in kwargs:
- return cls._create_without_data(kwargs)
diff --git a/src/easydiffraction/experiments/experiment/instrument_mixin.py b/src/easydiffraction/experiments/experiment/instrument_mixin.py
deleted file mode 100644
index c83c07bc..00000000
--- a/src/easydiffraction/experiments/experiment/instrument_mixin.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from typeguard import typechecked
-
-from easydiffraction.experiments.categories.instrument.factory import InstrumentFactory
-
-if TYPE_CHECKING:
- from easydiffraction.experiments.categories.instrument.base import InstrumentBase
-
-
-class InstrumentMixin:
- """Mixin that wires an experiment to an instrument category.
-
- Creates a default instrument via `InstrumentFactory` using the
- experiment type (scattering type and beam mode) at initialization.
- """
-
- def __init__(self, *args, **kwargs):
- expt_type = kwargs.get('type')
- super().__init__(*args, **kwargs)
- self._instrument = InstrumentFactory.create(
- scattering_type=expt_type.scattering_type.value,
- beam_mode=expt_type.beam_mode.value,
- )
-
- @property
- def instrument(self):
- """Instrument category object associated with the experiment."""
- return self._instrument
-
- @instrument.setter
- @typechecked
- def instrument(self, new_instrument: InstrumentBase):
- """Replace the instrument and re-parent it to this experiment.
-
- Args:
- new_instrument: Instrument instance compatible with the
- experiment type.
- """
- self._instrument = new_instrument
- self._instrument._parent = self
diff --git a/src/easydiffraction/experiments/experiment/total_pd.py b/src/easydiffraction/experiments/experiment/total_pd.py
deleted file mode 100644
index 2f2f5f89..00000000
--- a/src/easydiffraction/experiments/experiment/total_pd.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-import numpy as np
-
-from easydiffraction.experiments.experiment.base import PdExperimentBase
-from easydiffraction.utils.logging import console
-
-if TYPE_CHECKING:
- from easydiffraction.experiments.categories.experiment_type import ExperimentType
-
-
-class TotalPdExperiment(PdExperimentBase):
- """PDF experiment class with specific attributes."""
-
- def __init__(
- self,
- name: str,
- type: ExperimentType,
- ):
- super().__init__(name=name, type=type)
-
- 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
- """
- try:
- from diffpy.utils.parsers.loaddata import loadData
- except ImportError:
- raise ImportError('diffpy module not found.') from None
- try:
- data = loadData(data_path)
- except Exception as e:
- raise IOError(f'Failed to read data from {data_path}: {e}') from e
-
- if data.shape[1] < 2:
- raise ValueError('Data file must have at least two columns: x and y.')
-
- default_sy = 0.03
- if data.shape[1] < 3:
- print(f'Warning: No uncertainty (sy) column provided. Defaulting to {default_sy}.')
-
- x = data[:, 0]
- y = data[:, 1]
- sy = data[:, 2] if data.shape[1] > 2 else np.full_like(y, fill_value=default_sy)
-
- self.data._set_x(x)
- self.data._set_meas(y)
- self.data._set_meas_su(sy)
-
- console.paragraph('Data loaded successfully')
- console.print(f"Experiment 🔬 '{self.name}'. Number of data points: {len(x)}")
diff --git a/src/easydiffraction/experiments/experiments.py b/src/easydiffraction/experiments/experiments.py
deleted file mode 100644
index 9f58a3b7..00000000
--- a/src/easydiffraction/experiments/experiments.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from typeguard import typechecked
-
-from easydiffraction.core.datablock import DatablockCollection
-from easydiffraction.experiments.experiment.base import ExperimentBase
-from easydiffraction.experiments.experiment.factory import ExperimentFactory
-from easydiffraction.utils.logging import console
-
-
-class Experiments(DatablockCollection):
- """Collection of Experiment data blocks.
-
- Provides convenience constructors for common creation patterns and
- helper methods for simple presentation of collection contents.
- """
-
- def __init__(self) -> None:
- super().__init__(item_type=ExperimentBase)
-
- # --------------------
- # Add / Remove methods
- # --------------------
-
- # TODO: Move to DatablockCollection?
- # TODO: Disallow args and only allow kwargs?
- def add(self, **kwargs):
- experiment = kwargs.pop('experiment', None)
-
- if experiment is None:
- experiment = ExperimentFactory.create(**kwargs)
-
- self._add(experiment)
-
- # @typechecked
- # def add_from_cif_path(self, cif_path: str):
- # """Add an experiment from a CIF file path.
- #
- # Args:
- # cif_path: Path to a CIF document.
- # """
- # experiment = ExperimentFactory.create(cif_path=cif_path)
- # self.add(experiment)
-
- # @typechecked
- # def add_from_cif_str(self, cif_str: str):
- # """Add an experiment from a CIF string.
- #
- # Args:
- # cif_str: Full CIF document as a string.
- # """
- # experiment = ExperimentFactory.create(cif_str=cif_str)
- # self.add(experiment)
-
- # @typechecked
- # def add_from_data_path(
- # self,
- # name: str,
- # data_path: str,
- # sample_form: str = SampleFormEnum.default().value,
- # beam_mode: str = BeamModeEnum.default().value,
- # radiation_probe: str = RadiationProbeEnum.default().value,
- # scattering_type: str = ScatteringTypeEnum.default().value,
- # ):
- # """Add an experiment from a data file path.
- #
- # Args:
- # name: Experiment identifier.
- # data_path: Path to the measured data file.
- # sample_form: Sample form (powder or single crystal).
- # beam_mode: Beam mode (constant wavelength or TOF).
- # radiation_probe: Radiation probe (neutron or xray).
- # scattering_type: Scattering type (bragg or total).
- # """
- # experiment = ExperimentFactory.create(
- # name=name,
- # data_path=data_path,
- # sample_form=sample_form,
- # beam_mode=beam_mode,
- # radiation_probe=radiation_probe,
- # scattering_type=scattering_type,
- # )
- # self.add(experiment)
-
- # @typechecked
- # def add_without_data(
- # self,
- # name: str,
- # sample_form: str = SampleFormEnum.default().value,
- # beam_mode: str = BeamModeEnum.default().value,
- # radiation_probe: str = RadiationProbeEnum.default().value,
- # scattering_type: str = ScatteringTypeEnum.default().value,
- # ):
- # """Add an experiment without associating a data file.
- #
- # Args:
- # name: Experiment identifier.
- # sample_form: Sample form (powder or single crystal).
- # beam_mode: Beam mode (constant wavelength or TOF).
- # radiation_probe: Radiation probe (neutron or xray).
- # scattering_type: Scattering type (bragg or total).
- # """
- # experiment = ExperimentFactory.create(
- # name=name,
- # sample_form=sample_form,
- # beam_mode=beam_mode,
- # radiation_probe=radiation_probe,
- # scattering_type=scattering_type,
- # )
- # self.add(experiment)
-
- # TODO: Move to DatablockCollection?
- @typechecked
- def remove(self, name: str) -> None:
- """Remove an experiment by name if it exists."""
- if name in self:
- del self[name]
-
- # ------------
- # Show methods
- # ------------
-
- # TODO: Move to DatablockCollection?
- def show_names(self) -> None:
- """Print the list of experiment names."""
- console.paragraph('Defined experiments' + ' 🔬')
- console.print(self.names)
-
- # TODO: Move to DatablockCollection?
- def show_params(self) -> None:
- """Print parameters for each experiment in the collection."""
- for exp in self.values():
- exp.show_params()
diff --git a/src/easydiffraction/io/__init__.py b/src/easydiffraction/io/__init__.py
index 429f2648..6ce45a95 100644
--- a/src/easydiffraction/io/__init__.py
+++ b/src/easydiffraction/io/__init__.py
@@ -1,2 +1,7 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
# SPDX-License-Identifier: BSD-3-Clause
+
+from easydiffraction.io.ascii import extract_data_paths_from_dir
+from easydiffraction.io.ascii import extract_data_paths_from_zip
+from easydiffraction.io.ascii import extract_metadata
+from easydiffraction.io.ascii import load_numeric_block
diff --git a/src/easydiffraction/io/ascii.py b/src/easydiffraction/io/ascii.py
new file mode 100644
index 00000000..6987d217
--- /dev/null
+++ b/src/easydiffraction/io/ascii.py
@@ -0,0 +1,182 @@
+# SPDX-FileCopyrightText: 2026 EasyScience contributors
+# SPDX-License-Identifier: BSD-3-Clause
+"""Helpers for loading numeric data from ASCII files."""
+
+from __future__ import annotations
+
+import tempfile
+import zipfile
+from io import StringIO
+from pathlib import Path
+
+import numpy as np
+
+
+def extract_data_paths_from_zip(zip_path: str | Path) -> list[str]:
+ """
+ Extract all files from a ZIP archive and return their paths.
+
+ Files are extracted into a temporary directory that persists for the
+ lifetime of the process. The returned paths are sorted
+ lexicographically by file name so that numbered data files (e.g.
+ ``scan_001.dat``, ``scan_002.dat``) appear in natural order. Hidden
+ files and directories (names starting with ``'.'`` or ``'__'``) are
+ excluded.
+
+ Parameters
+ ----------
+ zip_path : str | Path
+ Path to the ZIP archive.
+
+ Returns
+ -------
+ list[str]
+ Sorted absolute paths to the extracted data files.
+
+ Raises
+ ------
+ FileNotFoundError
+ If *zip_path* does not exist.
+ ValueError
+ If the archive contains no usable data files.
+ """
+ zip_path = Path(zip_path)
+ if not zip_path.exists():
+ raise FileNotFoundError(f'ZIP file not found: {zip_path}')
+
+ # TODO: Unify mkdir with other uses in the code
+ extract_dir = Path(tempfile.mkdtemp(prefix='ed_zip_'))
+
+ with zipfile.ZipFile(zip_path, 'r') as zf:
+ zf.extractall(extract_dir)
+
+ paths = sorted(
+ str(p)
+ for p in extract_dir.rglob('*')
+ if p.is_file() and not p.name.startswith('.') and not p.name.startswith('__')
+ )
+
+ if not paths:
+ raise ValueError(f'No data files found in ZIP archive: {zip_path}')
+
+ return paths
+
+
+def extract_data_paths_from_dir(
+ dir_path: str | Path,
+ file_pattern: str = '*',
+) -> list[str]:
+ """
+ List data files in a directory and return their sorted paths.
+
+ Hidden files (names starting with ``'.'`` or ``'__'``) are excluded.
+ The returned paths are sorted lexicographically by file name.
+
+ Parameters
+ ----------
+ dir_path : str | Path
+ Path to the directory containing data files.
+ file_pattern : str, default='*'
+ Glob pattern to filter files (e.g. ``'*.dat'``, ``'*.xye'``).
+
+ Returns
+ -------
+ list[str]
+ Sorted absolute paths to the matching data files.
+
+ Raises
+ ------
+ FileNotFoundError
+ If *dir_path* does not exist or is not a directory.
+ ValueError
+ If no matching data files are found.
+ """
+ dir_path = Path(dir_path)
+ if not dir_path.is_dir():
+ raise FileNotFoundError(f'Directory not found: {dir_path}')
+
+ paths = sorted(
+ str(p)
+ for p in dir_path.glob(file_pattern)
+ if p.is_file() and not p.name.startswith('.') and not p.name.startswith('__')
+ )
+
+ if not paths:
+ raise ValueError(f"No files matching '{file_pattern}' found in directory: {dir_path}")
+
+ return paths
+
+
+def extract_metadata(
+ file_path: str | Path,
+ pattern: str,
+) -> float | None:
+ """
+ Extract a single numeric value from a file using a regex pattern.
+
+ The entire file content is searched (not just the header). The
+ **first** match is used. The regex must contain exactly one capture
+ group whose match is convertible to ``float``.
+
+ Parameters
+ ----------
+ file_path : str | Path
+ Path to the input file.
+ pattern : str
+ Regex with one capture group that matches the numeric value.
+
+ Returns
+ -------
+ float | None
+ The extracted value, or ``None`` if the pattern did not match or
+ the captured text could not be converted to float.
+ """
+ import re
+
+ content = Path(file_path).read_text(encoding='utf-8', errors='ignore')
+ match = re.search(pattern, content, re.MULTILINE)
+ if match is None:
+ return None
+ try:
+ return float(match.group(1))
+ except (ValueError, IndexError):
+ return None
+
+
+def load_numeric_block(data_path: str | Path) -> np.ndarray:
+ """
+ Load a numeric block from an ASCII file, skipping header lines.
+
+ Read the file and try ``numpy.loadtxt`` starting from the first
+ line, then the second, etc., until the load succeeds. This allows
+ files with an arbitrary number of non-numeric header lines to be
+ parsed without prior knowledge of the format.
+
+ Parameters
+ ----------
+ data_path : str | Path
+ Path to the ASCII data file.
+
+ Returns
+ -------
+ np.ndarray
+ 2-D array of the parsed numeric data.
+
+ Raises
+ ------
+ IOError
+ If no contiguous numeric block can be found in the file.
+ """
+ data_path = Path(data_path)
+ lines = data_path.read_text().splitlines()
+
+ last_error: Exception | None = None
+ for start in range(len(lines)):
+ try:
+ return np.loadtxt(StringIO('\n'.join(lines[start:])))
+ except Exception as e: # noqa: BLE001
+ last_error = e
+
+ raise IOError(
+ f'Failed to read numeric data from {data_path}: {last_error}',
+ ) from last_error
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..0cd3b62b 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."""
@@ -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.
@@ -16,7 +17,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/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 877f6ab1..f885aed7 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
@@ -19,15 +19,19 @@
from easydiffraction.core.category import CategoryCollection
from easydiffraction.core.category import CategoryItem
- from easydiffraction.core.parameters import GenericDescriptorBase
+ from easydiffraction.core.variable import GenericDescriptorBase
-def format_value(value) -> str:
- """Format a single CIF value, quoting strings with whitespace, and
- format floats with global precision.
+def format_value(value: object) -> str:
"""
- width = 8
- precision = 4
+ 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
+ relative) survive the float→string→float round-trip through CIF.
+ """
+ width = 12
+ precision = 8
# Converting
@@ -56,8 +60,9 @@ def format_value(value) -> str:
##################
-def param_to_cif(param) -> str:
- """Render a single descriptor/parameter to a CIF line.
+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``.
"""
@@ -66,8 +71,9 @@ def param_to_cif(param) -> str:
return f'{main_key} {format_value(param.value)}'
-def category_item_to_cif(item) -> str:
- """Render a CategoryItem-like object to CIF text.
+def category_item_to_cif(item: object) -> str:
+ """
+ Render a CategoryItem-like object to CIF text.
Expects ``item.parameters`` iterable of params with
``_cif_handler.names`` and ``value``.
@@ -79,10 +85,11 @@ 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.
+ """
+ Render a CategoryCollection-like object to CIF text.
Uses first item to build loop header, then emits rows for each item.
"""
@@ -120,8 +127,9 @@ def category_collection_to_cif(
return '\n'.join(lines)
-def datablock_item_to_cif(datablock) -> str:
- """Render a DatablockItem-like object to CIF text.
+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.
"""
@@ -145,15 +153,13 @@ 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:
- """Render ProjectInfo to CIF text (id, title, description,
- dates).
- """
+def project_info_to_cif(info: object) -> str:
+ """Render ProjectInfo to CIF text (id, title, description)."""
name = f'{info.name}'
title = f'{info.title}'
@@ -179,13 +185,13 @@ 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'):
parts.append(project.info.as_cif)
- if getattr(project, 'sample_models', None):
- parts.append(project.sample_models.as_cif)
+ if getattr(project, 'structures', None):
+ parts.append(project.structures.as_cif)
if getattr(project, 'experiments', None):
parts.append(project.experiments.as_cif)
if getattr(project, 'analysis', None):
@@ -195,26 +201,29 @@ 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] = []
- lines.append(f'_analysis.calculator_engine {format_value(analysis.current_calculator)}')
lines.append(f'_analysis.fitting_engine {cur_min}')
- lines.append(f'_analysis.fit_mode {format_value(analysis.fit_mode)}')
+ lines.append(analysis.fit_mode.as_cif)
lines.append('')
lines.append(analysis.aliases.as_cif)
lines.append('')
lines.append(analysis.constraints.as_cif)
+ jfe_cif = analysis.joint_fit_experiments.as_cif
+ if jfe_cif:
+ lines.append('')
+ lines.append(jfe_cif)
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...'
@@ -231,6 +240,18 @@ 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
@@ -282,6 +303,21 @@ 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?
@@ -294,7 +330,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/__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 980f680b..61b2cd5d 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."""
@@ -10,22 +10,23 @@
from easydiffraction.analysis.analysis import Analysis
from easydiffraction.core.guard import GuardedBase
+from easydiffraction.datablocks.experiment.collection import Experiments
+from easydiffraction.datablocks.structure.collection import Structures
from easydiffraction.display.plotting import Plotter
from easydiffraction.display.tables import TableRenderer
-from easydiffraction.experiments.experiments import Experiments
from easydiffraction.io.cif.serialize import project_to_cif
from easydiffraction.project.project_info import ProjectInfo
-from easydiffraction.sample_models.sample_models import SampleModels
from easydiffraction.summary.summary import Summary
+from easydiffraction.utils.enums import VerbosityEnum
from easydiffraction.utils.logging import console
from easydiffraction.utils.logging import log
class Project(GuardedBase):
- """Central API for managing a diffraction data analysis project.
+ """
+ Central API for managing a diffraction data analysis project.
- Provides access to sample models, experiments, analysis, and
- summary.
+ Provides access to structures, experiments, analysis, and summary.
"""
# ------------------------------------------------------------------
@@ -40,7 +41,7 @@ def __init__(
super().__init__()
self._info: ProjectInfo = ProjectInfo(name, title, description)
- self._sample_models = SampleModels()
+ self._structures = Structures()
self._experiments = Experiments()
self._tabler = TableRenderer.get()
self._plotter = Plotter()
@@ -48,6 +49,7 @@ def __init__(
self._summary = Summary(self)
self._saved = False
self._varname = varname()
+ self._verbosity: VerbosityEnum = VerbosityEnum.FULL
# ------------------------------------------------------------------
# Dunder methods
@@ -56,11 +58,11 @@ def __str__(self) -> str:
"""Human-readable representation."""
class_name = self.__class__.__name__
project_name = self.name
- sample_models_count = len(self.sample_models)
+ structures_count = len(self.structures)
experiments_count = len(self.experiments)
return (
f"{class_name} '{project_name}' "
- f'({sample_models_count} sample models, '
+ f'({structures_count} structures, '
f'{experiments_count} experiments)'
)
@@ -75,86 +77,113 @@ 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
def full_name(self) -> str:
+ """
+ Return the full project name (alias for :attr:`name`).
+
+ Returns
+ -------
+ str
+ The project name.
+ """
return self.name
@property
- def sample_models(self) -> SampleModels:
- """Collection of sample models in the project."""
- return self._sample_models
+ def structures(self) -> Structures:
+ """Collection of structures in the project."""
+ return self._structures
- @sample_models.setter
+ @structures.setter
@typechecked
- def sample_models(self, sample_models: SampleModels) -> None:
- self._sample_models = sample_models
+ 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):
- """Return parameters from all components (TBD)."""
- # To be implemented: return all parameters in the project
- return []
+ 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)
+ @property
+ def verbosity(self) -> str:
+ """
+ Project-wide console output verbosity.
+
+ Returns
+ -------
+ str
+ One of ``'full'``, ``'short'``, or ``'silent'``.
+ """
+ return self._verbosity.value
+
+ @verbosity.setter
+ def verbosity(self, value: str) -> None:
+ """
+ Set project-wide console output verbosity.
+
+ Parameters
+ ----------
+ value : str
+ ``'full'`` for multi-line output, ``'short'`` for one-line
+ status messages, or ``'silent'`` for no output.
+ """
+ self._verbosity = VerbosityEnum(value)
+
# ------------------------------------------
# Project File I/O
# ------------------------------------------
def load(self, dir_path: str) -> None:
- """Load a project from a given directory.
+ """
+ Load a project from a given directory.
- Loads project info, sample models, experiments, etc.
+ Loads project info, structures, experiments, etc.
"""
- console.paragraph('Loading project 📦 from')
- console.print(dir_path)
- self._info.path = dir_path
# TODO: load project components from files inside dir_path
- console.print('Loading project is not implemented yet.')
- self._saved = True
+ raise NotImplementedError('Project.load() is not implemented yet.')
def save(self) -> None:
"""Save the project into the existing project directory."""
- if not self._info.path:
+ if self._info.path is None:
log.error('Project path not specified. Use save_as() to define the path first.')
return
@@ -169,26 +198,24 @@ def save(self) -> None:
f.write(self._info.as_cif())
console.print('├── 📄 project.cif')
- # Save sample models
- sm_dir = self._info.path / 'sample_models'
+ # Save structures
+ sm_dir = self._info.path / 'structures'
sm_dir.mkdir(parents=True, exist_ok=True)
- # Iterate over sample model objects (MutableMapping iter gives
- # keys)
- for model in self.sample_models.values():
- file_name: str = f'{model.name}.cif'
+ console.print('├── 📁 structures/')
+ for structure in self.structures.values():
+ file_name: str = f'{structure.name}.cif'
file_path = sm_dir / file_name
- console.print('├── 📁 sample_models')
with file_path.open('w') as f:
- f.write(model.as_cif)
+ f.write(structure.as_cif)
console.print(f'│ └── 📄 {file_name}')
# Save experiments
expt_dir = self._info.path / 'experiments'
expt_dir.mkdir(parents=True, exist_ok=True)
+ console.print('├── 📁 experiments/')
for experiment in self.experiments.values():
file_name: str = f'{experiment.name}.cif'
file_path = expt_dir / file_name
- console.print('├── 📁 experiments')
with file_path.open('w') as f:
f.write(experiment.as_cif)
console.print(f'│ └── 📄 {file_name}')
@@ -222,20 +249,34 @@ def save_as(
# Plotting
# ------------------------------------------
- def _update_categories(self, expt_name) -> None:
- for sample_model in self.sample_models:
- sample_model._update_categories()
+ def _update_categories(self, expt_name: str) -> None:
+ for structure in self.structures:
+ structure._update_categories()
self.analysis._update_categories()
experiment = self.experiments[expt_name]
experiment._update_categories()
def plot_meas(
self,
- expt_name,
- x_min=None,
- x_max=None,
- d_spacing=False,
- ):
+ expt_name: str,
+ x_min: float | None = None,
+ 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]
@@ -245,16 +286,30 @@ def plot_meas(
experiment.type,
x_min=x_min,
x_max=x_max,
- d_spacing=d_spacing,
+ x=x,
)
def plot_calc(
self,
- expt_name,
- x_min=None,
- x_max=None,
- d_spacing=False,
- ):
+ expt_name: str,
+ x_min: float | None = None,
+ 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]
@@ -264,17 +319,33 @@ def plot_calc(
experiment.type,
x_min=x_min,
x_max=x_max,
- d_spacing=d_spacing,
+ x=x,
)
def plot_meas_vs_calc(
self,
- expt_name,
- x_min=None,
- x_max=None,
- show_residual=False,
- d_spacing=False,
- ):
+ expt_name: str,
+ x_min: float | None = None,
+ x_max: float | None = None,
+ 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]
@@ -285,5 +356,29 @@ def plot_meas_vs_calc(
x_min=x_min,
x_max=x_max,
show_residual=show_residual,
- d_spacing=d_spacing,
+ x=x,
+ )
+
+ def plot_param_series(self, param: object, versus: object | None = None) -> None:
+ """
+ Plot a parameter's value across sequential fit results.
+
+ Parameters
+ ----------
+ param : object
+ Parameter descriptor whose ``unique_name`` identifies the
+ values to plot.
+ versus : object | None, default=None
+ A diffrn descriptor (e.g.
+ ``expt.diffrn.ambient_temperature``) whose value is used as
+ the x-axis for each experiment. When ``None``, the
+ experiment sequence number is used instead.
+ """
+ unique_name = param.unique_name
+ versus_name = versus.name if versus is not None else None
+ self.plotter.plot_param_series(
+ unique_name,
+ versus_name,
+ self.experiments,
+ self.analysis._parameter_snapshots,
)
diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_info.py
index 998ba200..dcba2fba 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."""
@@ -12,9 +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,
@@ -27,7 +25,7 @@ def __init__(
self._name = name
self._title = title
self._description = description
- self._path: pathlib.Path = pathlib.Path.cwd()
+ self._path: pathlib.Path | None = None # pathlib.Path.cwd()
self._created: datetime.datetime = datetime.datetime.now()
self._last_modified: datetime.datetime = datetime.datetime.now()
@@ -38,6 +36,14 @@ 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
@@ -52,6 +58,14 @@ 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
@@ -61,15 +75,31 @@ 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
- def path(self) -> pathlib.Path:
+ def path(self) -> pathlib.Path | None:
"""Return the project path as a Path object."""
return self._path
@path.setter
- def path(self, value) -> None:
+ 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)
@@ -87,8 +117,8 @@ def update_last_modified(self) -> None:
"""Update the last modified timestamp."""
self._last_modified = datetime.datetime.now()
- def parameters(self):
- """Placeholder for parameter listing."""
+ def parameters(self) -> None:
+ """List parameters (not implemented)."""
pass
# TODO: Consider moving to io.cif.serialize
diff --git a/src/easydiffraction/sample_models/__init__.py b/src/easydiffraction/sample_models/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/sample_models/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/sample_models/categories/__init__.py b/src/easydiffraction/sample_models/categories/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/sample_models/categories/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/sample_models/categories/atom_sites.py b/src/easydiffraction/sample_models/categories/atom_sites.py
deleted file mode 100644
index 955c62df..00000000
--- a/src/easydiffraction/sample_models/categories/atom_sites.py
+++ /dev/null
@@ -1,334 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Atom site category.
-
-Defines AtomSite items and AtomSites collection used in sample models.
-Only documentation was added; behavior remains unchanged.
-"""
-
-from cryspy.A_functions_base.database import DATABASE
-
-from easydiffraction.core.category import CategoryCollection
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import MembershipValidator
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.core.validation import RegexValidator
-from easydiffraction.crystallography import crystallography as ecr
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class AtomSite(CategoryItem):
- """Single atom site with fractional coordinates and ADP.
-
- Attributes are represented by descriptors to support validation and
- CIF serialization.
- """
-
- def __init__(
- self,
- *,
- label=None,
- type_symbol=None,
- fract_x=None,
- fract_y=None,
- fract_z=None,
- wyckoff_letter=None,
- occupancy=None,
- b_iso=None,
- adp_type=None,
- ) -> None:
- super().__init__()
-
- self._label: StringDescriptor = StringDescriptor(
- name='label',
- description='Unique identifier for the atom site.',
- value_spec=AttributeSpec(
- value=label,
- type_=DataTypes.STRING,
- default='Si',
- # TODO: the following pattern is valid for dict key
- # (keywords are not checked). CIF label is less strict.
- # Do we need conversion between CIF and internal label?
- content_validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.label',
- ]
- ),
- )
- self._type_symbol: StringDescriptor = StringDescriptor(
- name='type_symbol',
- description='Chemical symbol of the atom at this site.',
- value_spec=AttributeSpec(
- value=type_symbol,
- type_=DataTypes.STRING,
- default='Tb',
- content_validator=MembershipValidator(allowed=self._type_symbol_allowed_values),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.type_symbol',
- ]
- ),
- )
- self._fract_x: Parameter = Parameter(
- name='fract_x',
- description='Fractional x-coordinate of the atom site within the unit cell.',
- value_spec=AttributeSpec(
- value=fract_x,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.fract_x',
- ]
- ),
- )
- self._fract_y: Parameter = Parameter(
- name='fract_y',
- description='Fractional y-coordinate of the atom site within the unit cell.',
- value_spec=AttributeSpec(
- value=fract_y,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.fract_y',
- ]
- ),
- )
- self._fract_z: Parameter = Parameter(
- name='fract_z',
- description='Fractional z-coordinate of the atom site within the unit cell.',
- value_spec=AttributeSpec(
- value=fract_z,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.fract_z',
- ]
- ),
- )
- self._wyckoff_letter: StringDescriptor = StringDescriptor(
- name='wyckoff_letter',
- description='Wyckoff letter indicating the symmetry of the '
- 'atom site within the space group.',
- value_spec=AttributeSpec(
- value=wyckoff_letter,
- type_=DataTypes.STRING,
- default=self._wyckoff_letter_default_value,
- content_validator=MembershipValidator(allowed=self._wyckoff_letter_allowed_values),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.Wyckoff_letter',
- '_atom_site.Wyckoff_symbol',
- ]
- ),
- )
- self._occupancy: Parameter = Parameter(
- name='occupancy',
- description='Occupancy of the atom site, representing the '
- 'fraction of the site occupied by the atom type.',
- value_spec=AttributeSpec(
- value=occupancy,
- type_=DataTypes.NUMERIC,
- default=1.0,
- content_validator=RangeValidator(),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.occupancy',
- ]
- ),
- )
- self._b_iso: Parameter = Parameter(
- name='b_iso',
- description='Isotropic atomic displacement parameter (ADP) for the atom site.',
- value_spec=AttributeSpec(
- value=b_iso,
- type_=DataTypes.NUMERIC,
- default=0.0,
- content_validator=RangeValidator(ge=0.0),
- ),
- units='Ų',
- cif_handler=CifHandler(
- names=[
- '_atom_site.B_iso_or_equiv',
- ]
- ),
- )
- self._adp_type: StringDescriptor = StringDescriptor(
- name='adp_type',
- description='Type of atomic displacement parameter (ADP) '
- 'used (e.g., Biso, Uiso, Uani, Bani).',
- value_spec=AttributeSpec(
- value=adp_type,
- type_=DataTypes.STRING,
- default='Biso',
- content_validator=MembershipValidator(allowed=['Biso']),
- ),
- cif_handler=CifHandler(
- names=[
- '_atom_site.adp_type',
- ]
- ),
- )
-
- self._identity.category_code = 'atom_site'
- self._identity.category_entry_name = lambda: str(self.label.value)
-
- @property
- def _type_symbol_allowed_values(self):
- return list({key[1] for key in DATABASE['Isotopes']})
-
- @property
- def _wyckoff_letter_allowed_values(self):
- # TODO: Need to now current space group. How to access it? Via
- # parent Cell? Then letters =
- # list(SPACE_GROUPS[62, 'cab']['Wyckoff_positions'].keys())
- # Temporarily return hardcoded list:
- return ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
-
- @property
- def _wyckoff_letter_default_value(self):
- # TODO: What to pass as default?
- return self._wyckoff_letter_allowed_values[0]
-
- @property
- def label(self):
- """Label descriptor for the site (unique key)."""
- return self._label
-
- @label.setter
- def label(self, value):
- self._label.value = value
-
- @property
- def type_symbol(self):
- """Chemical symbol descriptor (e.g. 'Si')."""
- return self._type_symbol
-
- @type_symbol.setter
- def type_symbol(self, value):
- self._type_symbol.value = value
-
- @property
- def adp_type(self):
- """ADP type descriptor (e.g. 'Biso')."""
- return self._adp_type
-
- @adp_type.setter
- def adp_type(self, value):
- self._adp_type.value = value
-
- @property
- def wyckoff_letter(self):
- """Wyckoff letter descriptor (space-group position)."""
- return self._wyckoff_letter
-
- @wyckoff_letter.setter
- def wyckoff_letter(self, value):
- self._wyckoff_letter.value = value
-
- @property
- def fract_x(self):
- """Fractional x coordinate descriptor."""
- return self._fract_x
-
- @fract_x.setter
- def fract_x(self, value):
- self._fract_x.value = value
-
- @property
- def fract_y(self):
- """Fractional y coordinate descriptor."""
- return self._fract_y
-
- @fract_y.setter
- def fract_y(self, value):
- self._fract_y.value = value
-
- @property
- def fract_z(self):
- """Fractional z coordinate descriptor."""
- return self._fract_z
-
- @fract_z.setter
- def fract_z(self, value):
- self._fract_z.value = value
-
- @property
- def occupancy(self):
- """Occupancy descriptor (0..1)."""
- return self._occupancy
-
- @occupancy.setter
- def occupancy(self, value):
- self._occupancy.value = value
-
- @property
- def b_iso(self):
- """Isotropic ADP descriptor in Ų."""
- return self._b_iso
-
- @b_iso.setter
- def b_iso(self, value):
- self._b_iso.value = value
-
-
-class AtomSites(CategoryCollection):
- """Collection of AtomSite instances."""
-
- def __init__(self):
- super().__init__(item_type=AtomSite)
-
- def _apply_atomic_coordinates_symmetry_constraints(self):
- """Apply symmetry rules to fractional coordinates of atom
- sites.
- """
- sample_model = self._parent
- space_group_name = sample_model.space_group.name_h_m.value
- space_group_coord_code = sample_model.space_group.it_coordinate_system_code.value
- for atom in self._items:
- dummy_atom = {
- 'fract_x': atom.fract_x.value,
- 'fract_y': atom.fract_y.value,
- 'fract_z': atom.fract_z.value,
- }
- wl = atom.wyckoff_letter.value
- if not wl:
- # TODO: Decide how to handle this case
- # For now, we just skip applying constraints if wyckoff
- # letter is not set. Alternatively, could raise an
- # error or warning
- # print(f"Warning: Wyckoff letter is not ...")
- # raise ValueError("Wyckoff letter is not ...")
- continue
- ecr.apply_atom_site_symmetry_constraints(
- atom_site=dummy_atom,
- name_hm=space_group_name,
- coord_code=space_group_coord_code,
- wyckoff_letter=wl,
- )
- atom.fract_x.value = dummy_atom['fract_x']
- atom.fract_y.value = dummy_atom['fract_y']
- atom.fract_z.value = dummy_atom['fract_z']
-
- def _update(self, called_by_minimizer=False):
- """Update atom sites by applying symmetry constraints."""
- del called_by_minimizer
-
- self._apply_atomic_coordinates_symmetry_constraints()
diff --git a/src/easydiffraction/sample_models/categories/cell.py b/src/easydiffraction/sample_models/categories/cell.py
deleted file mode 100644
index 80c0b144..00000000
--- a/src/easydiffraction/sample_models/categories/cell.py
+++ /dev/null
@@ -1,188 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Unit cell parameters category for sample models."""
-
-from typing import Optional
-
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import Parameter
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import RangeValidator
-from easydiffraction.crystallography import crystallography as ecr
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class Cell(CategoryItem):
- """Unit cell with lengths a, b, c and angles alpha, beta, gamma."""
-
- def __init__(
- self,
- *,
- length_a: Optional[int | float] = None,
- length_b: Optional[int | float] = None,
- length_c: Optional[int | float] = None,
- angle_alpha: Optional[int | float] = None,
- angle_beta: Optional[int | float] = None,
- angle_gamma: Optional[int | float] = None,
- ) -> None:
- super().__init__()
-
- self._length_a: Parameter = Parameter(
- name='length_a',
- description='Length of the a axis of the unit cell.',
- value_spec=AttributeSpec(
- value=length_a,
- type_=DataTypes.NUMERIC,
- default=10.0,
- content_validator=RangeValidator(ge=0, le=1000),
- ),
- units='Å',
- cif_handler=CifHandler(names=['_cell.length_a']),
- )
- self._length_b: Parameter = Parameter(
- name='length_b',
- description='Length of the b axis of the unit cell.',
- value_spec=AttributeSpec(
- value=length_b,
- type_=DataTypes.NUMERIC,
- default=10.0,
- content_validator=RangeValidator(ge=0, le=1000),
- ),
- units='Å',
- cif_handler=CifHandler(names=['_cell.length_b']),
- )
- self._length_c: Parameter = Parameter(
- name='length_c',
- description='Length of the c axis of the unit cell.',
- value_spec=AttributeSpec(
- value=length_c,
- type_=DataTypes.NUMERIC,
- default=10.0,
- content_validator=RangeValidator(ge=0, le=1000),
- ),
- units='Å',
- cif_handler=CifHandler(names=['_cell.length_c']),
- )
- self._angle_alpha: Parameter = Parameter(
- name='angle_alpha',
- description='Angle between edges b and c.',
- value_spec=AttributeSpec(
- value=angle_alpha,
- type_=DataTypes.NUMERIC,
- default=90.0,
- content_validator=RangeValidator(ge=0, le=180),
- ),
- units='deg',
- cif_handler=CifHandler(names=['_cell.angle_alpha']),
- )
- self._angle_beta: Parameter = Parameter(
- name='angle_beta',
- description='Angle between edges a and c.',
- value_spec=AttributeSpec(
- value=angle_beta,
- type_=DataTypes.NUMERIC,
- default=90.0,
- content_validator=RangeValidator(ge=0, le=180),
- ),
- units='deg',
- cif_handler=CifHandler(names=['_cell.angle_beta']),
- )
- self._angle_gamma: Parameter = Parameter(
- name='angle_gamma',
- description='Angle between edges a and b.',
- value_spec=AttributeSpec(
- value=angle_gamma,
- type_=DataTypes.NUMERIC,
- default=90.0,
- content_validator=RangeValidator(ge=0, le=180),
- ),
- units='deg',
- cif_handler=CifHandler(names=['_cell.angle_gamma']),
- )
-
- self._identity.category_code = 'cell'
-
- @property
- def length_a(self):
- """Descriptor for a-axis length in Å."""
- return self._length_a
-
- @length_a.setter
- def length_a(self, value):
- self._length_a.value = value
-
- @property
- def length_b(self):
- """Descriptor for b-axis length in Å."""
- return self._length_b
-
- @length_b.setter
- def length_b(self, value):
- self._length_b.value = value
-
- @property
- def length_c(self):
- """Descriptor for c-axis length in Å."""
- return self._length_c
-
- @length_c.setter
- def length_c(self, value):
- self._length_c.value = value
-
- @property
- def angle_alpha(self):
- """Descriptor for angle alpha in degrees."""
- return self._angle_alpha
-
- @angle_alpha.setter
- def angle_alpha(self, value):
- self._angle_alpha.value = value
-
- @property
- def angle_beta(self):
- """Descriptor for angle beta in degrees."""
- return self._angle_beta
-
- @angle_beta.setter
- def angle_beta(self, value):
- self._angle_beta.value = value
-
- @property
- def angle_gamma(self):
- """Descriptor for angle gamma in degrees."""
- return self._angle_gamma
-
- @angle_gamma.setter
- def angle_gamma(self, value):
- self._angle_gamma.value = value
-
- def _apply_cell_symmetry_constraints(self):
- """Apply symmetry constraints to cell parameters."""
- dummy_cell = {
- 'lattice_a': self.length_a.value,
- 'lattice_b': self.length_b.value,
- 'lattice_c': self.length_c.value,
- 'angle_alpha': self.angle_alpha.value,
- 'angle_beta': self.angle_beta.value,
- 'angle_gamma': self.angle_gamma.value,
- }
- space_group_name = self._parent.space_group.name_h_m.value
-
- ecr.apply_cell_symmetry_constraints(
- cell=dummy_cell,
- name_hm=space_group_name,
- )
-
- self.length_a.value = dummy_cell['lattice_a']
- self.length_b.value = dummy_cell['lattice_b']
- self.length_c.value = dummy_cell['lattice_c']
- self.angle_alpha.value = dummy_cell['angle_alpha']
- self.angle_beta.value = dummy_cell['angle_beta']
- self.angle_gamma.value = dummy_cell['angle_gamma']
-
- def _update(self, called_by_minimizer=False):
- """Update cell parameters by applying symmetry constraints."""
- del called_by_minimizer
-
- self._apply_cell_symmetry_constraints()
diff --git a/src/easydiffraction/sample_models/categories/space_group.py b/src/easydiffraction/sample_models/categories/space_group.py
deleted file mode 100644
index 3e726b17..00000000
--- a/src/easydiffraction/sample_models/categories/space_group.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-"""Space group category for crystallographic sample models."""
-
-from cryspy.A_functions_base.function_2_space_group import ACCESIBLE_NAME_HM_SHORT
-from cryspy.A_functions_base.function_2_space_group import (
- get_it_coordinate_system_codes_by_it_number,
-)
-from cryspy.A_functions_base.function_2_space_group import get_it_number_by_name_hm_short
-
-from easydiffraction.core.category import CategoryItem
-from easydiffraction.core.parameters import StringDescriptor
-from easydiffraction.core.validation import AttributeSpec
-from easydiffraction.core.validation import DataTypes
-from easydiffraction.core.validation import MembershipValidator
-from easydiffraction.io.cif.handler import CifHandler
-
-
-class SpaceGroup(CategoryItem):
- """Space group with Hermann–Mauguin symbol and IT code."""
-
- def __init__(
- self,
- *,
- name_h_m: str = None,
- it_coordinate_system_code: str = None,
- ) -> None:
- super().__init__()
- self._name_h_m: StringDescriptor = StringDescriptor(
- name='name_h_m',
- description='Hermann-Mauguin symbol of the space group.',
- value_spec=AttributeSpec(
- value=name_h_m,
- type_=DataTypes.STRING,
- default='P 1',
- content_validator=MembershipValidator(
- allowed=lambda: self._name_h_m_allowed_values
- ),
- ),
- cif_handler=CifHandler(
- names=[
- '_space_group.name_H-M_alt',
- '_space_group_name_H-M_alt',
- '_symmetry.space_group_name_H-M',
- '_symmetry_space_group_name_H-M',
- ]
- ),
- )
- self._it_coordinate_system_code: StringDescriptor = StringDescriptor(
- name='it_coordinate_system_code',
- description='A qualifier identifying which setting in IT is used.',
- value_spec=AttributeSpec(
- value=it_coordinate_system_code,
- type_=DataTypes.STRING,
- default=lambda: self._it_coordinate_system_code_default_value,
- content_validator=MembershipValidator(
- allowed=lambda: self._it_coordinate_system_code_allowed_values
- ),
- ),
- cif_handler=CifHandler(
- names=[
- '_space_group.IT_coordinate_system_code',
- '_space_group_IT_coordinate_system_code',
- '_symmetry.IT_coordinate_system_code',
- '_symmetry_IT_coordinate_system_code',
- ]
- ),
- )
- self._identity.category_code = 'space_group'
-
- def _reset_it_coordinate_system_code(self):
- self._it_coordinate_system_code.value = self._it_coordinate_system_code_default_value
-
- @property
- def _name_h_m_allowed_values(self):
- return ACCESIBLE_NAME_HM_SHORT
-
- @property
- def _it_coordinate_system_code_allowed_values(self):
- name = self.name_h_m.value
- it_number = get_it_number_by_name_hm_short(name)
- codes = get_it_coordinate_system_codes_by_it_number(it_number)
- codes = [str(code) for code in codes]
- return codes if codes else ['']
-
- @property
- def _it_coordinate_system_code_default_value(self):
- return self._it_coordinate_system_code_allowed_values[0]
-
- @property
- def name_h_m(self):
- """Descriptor for Hermann–Mauguin symbol."""
- return self._name_h_m
-
- @name_h_m.setter
- def name_h_m(self, value):
- self._name_h_m.value = value
- self._reset_it_coordinate_system_code()
-
- @property
- def it_coordinate_system_code(self):
- """Descriptor for IT coordinate system code."""
- return self._it_coordinate_system_code
-
- @it_coordinate_system_code.setter
- def it_coordinate_system_code(self, value):
- self._it_coordinate_system_code.value = value
diff --git a/src/easydiffraction/sample_models/sample_model/__init__.py b/src/easydiffraction/sample_models/sample_model/__init__.py
deleted file mode 100644
index 429f2648..00000000
--- a/src/easydiffraction/sample_models/sample_model/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
diff --git a/src/easydiffraction/sample_models/sample_model/base.py b/src/easydiffraction/sample_models/sample_model/base.py
deleted file mode 100644
index 7ae135de..00000000
--- a/src/easydiffraction/sample_models/sample_model/base.py
+++ /dev/null
@@ -1,183 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors
-# SPDX-License-Identifier: BSD-3-Clause
-
-from easydiffraction.core.datablock import DatablockItem
-from easydiffraction.crystallography import crystallography as ecr
-from easydiffraction.sample_models.categories.atom_sites import AtomSites
-from easydiffraction.sample_models.categories.cell import Cell
-from easydiffraction.sample_models.categories.space_group import SpaceGroup
-from easydiffraction.utils.logging import console
-from easydiffraction.utils.utils import render_cif
-
-
-class SampleModelBase(DatablockItem):
- """Base sample model and container for structural information.
-
- Holds space group, unit cell and atom-site categories. The
- factory is responsible for creating rich instances from CIF;
- this base accepts just the ``name`` and exposes helpers for
- applying symmetry.
- """
-
- def __init__(
- self,
- *,
- name,
- ) -> None:
- super().__init__()
- self._name = name
- self._cell: Cell = Cell()
- self._space_group: SpaceGroup = SpaceGroup()
- self._atom_sites: AtomSites = AtomSites()
- self._identity.datablock_entry_name = lambda: self.name
-
- def __str__(self) -> str:
- """Human-readable representation of this component."""
- name = self._log_name
- items = ', '.join(
- f'{k}={v}'
- for k, v in {
- 'cell': self.cell,
- 'space_group': self.space_group,
- 'atom_sites': self.atom_sites,
- }.items()
- )
- return f'<{name} ({items})>'
-
- @property
- def name(self) -> str:
- """Model name.
-
- Returns:
- The user-facing identifier for this model.
- """
- return self._name
-
- @name.setter
- def name(self, new: str) -> None:
- """Update model name."""
- self._name = new
-
- @property
- def cell(self) -> Cell:
- """Unit-cell category object."""
- return self._cell
-
- @cell.setter
- def cell(self, new: Cell) -> None:
- """Replace the unit-cell category object."""
- self._cell = new
-
- @property
- def space_group(self) -> SpaceGroup:
- """Space-group category object."""
- return self._space_group
-
- @space_group.setter
- def space_group(self, new: SpaceGroup) -> None:
- """Replace the space-group category object."""
- self._space_group = new
-
- @property
- def atom_sites(self) -> AtomSites:
- """Atom-sites collection for this model."""
- return self._atom_sites
-
- @atom_sites.setter
- def atom_sites(self, new: AtomSites) -> None:
- """Replace the atom-sites collection."""
- self._atom_sites = new
-
- # --------------------
- # Symmetry constraints
- # --------------------
-
- def _apply_cell_symmetry_constraints(self):
- """Apply symmetry rules to unit-cell parameters in place."""
- dummy_cell = {
- 'lattice_a': self.cell.length_a.value,
- 'lattice_b': self.cell.length_b.value,
- 'lattice_c': self.cell.length_c.value,
- 'angle_alpha': self.cell.angle_alpha.value,
- 'angle_beta': self.cell.angle_beta.value,
- 'angle_gamma': self.cell.angle_gamma.value,
- }
- space_group_name = self.space_group.name_h_m.value
- ecr.apply_cell_symmetry_constraints(cell=dummy_cell, name_hm=space_group_name)
- self.cell.length_a.value = dummy_cell['lattice_a']
- self.cell.length_b.value = dummy_cell['lattice_b']
- self.cell.length_c.value = dummy_cell['lattice_c']
- self.cell.angle_alpha.value = dummy_cell['angle_alpha']
- self.cell.angle_beta.value = dummy_cell['angle_beta']
- self.cell.angle_gamma.value = dummy_cell['angle_gamma']
-
- def _apply_atomic_coordinates_symmetry_constraints(self):
- """Apply symmetry rules to fractional coordinates of atom
- sites.
- """
- space_group_name = self.space_group.name_h_m.value
- space_group_coord_code = self.space_group.it_coordinate_system_code.value
- for atom in self.atom_sites:
- dummy_atom = {
- 'fract_x': atom.fract_x.value,
- 'fract_y': atom.fract_y.value,
- 'fract_z': atom.fract_z.value,
- }
- wl = atom.wyckoff_letter.value
- if not wl:
- # TODO: Decide how to handle this case
- # For now, we just skip applying constraints if wyckoff
- # letter is not set. Alternatively, could raise an
- # error or warning
- # print(f"Warning: Wyckoff letter is not ...")
- # raise ValueError("Wyckoff letter is not ...")
- continue
- ecr.apply_atom_site_symmetry_constraints(
- atom_site=dummy_atom,
- name_hm=space_group_name,
- coord_code=space_group_coord_code,
- wyckoff_letter=wl,
- )
- atom.fract_x.value = dummy_atom['fract_x']
- atom.fract_y.value = dummy_atom['fract_y']
- atom.fract_z.value = dummy_atom['fract_z']
-
- def _apply_atomic_displacement_symmetry_constraints(self):
- """Placeholder for ADP symmetry constraints (not
- implemented).
- """
- pass
-
- def _apply_symmetry_constraints(self):
- """Apply all available symmetry constraints to this model."""
- self._apply_cell_symmetry_constraints()
- self._apply_atomic_coordinates_symmetry_constraints()
- self._apply_atomic_displacement_symmetry_constraints()
-
- # ------------
- # Show methods
- # ------------
-
- def show_structure(self):
- """Show an ASCII projection of the structure on a 2D plane."""
- console.paragraph(f"Sample model 🧩 '{self.name}' structure view")
- console.print('Not implemented yet.')
-
- def show_params(self):
- """Display structural parameters (space group, cell, atom
- sites).
- """
- console.print(f'\nSampleModel ID: {self.name}')
- console.print(f'Space group: {self.space_group.name_h_m}')
- console.print(f'Cell parameters: {self.cell.as_dict}')
- console.print('Atom sites:')
- self.atom_sites.show()
-
- def show_as_cif(self) -> None:
- """Render the CIF text for this model in a terminal-friendly
- view.
- """
- cif_text: str = self.as_cif
- paragraph_title: str = f"Sample model 🧩 '{self.name}' as cif"
- console.paragraph(paragraph_title)
- render_cif(cif_text)
diff --git a/src/easydiffraction/sample_models/sample_model/factory.py b/src/easydiffraction/sample_models/sample_model/factory.py
deleted file mode 100644
index 7a03b0f0..00000000
--- a/src/easydiffraction/sample_models/sample_model/factory.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 EasyDiffraction contributors