diff --git a/docs/dev/adrs/accepted/analysis-cif-fit-state.md b/docs/dev/adrs/accepted/analysis-cif-fit-state.md index 9051c0ff4..7aca93d82 100644 --- a/docs/dev/adrs/accepted/analysis-cif-fit-state.md +++ b/docs/dev/adrs/accepted/analysis-cif-fit-state.md @@ -61,7 +61,7 @@ Persist these common categories for any saved fit projection: `_fit_parameter` stores analysis-owned per-parameter fit controls and pre-fit scalar snapshots: -- `param_unique_name` +- `parameter_unique_name` - `fit_min` - `fit_max` - `start_value` @@ -70,7 +70,7 @@ pre-fit scalar snapshots: When any row has uncertainty-derived bounds, `_fit_parameter` also stores the provenance field: -- `fit_bounds_uncertainty_multiplier` +- `bounds_uncertainty_multiplier` For Bayesian fit projections, `_fit_parameter` also stores per-parameter posterior summaries: diff --git a/docs/dev/adrs/accepted/category-owner-sections.md b/docs/dev/adrs/accepted/category-owner-sections.md index 0091b4c59..aec61b6e6 100644 --- a/docs/dev/adrs/accepted/category-owner-sections.md +++ b/docs/dev/adrs/accepted/category-owner-sections.md @@ -8,6 +8,13 @@ Accepted and implemented. 2026-05-17 +## Amendment + +[`edstar-project-persistence.md`](edstar-project-persistence.md) updates +the default project file format from CIF files to Edi files. This ADR's +ownership split still applies: real structure/experiment datablocks +remain distinct from singleton category-owner sections. + ## Context The library has two different kinds of objects that expose CIF-like diff --git a/docs/dev/adrs/accepted/data-source-pinning.md b/docs/dev/adrs/accepted/data-source-pinning.md new file mode 100644 index 000000000..8a45a2d5b --- /dev/null +++ b/docs/dev/adrs/accepted/data-source-pinning.md @@ -0,0 +1,206 @@ +# ADR: Data Download Source Pinning + +**Status:** Accepted **Date:** 2026-06-14 + +> This ADR follows [`AGENTS.md`](../../../../AGENTS.md). No deliberate +> exception to those instructions is taken. + +## Group + +Documentation. + +> Placed with the documentation/tutorial-infrastructure decisions +> (notebook generation, versioned docs) because the downloaded payload +> is primarily example, tutorial, and project-archive data. The +> mechanism is reused by the public `download_data()` API. + +> Sibling of [`resource-naming.md`](resource-naming.md): this ADR pins +> _which snapshot_ of the data repository to fetch and decides +> replace-in-place under stable identifiers; that ADR decides what those +> identifiers _are_ (the dash-prefixed `-` scheme that +> replaces the integer ids). + +## Context + +EasyDiffraction does not ship its example/tutorial datasets inside the +wheel — they are large (measured patterns, project archives) — so the +library downloads them on demand from the `easyscience/diffraction` +repository via `pooch`. The library therefore has to record **which +snapshot of that repository to fetch**. + +Today `src/easydiffraction/utils/utils.py` records this with **two** +hand-edited constants: + +- `_DATA_INDEX_REF` — a full commit SHA, used to build the raw + `raw.githubusercontent.com/easyscience/diffraction//data/...` + URLs (`_build_data_url`). +- `_DATA_INDEX_HASH` — a `sha256:` checksum of `data/index.json`, passed + to `pooch.retrieve` as `known_hash` (`_fetch_data_index`). + +Each downloadable archive additionally carries its own content checksum +inside `index.json` — stored today in the existing **`hash`** field as a +`sha256:...` string (`record.get('hash')`, passed to `pooch` as +`known_hash`) — so per-archive integrity is already data-driven and is +**not** part of this decision. Throughout this ADR, "the dataset +`sha256`" means the value of that existing `hash` field; this ADR does +**not** rename the data-index schema or introduce a new `sha256` key, so +no data-repository migration follows from it. + +Problems with the current arrangement: + +- **Two coupled values.** Every data change requires editing both the + commit and the index checksum, in source, plus a release. +- **Opaque and in code.** The pin is a bare SHA embedded in logic. +- **Cache staleness coupling.** `_fetch_data_index` caches the index + under the fixed filename `data-index.json`. The checksum is what makes + `pooch` notice the index changed and re-download it; without it, a + changed commit would silently reuse the stale cached index. + +Constraints established for this decision: + +- The data repository is **multi-purpose** (data, project metadata, and + later umbrella site content), so it must not be tagged or + directory-versioned for data reasons alone. +- **No new repositories** (the project already has `diffraction`, + `diffraction-lib`, `diffraction-app`) and **no git submodules**. +- The data is **not small** — it stays a runtime download, not a + packaged or submodule-checked-out payload. +- Which data a build uses should **track the library version** and be + **reproducible** for any released version. +- There must be a way to **test new or updated data during + development**. + +## Decision + +1. **Pin by a single git commit.** The data source is identified by one + value: a full commit SHA of `easyscience/diffraction`. A full SHA is + content-addressed, so it fixes the exact bytes of `index.json` and + every archive at that snapshot, over HTTPS. This is purpose-neutral: + it pins "the repository as it was at this commit" regardless of why + the repository changed. + +2. **Store the commit in a packaged, non-code file.** The commit lives + in `src/easydiffraction/_data_index_ref.txt` (one line: the SHA), + read at runtime via `importlib.resources`. It is edited by hand, like + the current constant, but it is data rather than logic, and because + it sits under the package directory it is included in the wheel and + readable for both source checkouts and installed users. + + `pyproject.toml` is explicitly **not** used to hold this value: + `pyproject.toml` is a build-time file that is not shipped in the + wheel, and custom `[tool.*]` tables are not exposed through + `importlib.metadata`, so an installed package could not read it at + runtime. + +3. **Drop `_DATA_INDEX_HASH`.** With the commit pinning the index bytes + and per-archive checksums verifying downloads, the separate index + checksum is redundant. `pooch.retrieve` for the index uses + `known_hash=None`. + +4. **Derive the cached index filename from the commit.** The index is + cached as `data-index-.json` instead of `data-index.json`. + Changing the commit therefore changes the cache filename, so the new + index is downloaded fresh; old indices remain cached under their own + names (harmless, and useful offline). This replaces the checksum as + the cache-busting mechanism while keeping a single source of truth. + +5. **Update data by replacing files in place.** Datasets keep stable + paths/identifiers (their form — the dash-prefixed `-` + scheme — is fixed by the sibling + [`resource-naming.md`](resource-naming.md)); updating a dataset + overwrites the existing file and refreshes its `sha256` in + `index.json`. Git history records the replacement and the pinned + commit selects the snapshot. No new dataset ids are minted for what + is conceptually the same dataset. + +6. **Version tracking and reproducibility come from the release.** The + committed `_data_index_ref.txt` is part of the library source, so it + is frozen into every release artifact. A released library version + therefore always resolves to the same data snapshot, and the data a + build uses tracks the library version without any cross-repository + tagging. + +7. **Invalidate local payloads by content, including extractions.** + Because datasets are replaced in place under stable identifiers + (Decision 5), the local cache must not return stale bytes after a ref + bump. Both the downloaded archive/file **and** any project ZIP + extraction directory are keyed by that dataset's `sha256` from the + index — the value of the existing `hash` field per the §Context note, + not a renamed schema key (for example the cached filename and the + extraction directory carry a short content hash). A dataset whose + bytes changed therefore resolves to a new local path and is + re-fetched/re-extracted, while unchanged datasets are reused — so + only what actually changed is re-downloaded, and an existing stale + file or extracted directory is never served. Equivalently, an + existing payload may be verified against the current index `sha256` + before reuse; either way the rule covers extraction directories, not + only direct downloads. (The index itself is keyed by the commit per + Decision 4, so it always refreshes on a ref bump.) + +8. **Validate the pinned ref before use.** On read, the contents of + `_data_index_ref.txt` are stripped of surrounding whitespace and must + be a full 40-character hexadecimal commit SHA. Any other value — + empty, a branch name, a short SHA, or path-like text — raises a clear + error before any URL or cache filename is built. This turns the "must + be a full commit SHA" requirement into an enforced contract, so a + malformed edit fails fast instead of silently breaking + reproducibility or the cache-busting invariant. + +## Consequences + +- The data pin is a **single, non-code value** that is edited the same + way as today (paste a commit), minus the checksum. +- Integrity is preserved: full-commit content addressing over HTTPS for + the index, plus per-archive `sha256` for every download. +- Cache invalidation is automatic and tied to the one value: the index + by commit, and each archive/extraction by its `sha256` (Decisions 4 + and 7). A ref bump re-fetches only the datasets whose bytes changed, + and never returns a stale file or extracted project directory. +- Reproducibility is preserved per released version, including repeated + use on a machine that already has older local copies; the data pin is + agnostic to non-data changes in the multi-purpose repository. +- The pinned value **must be a full commit SHA**, not a branch name: the + cache-busting filename trick only works for immutable refs (a moving + branch keeps the same `index.json` content address in its name). This + is enforced at read time (Decision 8), so a bad edit fails fast. +- Retaining content-keyed local copies of superseded datasets uses some + extra disk in the cache; this is accepted (and prunable) in exchange + for never serving stale bytes. +- Dropping the index checksum slightly reduces index-level + defense-in-depth; this is accepted because the commit already fixes + the bytes and transport is HTTPS. + +## Alternatives Considered + +- **Keep both constants in code.** Status quo; rejected for the + two-value coupling, opacity, and cache coupling above. +- **Version the data with git tags** (`data-vN` or per-release tags). + Rejected: the repository is multi-purpose, so data-driven tags pollute + its tag namespace and conflate data with site/metadata changes. +- **Append-only versioned directories** (`data/v3/...`). Rejected: it + conflicts with the preferred replace-in-place workflow and grows the + repository indefinitely. +- **Git submodule of the data repository.** Rejected: no submodules, and + the data is too large to check out or ship with the library. +- **Hold the value in `pyproject.toml`.** Rejected: not present in the + installed wheel and not exposed via `importlib.metadata`, so it is not + runtime-readable for installed users (see Decision 2). +- **Track a moving branch (e.g. `master`) for releases.** Rejected: + releases would retroactively see new data and lose reproducibility; + also defeats the filename cache-busting. + +## Deferred Work + +- **Development data channel and override.** A future change may let + unreleased/dev builds resolve a live channel automatically and/or + honor an `EASYDIFFRACTION_DATA_REF` environment variable to point a + build at a branch or commit for testing data before it is finalized. + This ADR only fixes the released-build pin; the dev workflow today is + to set the commit (or the override, once added) to the data snapshot + under test. +- **Automated bump.** Optionally, release CI could write the current + data commit into `_data_index_ref.txt` so the value is never + hand-edited. Out of scope here; manual editing remains the baseline. +- **Dedicated data home.** If the umbrella repository grows, moving the + data to its own released artifact could fully decouple its lifecycle. + Out of scope: the project does not want additional repositories now. diff --git a/docs/dev/adrs/accepted/display-ux.md b/docs/dev/adrs/accepted/display-ux.md index edd55f3b3..27777a92d 100644 --- a/docs/dev/adrs/accepted/display-ux.md +++ b/docs/dev/adrs/accepted/display-ux.md @@ -73,7 +73,9 @@ project.display.parameters.free() project.display.parameters.fittable() project.display.parameters.all() project.display.parameters.access() -project.display.parameters.cif_uids() +project.display.parameters.uid() +project.display.parameters.edi() +project.display.parameters.cif() project.display.fit.results() project.display.fit.correlations() @@ -88,22 +90,26 @@ project.display.posterior.predictive(expt_name='hrpt') current responsibilities move to clearer homes, while the implementation may keep the existing helpers as internal delegation targets: -| Current method | New home | -| ---------------------------- | -------------------------------------------------------------- | -| `all_params()` | `project.display.parameters.all()` | -| `fittable_params()` | `project.display.parameters.fittable()` | -| `free_params()` | `project.display.parameters.free()` | -| `how_to_access_parameters()` | `project.display.parameters.access()` | -| `parameter_cif_uids()` | `project.display.parameters.cif_uids()` | -| `fit_results()` | `project.display.fit.results()` | -| `constraints()` | `project.analysis.constraints.show()` | -| `as_cif()` | `project.analysis.as_cif` and `project.analysis.show_as_cif()` | - -`project.analysis` and `project.info` follow the same CIF display -pattern as structures and experiments: - -- `as_cif` is a read-only property returning CIF text as a string. -- `show_as_cif()` pretty-prints the CIF text with a header. +| Current method | New home | +| ---------------------------- | --------------------------------------------------------------- | +| `all_params()` | `project.display.parameters.all()` | +| `fittable_params()` | `project.display.parameters.fittable()` | +| `free_params()` | `project.display.parameters.free()` | +| `how_to_access_parameters()` | `project.display.parameters.access()` | +| `parameter_uids()` | `project.display.parameters.uid()` | +| `parameter_edi_tags()` | `project.display.parameters.edi()` | +| `parameter_cif_tags()` | `project.display.parameters.cif()` | +| `fit_results()` | `project.display.fit.results()` | +| `constraints()` | `project.analysis.constraints.show()` | +| `as_cif()` | `project.analysis.as_cif` and `project.analysis.show_as_text()` | + +`project.analysis` and `project.info` follow the same display pattern as +structures and experiments: + +- `as_cif` is a read-only property returning the serialized CIF text as + a string (the block body that is persisted into the project's Edi + files). +- `show_as_text()` pretty-prints that text with a header. ## Pattern Display diff --git a/docs/dev/adrs/accepted/edstar-project-persistence.md b/docs/dev/adrs/accepted/edstar-project-persistence.md new file mode 100644 index 000000000..634f3ef8b --- /dev/null +++ b/docs/dev/adrs/accepted/edstar-project-persistence.md @@ -0,0 +1,1328 @@ +# ADR: Edi Project Persistence + +**Status:** Accepted +**Date:** 2026-06-12 + +## Group + +Persistence. + +## Context + +`AGENTS.md` says CIF maps to `DatablockItem` / `DatablockCollection` and +`CategoryItem` / `CategoryCollection`, and that CIF naming should be +followed unless a better API is clear. The better API case now exists in +several places. + +EasyDiffraction already exposes user-facing names that intentionally do +not mirror official CIF names: + +- `atom_site.adp_iso` instead of `B_iso_or_equiv` or `U_iso_or_equiv` +- `atom_site_aniso.adp_11` instead of `B_11`, `U_11`, or `beta_11` +- `experiment.instrument.setup_wavelength` instead of a radiation + wavelength loop +- `experiment.instrument.calib_d_to_tof_*` instead of a + `_pd_calib_d_to_tof` coefficient loop +- `experiment.preferred_orientation.march_r` instead of the long + `_pd_pref_orient_March_Dollase.r` project tag + +The current accepted ADRs split the save/export surface only partly. +Default project files are still named `*.cif`, while +[`iucr-cif-tag-alignment.md`](iucr-cif-tag-alignment.md) also says the +default save should use IUCr-aligned structure tags in some areas. That +makes the project files look stricter than they really are, and it +pushes unfriendly official names back into the round-trip format used by +scientists and by EasyDiffraction itself. + +Report CIF is different. `project.report.save_cif()` is the intended +external submission/export boundary, and it already has a separate +writer with IUCr-oriented reshaping and extension namespacing. + +## Relationship To Existing ADRs + +If accepted, this ADR amends or supersedes parts of several accepted +ADRs. Acceptance must update those ADRs and +[`docs/dev/adrs/index.md`](../index.md) so the accepted documentation +does not describe conflicting persistence layouts. + +- Supersedes the **default-save naming and file-extension** parts of + [`iucr-cif-tag-alignment.md`](iucr-cif-tag-alignment.md). Report-CIF + export remains governed by that ADR's IUCr-aligned writer policy. +- Amends + [`python-cif-category-correspondence.md`](python-cif-category-correspondence.md) + by replacing the scoped Python-to-`project.cif` correspondence with + Python-to-Edi correspondence across project files. +- Amends + [`project-facade-and-persistence.md`](project-facade-and-persistence.md) + by replacing `project.cif`, `structures/*.cif`, `experiments/*.cif`, + and `analysis/analysis.cif` with the `.edi` project layout. +- Amends [`category-owner-sections.md`](category-owner-sections.md) only + for file-format terminology. The distinction between real data blocks + and singleton category-owner sections remains. +- Carries forward + [`free-flag-cif-encoding.md`](free-flag-cif-encoding.md) for + free/fixed parameter encoding inside STAR values. + +Other accepted ADRs that mention `project.cif`, `analysis/analysis.cif`, +or `structures/*.cif` need follow-up wording updates when this ADR is +accepted, but their domain decisions remain unchanged unless explicitly +listed above. + +## Decision + +Adopt **Edi** as the internal EasyDiffraction project persistence +format: + +- Edi uses STAR syntax and leading-underscore data names. +- Edi is an EasyDiffraction-owned schema, not an IUCr dictionary claim. +- Edi project names optimize for Python/API discoverability, readable + diffs, and safe hand editing. +- IUCr CIF remains a strict import/export boundary format. + +Use **Edi** as the human-facing schema/format name in prose, headings, +UI labels, and documentation tables. Use lowercase only for literal +syntax: `.edi` for the file extension and `_edi.*` for the schema-marker +category/items. Do not use `EDI` unless quoting an external source that +has already standardized that spelling. + +Persist project state using `.edi` files: + +```text +project_dir/ +|-- project.edi +|-- structures/ +| `-- .edi +|-- experiments/ +| `-- .edi +|-- analysis/ +| |-- analysis.edi +| |-- results.csv +| `-- results.h5 +`-- reports/ + `-- .cif +``` + +The report file remains `reports/.cif` and remains strict +IUCr/pdCIF as far as the project can make it. Nonstandard report values +continue to use `_easydiffraction_*` extension categories inside report +CIF. + +Edi governs the `*.edi` files only. Existing non-STAR analysis artifacts +keep their current formats: `analysis/results.h5` remains the binary +fit-result sidecar, and `analysis/results.csv` remains the tabular +sequential-fit output used by plotting and user inspection. + +The existing gemmi-based parser reads STAR/CIF content rather than +relying on the file extension, so no new low-level parser is required. +The implementation work is in save/load path discovery, filename +conventions, tag aliases, validation, CLI help, documentation, and +tutorials. + +### File Extension Alternatives + +The extension decision is separate from the tag-naming decision. + +**Keep `.cif`, but document it as an EasyDiffraction STAR dialect.** +This minimizes migration churn: existing ZIP project detection, docs, +tutorials, tests, and external workflows that look for `project.cif` +keep working. The downside is that the file name continues to imply +strict CIF dictionary compatibility for files that intentionally use +EasyDiffraction-owned names such as `_atom_site.adp_iso` and +`_instrument.setup_wavelength`. + +**Use `.edi` for project persistence.** This is the selected option. It +makes the file type honest: STAR syntax, EasyDiffraction schema. The +cost is a beta layout migration and documentation churn, but it prevents +scientists and external tools from mistaking project state files for +submission/interchange CIFs. + +**Use `.edstar`.** This is rejected. STAR is the syntax layer, while the +saved files are EasyDiffraction application artifacts with an +EasyDiffraction-owned schema. The name over-emphasizes the syntax and +can sound like a new generic STAR dialect. It also keeps the `ed` +prefix, which crystallographers may read as electron diffraction. + +**Use `.easydiffraction`.** This is rejected. It identifies the product +but not the syntax, is long for files scientists may inspect and share, +and would be awkward if EasyDiffraction later owns non-STAR project +artifacts with the same brand name. `Edi` already expands the product +association into the format name: EasyDiffraction-owned STAR. + +**Use `.edcif`.** This advertises EasyDiffraction ownership but still +suggests CIF dictionary semantics. It is therefore less clear than +`.edi`. It is also too easy to read as electron-diffraction CIF, +matching the existing `cif_ed` naming convention in the COMCIFS +electron-diffraction dictionary work. + +**Use `.txt`.** This is rejected. Its one real advantage is that a +desktop double-click opens it in any text editor with no file +association — but that is a GUI-only benefit. In a terminal, notebook, +or CLI workflow (`cat`, `less`, `vim`, `nano`, `code …`) an `.edi` file +opens identically regardless of suffix, so CLI users gain nothing from +`.txt`. Against that, `.txt` loses everything the chosen extension +provides: the project files get **no identity** (a directory of +`project.txt`, `.txt`, `.txt` is +indistinguishable from loose notes or data dumps); they **cannot be +globbed** to locate EasyDiffraction projects (`*.txt` collides with +everything); load-path discovery **weakens** (the loader can no longer +key on a unique suffix and would have to rely on fixed filenames or +content sniffing); and the suffix signals "scratch file, edit freely" +for a format that has selector/body consistency rules and a load-time +validation boundary — the casual hand-editing most likely to corrupt it. +`.txt` is the opposite extreme from `.cif`: where `.cif` over-claims +dictionary semantics, `.txt` claims none at all, so the same honesty +argument that rejects `.cif` also rejects `.txt`. The "I can't open an +unknown extension" concern that motivates `.txt` is instead addressed by +the plain-text guarantee in §Naming Policy, which keeps the files +openable in any editor without sacrificing identity. + +## Naming Policy + +Edi data names should follow the public EasyDiffraction model: + +```text +_. +``` + +Use leading underscores because they are part of STAR/CIF data-name +syntax, not Python privacy markers. + +Use API-oriented field names for project persistence: + +```text +_atom_site.adp_iso +_atom_site.adp_type +_atom_site_aniso.adp_11 +_instrument.setup_wavelength +_instrument.calib_d_to_tof_linear +_preferred_orientation.march_r +``` + +Three rules make these names deterministic across the inventory: + +- **Field names follow the public property, not the descriptor + `Parameter.name`.** Where the two differ — notably the TOF peak + profile, whose descriptors are stored as bare stems (`gauss_sigma_0`, + `lorentz_gamma_0`, `rise_alpha_0`, `decay_beta_0`) while the public + properties carry grouping prefixes (`broad_gauss_sigma_0`, + `broad_lorentz_gamma_0`, `rise_alpha_0`, `decay_beta_0`) — Edi writes + the public-property name. This is the point of the format: a saved + field matches the Python path a scientist types. The current bare CIF + stem is preserved as a read alias. +- **Loop (collection) categories use the singular row-category form of + the public owner attribute**, following the CIF convention that a loop + of many rows is named in the singular (`_atom_site` for many atom + sites). So `structure.atom_sites` → `_atom_site`, `analysis.aliases` → + `_alias`, `experiment.excluded_regions` → `_excluded_region`, + `experiment.linked_phases` → `_linked_structure`, + `experiment.linked_crystal` → `_linked_structure`. This is + intentionally the singular noun, not the internal + `CategoryItem._category_code`, which is plural for some collections + (for example `excluded_regions`, `linked_phases`) and singular for + others (`alias`, `constraint`). The plural collection name stays in + the Python API (`structure.atom_sites`); the file describes the + per-row item, so it is singular — the universal STAR/CIF convention. +- **Row keys use `id` for a row's own local identity and `_id` + for a reference to another datablock.** Every loop category's own + primary key is `id` — including `_atom_site.id` and `_alias.id`, + replacing the CIF-specific `label` so users do not memorize which + categories key on `label`. A column that references a separate + datablock keeps the explicit `_id` form: `_linked_structure`, + `_preferred_orientation`, and `_refln` (powder) reference a structure + datablock via `structure_id`; `_joint_fit` references an experiment + via `experiment_id`. The `_id` suffix always means "points at another + datablock's `id`." Report CIF still emits the official keys + (`_atom_site.label`, `_pd_phase_block.id`), and `label` / `phase_id` + remain read aliases. Tightly-coupled satellite loops reuse the parent + key name (`_atom_site_aniso.id` joins `_atom_site.id`), mirroring how + CIF reuses `_atom_site_aniso.label`. + +### Abbreviation Policy + +Spell every word in full. Abbreviate a word only if its short form is on +the approved allowlist below — a short form qualifies only when **both** +(1) the IUCr CIF dictionaries use it in data names and (2) it is the +form a crystallographer or instrument scientist recognizes on sight. The +same concept uses the same form in every name. Use an initialism (`h_m`, +`it`) only when the initialism is itself the standard term; otherwise +use the recognizable word (`march`, not `m_d`). + +CIF-named concepts inherit CIF's spelling automatically (CIF's +abbreviations are this allowlist's source, and report export must emit +them anyway). EasyDiffraction-owned concepts use full words, drawing +only from the allowlist. + +**Approved abbreviations:** `calc` (calculated), `meas` (measured), +`coef` (coefficient), `su` (standard uncertainty), `iso`/`aniso` +(isotropic/anisotropic), `fract` (fractional), `coord` (coordinate, only +for coordinate-code/template names), `inc` (increment), `min`/`max`, +`prof` (profile), `r`/`wr`/`gt` (R-factor / weighted-R / greater-than), +`h_m` (Hermann–Mauguin), `it` (International Tables), `id`, +`index_h`/`index_k`/`index_l` (Miller indices), `adp` (atomic +displacement parameter), `tof` (time-of-flight), `cwl` (constant +wavelength), `fcj` (Finger–Cox–Jephcoat), `q` (momentum transfer). + +**Always spelled in full** (not on the allowlist): `parameter` (not +`param`), `distance` (not `dist`), `reciprocal` and `quadratic` (not +`recip`/`quad`), `preferred_orientation` (CIF's `pref_orient` +contraction is not sight-recognized), and MCMC terms such as +`effective_sample_size` and `gelman_rubin`. + +Persist selectors for user-visible model choices, and keep value names +generic when they represent the same user concept across selector +values: + +```text +_background.type line_segment + +loop_ +_background.id +_background.position +_background.intensity +1 10.0 120.0 +2 20.0 118.0 +``` + +```text +loop_ +_atom_site.id +_atom_site.adp_type +_atom_site.adp_iso +Si Biso 0.5 +O Uiso 0.0063 +``` + +Do not prefix internal Edi categories with `_easydiffraction_` or +`_edi_`. The `.edi` suffix and schema marker already identify the +dialect. + +Use `_easydiffraction_*` for custom keys serialized into strict report +CIF when a nonstandard extension must coexist with official IUCr tags. +Do not use `_edi_*` in report CIFs. `_edi.*` is reserved for the Edi +schema marker in project files, while report CIF is an IUCr-facing +export with EasyDiffraction extension categories. Keeping the prefixes +separate means report-CIF extensions can remain stable even if the +internal Edi project schema changes. + +Each Edi file should include a schema marker near the top: + +```text +_edi.schema_version 1 +``` + +The `_edi.` namespace already identifies the dialect, so the marker +carries only the version. The v1 loader accepts `1`, rejects newer major +versions with a clear error, and rejects missing markers in `.edi` +project files. The marker is therefore a validation boundary, not +decorative metadata. + +**Plain-text guarantee (openability).** Edi files are plain UTF-8 STAR +text with no binary content, so they open and hand-edit in any text +editor. The `.edi` suffix is an honest _label_, not a barrier: even +where the operating system has no default application registered for it, +a user can always open the file with "Open With → any text editor" (or +`cat`/`less`/`vim`/`nano`/`code` in a terminal). This is the deliberate +answer to the "unknown extension" concern that would otherwise argue for +a generic `.txt` (see §File Extension Alternatives): Edi keeps the +universal openability of plain text while retaining a distinct, +greppable identity. Editors may additionally be mapped to treat `*.edi` +as CIF/STAR for syntax highlighting — something a generic `.txt` cannot +provide per-file-type. + +### Selector Validation Contract + +Selectors are authoritative boundary input. During restore, loaders read +selector fields such as `_background.type`, `_minimizer.type`, and +`_atom_site.adp_type` before loading the value fields controlled by +those selectors. + +When selector and body fields disagree, load rejects the file with a +clear error. It must not silently drop rows, silently switch the +selector, or let one side win. Examples: + +- `_background.type chebyshev` with `_background.position` / + `_background.intensity` line-segment rows is invalid. +- `_background.type line_segment` with Chebyshev-only fields is invalid. +- unknown selector values are invalid. + +Edi v1 does not rename selector values, so it has no selector-value +legacy aliases. The `import_names`/read-alias mechanism covers data-name +aliases only. If a future ADR renames a selector value, that ADR must +also define where the value alias map lives, for example on the +`(str, Enum)` that owns the closed value set or on the category setter +that validates the selector. + +If a selector is absent in a legacy file, the loader may use the current +category default only when no implementation-specific fields are +present. If implementation-specific fields are present and the type +cannot be resolved unambiguously, load rejects with a clear error. + +### Free/Fixed Fit Flags + +Edi keeps the accepted free/fixed parameter encoding from +[`free-flag-cif-encoding.md`](free-flag-cif-encoding.md): + +- fixed or constrained numeric parameters write as plain values; +- independently free parameters write with uncertainty brackets, for + example `3.8909()` or `3.89(20)`; +- user-constrained dependent parameters write without brackets. + +This remains valid because Edi uses STAR value syntax. The schema marker +and renamed data names do not change the value-level round-trip +contract. + +## Compatibility + +Project restore should accept: + +- the new `.edi` project layout; +- official CIF import tags where supported today; +- known EasyDiffraction data-name read aliases in `CifHandler` import + aliases. + +Project restore should not load the previous beta `.cif` project layout. +The project is still in beta, so no project-persistence deprecation shim +is required. + +### Restore Contract + +The loader follows a fixed contract: + +- **`.edi` takes precedence.** When a project directory contains both + `project.edi` and a legacy `project.cif`, the loader reads + `project.edi` and ignores `project.cif`, treating the `.cif` as a + stale pre-migration copy. It does not merge the two. +- **Clear error for legacy-only projects.** A directory that contains + only `project.cif` fails to load with an explicit migration error that + names the file and tells the user to open it in a supporting version + and re-save as `.edi`. The loader never silently produces an empty or + partial project. +- **Clear error for missing Edi metadata.** A project directory with + neither `project.edi` nor legacy `project.cif` fails with an explicit + message naming the required `project.edi` marker. + +## Handler Model + +The original `CifHandler.names` list was overloaded: the first entry was +the default write tag, and the full list doubled as the import-alias +list, with a separate `iucr_name` for report export. The accepted model +makes the two formats explicit with one list per format, in a class +renamed `TagSpec`: + +```python +TagSpec( + edi_names=['_atom_site.adp_iso'], + cif_names=[ + '_atom_site.B_iso_or_equiv', + '_atom_site.U_iso_or_equiv', + ], +) +``` + +Responsibilities: + +- `edi_names`: names for the **Edi** format. `edi_name` (`edi_names[0]`) + is the canonical write tag; the whole list is accepted on `.edi` read. +- `cif_names`: names for strict **CIF** import/export. `cif_name` + (`cif_names[0]`) is the canonical name written by the + report/strict-CIF export — an IUCr/pdCIF dictionary name where one + exists, an `_easydiffraction_*` extension otherwise — and the whole + list is accepted on `.cif` import. Defaults to `edi_names` when + omitted. There is no separate `iucr_name` field; export reads + `cif_names[0]`. +- category transformers: report-CIF reshaping when a field cannot map + one-to-one. + +For type-neutral ADPs, `edi_names` stays the neutral +`_atom_site.adp_iso` (so the Edi save is always type-neutral) while the +type-specific B/U order is applied to `cif_names` for export. + +## Consequences + +### Positive + +- Project files stop claiming to be strict CIF while still using a + mature STAR parser and syntax. +- Python and saved-project names become easier for scientists to match. +- Official CIF remains available where it matters: import/export and + report submission. +- Type-neutral ADP persistence becomes straightforward: + `_atom_site.adp_iso` and `_atom_site_aniso.adp_ij`. +- Future project-owned categories no longer need to search for awkward + pseudo-CIF tags before they have an external dictionary counterpart. + +### Trade-Offs + +- The saved project layout changes from `*.cif` to `*.edi`. +- Existing docs, tutorials, tests, ZIP project detection, and loaders + need an explicit migration. +- External tools that previously tried to read project `*.cif` files + must instead use `reports/.cif` or official CIF import/export + paths. +- The code needs a clearer handler API so import aliases and write names + are not conflated. + +## Considered Naming Options + +### Option A: Type In The Data Names + +This mirrors how CIF often encodes the selected convention or model in +the item names themselves. + +```text +loop_ +_background_line_segment.id +_background_line_segment.x +_background_line_segment.y +1 10.0 120.0 +2 20.0 118.0 +``` + +```text +loop_ +_atom_site.label +_atom_site.B_iso_or_equiv +Si 0.5 +``` + +Advantages: + +- The tag itself tells a hand editor which convention or model is being + edited. +- There is no separate selector field that can disagree with the value + fields. +- It matches external interchange formats where independent programs + cannot rely on EasyDiffraction project state. + +Disadvantages: + +- It breaks stable parameter identity for fields like ADPs. The same + conceptual value would move between `B_iso_or_equiv`, + `U_iso_or_equiv`, and `beta_*`, which complicates aliases, + constraints, free flags, tables, and UI state. +- Empty selected models are hard to represent. A line-segment background + with no points has no loop rows from which to infer the selected type. +- It reintroduces long dictionary names into the internal project + format. + +### Option B: Selector Plus Generic Names + +This is the selected Edi policy. + +```text +_background.type line_segment + +loop_ +_background.id +_background.position +_background.intensity +1 10.0 120.0 +2 20.0 118.0 +``` + +```text +loop_ +_atom_site.id +_atom_site.adp_type +_atom_site.adp_iso +Si Biso 0.5 +O Uiso 0.0063 +``` + +Advantages: + +- It matches the Python API and the switchable-category selector + contract. +- Parameter names remain stable across type switches, which protects + aliases, constraints, fit flags, parameter tables, and display state. +- Empty selected models are representable because the selector exists + even when no value rows exist. +- The report CIF writer can still emit official type-specific tags at + the external boundary. + +Disadvantages: + +- A hand editor must read selector and values together. +- Raw text can contain inconsistent combinations, such as + `_background.type chebyshev` with `_background.position` rows. Load + validation must report these boundary-input errors clearly. + +### Option C: Selector Plus Type-Specific Body + +This keeps an explicit selector and also embeds the selected type in the +body tags. + +```text +_background.type line_segment + +loop_ +_background_line_segment.id +_background_line_segment.x +_background_line_segment.y +1 10.0 120.0 +2 20.0 118.0 +``` + +Advantages: + +- The selected type remains explicit even for empty models. +- Body tags are highly self-describing. +- It may be useful for rare categories whose implementations have + genuinely different shapes. + +Disadvantages: + +- It duplicates type information in two places. +- It creates consistency rules between selector and body category. +- It adds loader complexity and still harms API-to-file predictability. + +Edi therefore uses Option B by default. Option C is allowed only when +selected implementations have genuinely different data shapes and the +type-specific body names improve hand editing more than they harm +consistency. ADPs are not such a case: `adp_iso` and `adp_ij` remain +generic values interpreted through `adp_type`. + +## Parameter Inventory + +This table inventories persisted descriptors currently declared through +`CifHandler` in `src/easydiffraction` as of 2026-06-12. Compact +`{a,b,c}` notation means each listed field is a separate parameter. The +official/report column lists IUCr or pdCIF names where a current +one-to-one or report-transform mapping is known. Blank means no official +CIF name is currently available or the current report path uses an +EasyDiffraction extension tag. + +The table is intended to be exhaustive for every `CifHandler`-declared +descriptor in `src/easydiffraction`. Implementation must verify that +claim with a generated inventory before changing write tags; any +descriptor absent from this table is a migration blocker. + +| Area | Current EasyDiffraction names | Current project tags | Suggested Edi tags | Official/report CIF names | +| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `analysis.aliases` | `label`, `param_unique_name` | `_alias.{label,param_unique_name}` | `_alias.{id,parameter_unique_name}` | | +| `analysis.constraints` | `id`, `expression` | `_constraint.{id,expression}` | `_constraint.{id,expression}` | | +| `analysis.fit_parameter_correlations` | `id`, `source_kind`, `param_unique_name_i`, `param_unique_name_j`, `correlation` | `_fit_parameter_correlation.{id,source_kind,param_unique_name_i,param_unique_name_j,correlation}` | `_fit_parameter_correlation.{id,source_kind,parameter_unique_name_i,parameter_unique_name_j,correlation}` | | +| `analysis.fit_parameters` | `param_unique_name`, `fit_min`, `fit_max`, `fit_bounds_uncertainty_multiplier`, `start_value`, `start_uncertainty`, `posterior_best_sample_value`, `posterior_median`, `posterior_uncertainty`, `posterior_interval_68_low`, `posterior_interval_68_high`, `posterior_interval_95_low`, `posterior_interval_95_high`, `posterior_gelman_rubin`, `posterior_effective_sample_size_bulk` | `_fit_parameter.*` with same item names | `_fit_parameter.{parameter_unique_name,fit_min,fit_max,bounds_uncertainty_multiplier,start_value,start_uncertainty,posterior_best_sample_value,posterior_median,posterior_uncertainty,posterior_interval_68_low,posterior_interval_68_high,posterior_interval_95_low,posterior_interval_95_high,posterior_gelman_rubin,posterior_effective_sample_size_bulk}` | | +| `analysis.fit_result` common | `result_kind`, `success`, `message`, `iterations`, `fitting_time`, `reduced_chi_square` | `_fit_result.{result_kind,success,message,iterations,fitting_time,reduced_chi_square}` | same | `reduced_chi_square` maps by report topology to `_refine_ls.*` or `_pd_proc_ls.*` | +| `analysis.fit_result` least-squares core | `objective_name`, `objective_value`, `n_data_points`, `n_parameters`, `n_free_parameters`, `degrees_of_freedom`, `covariance_available`, `correlation_available`, `exit_reason` | `_fit_result.*` with same item names | same | topology-specific `_refine_ls.*` / `_pd_proc_ls.*` for counts where reportable | +| `analysis.fit_result` least-squares R factors | `r_factor_all`, `wr_factor_all`, `r_factor_gt`, `wr_factor_gt` | `_fit_result.R_factor_all`, `_fit_result.wR_factor_all`, `_fit_result.R_factor_gt`, `_fit_result.wR_factor_gt` | `_fit_result.{r_factor_all,wr_factor_all,r_factor_gt,wr_factor_gt}` | `_refine_ls.{R_factor_all,wR_factor_all,R_factor_gt,wR_factor_gt}` | +| `analysis.fit_result` powder profile | `prof_r_factor`, `prof_wr_factor`, `prof_wr_expected`, `profile_function`, `background_function` | `_fit_result.prof_R_factor`, `_fit_result.prof_wR_factor`, `_fit_result.prof_wR_expected`, `_fit_result.profile_function`, `_fit_result.background_function` | `_fit_result.{prof_r_factor,prof_wr_factor,prof_wr_expected,profile_function,background_function}` | `_pd_proc_ls.{prof_R_factor,prof_wR_factor,prof_wR_expected,profile_function,background_function}` | +| `analysis.fit_result` fit counts | `number_restraints`, `number_constraints`, `shift_over_su_max`, `shift_over_su_mean` | `_fit_result.*` with same item names | same | `_refine_ls.{number_restraints,number_constraints}` for counts | +| `analysis.fit_result` reflection summaries | `threshold_expression`, `number_reflns_total`, `number_reflns_gt` | `_fit_result.*` with same item names | same | `_reflns.{threshold_expression,number_total,number_gt}` | +| `analysis.fit_result` Bayesian | `point_estimate_name`, `sampler_completed`, `credible_interval_inner`, `credible_interval_outer`, `acceptance_rate_mean`, `resolved_random_seed`, `gelman_rubin_max`, `effective_sample_size_min`, `best_log_posterior` | `_fit_result.*` with same item names | same | | +| `analysis.fitting_mode` | `type` | `_fitting_mode.type` | same | | +| `analysis.joint_fit` | `experiment_id`, `weight` | `_joint_fit.{experiment_id,weight}` | same | | +| `analysis.minimizer` common | `type`, `max_iterations` | `_minimizer.{type,max_iterations}` | same | | +| `analysis.minimizer` Bayesian | `sampling_steps`, `burn_in_steps`, `thinning_interval`, `population_size`, `parallel_workers`, `initialization_method`, `random_seed`, `proposal_moves` | `_minimizer.*` with same item names | same | | +| `analysis.sequential_fit` | `data_dir`, `file_pattern`, `max_workers`, `chunk_size`, `reverse` | `_sequential_fit.*` with same item names | same | | +| `analysis.sequential_fit_extract` | `id`, `target`, `pattern`, `required` | `_sequential_fit_extract.*` with same item names | same | | +| `analysis.software` (role loop) | `framework.{name,version,url}`, `calculator.{name,version,url}`, `minimizer.{name,version,url}`, `timestamp` | `_software.{framework,calculator,minimizer}_{name,version,url}`, `_software.timestamp` | `_software.{id,name,version,url}` loop (`id` ∈ framework/calculator/minimizer); `timestamp` → `_metadata.timestamp` | `_computing.structure_refinement` and `_easydiffraction_software.*` derived in report CIF | +| `experiment.background` selector | `type` | `_background.type` | same | | +| `experiment.background` line segment | `id`, `x`, `y` | `_pd_background.id`, `_pd_background.line_segment_X`, `_pd_background.line_segment_intensity` | `_background.{id,position,intensity}` | `_pd_background.*` where representable | +| `experiment.background` Chebyshev | `id`, `order`, `coef` | `_pd_background.id`, `_pd_background.Chebyshev_order`, `_pd_background.Chebyshev_coef` | `_background.{id,order,coef}` | `_pd_background.*` where representable | +| `experiment.calculator` | `type` | `_calculator.type` | same | | +| `experiment.data` Bragg powder | `point_id`, `d_spacing`, `intensity_meas`, `intensity_meas_su`, `intensity_calc`, `intensity_bkg`, `calc_status`, `two_theta`, `time_of_flight` | `_pd_data.point_id`, `_pd_proc.d_spacing`, `_pd_meas.intensity_total`, `_pd_meas.intensity_total_su`, `_pd_calc.intensity_total`, `_pd_calc.intensity_bkg`, `_pd_data.refinement_status`, `_pd_proc.2theta_scan`, `_pd_meas.time_of_flight` | `_data.{id,d_spacing,intensity_meas,intensity_meas_su,intensity_calc,intensity_bkg,calc_status,two_theta,time_of_flight}` | current `_pd_*` tags, with report profile loop using `_pd_meas.*`, `_pd_calc.*`, `_pd_proc.*`, and `_pd_proc_ls.weight` | +| `experiment.data` total powder | `point_id`, `r`, `g_r_meas`, `g_r_meas_su`, `g_r_calc`, `calc_status` | `_pd_data.point_id`, `_pd_proc.r`, `_pd_meas.intensity_total`, `_pd_meas.intensity_total_su`, `_pd_calc.intensity_total`, `_pd_data.refinement_status` | `_data.{id,r,g_r_meas,g_r_meas_su,g_r_calc,calc_status}` | PDF-specific report names are not finalized | +| `experiment.data_range` CWL powder | `two_theta_min`, `two_theta_max`, `two_theta_inc` | `_pd_meas.{2theta_range_min,2theta_range_max,2theta_range_inc}` | `_data_range.{two_theta_min,two_theta_max,two_theta_inc}` | current `_pd_meas.*` tags | +| `experiment.data_range` single crystal | `sin_theta_over_lambda_min`, `sin_theta_over_lambda_max` | `_refln.{sin_theta_over_lambda_range_min,sin_theta_over_lambda_range_max}` | `_data_range.{sin_theta_over_lambda_min,sin_theta_over_lambda_max}` | current `_refln.*` tags | +| `experiment.data_range` TOF | `time_of_flight_min`, `time_of_flight_max`, `time_of_flight_inc` | `_pd_meas.{time_of_flight_range_min,time_of_flight_range_max,time_of_flight_range_inc}` | `_data_range.{time_of_flight_min,time_of_flight_max,time_of_flight_inc}` | current `_pd_meas.*` tags | +| `experiment.diffrn` | `ambient_temperature`, `ambient_pressure`, `ambient_magnetic_field`, `ambient_electric_field` | `_diffrn.*` with same item names | same | `_diffrn.{ambient_temperature,ambient_pressure}`; fields and electric/magnetic fields are report extensions | +| `experiment.excluded_regions` | `id`, `start`, `end` | `_excluded_region.{id,start,end}` | same | report free text `_pd_proc.info_excluded_regions` plus extension rows | +| `experiment.type` → `experiment_type` | `sample_form`, `beam_mode`, `radiation_probe`, `scattering_type` | `_expt_type.{sample_form,beam_mode,radiation_probe,scattering_type}` | `_experiment_type.{sample_form,beam_mode,radiation_probe,scattering_type}` | | +| `experiment.extinction` | `type`, `model`, `mosaicity`, `radius` | `_extinction.{type,model,mosaicity,radius}` | same | `_refine_ls.extinction_method`, `_refine_ls.extinction_coef`, `_refine.special_details` by report transform | +| `experiment.instrument` CWL | `setup_wavelength`, `calib_twotheta_offset`, `calib_sample_displacement`, `calib_sample_transparency` | `_instr.wavelength`, `_instr.2theta_offset`, `_instr.sample_displacement`, `_instr.sample_transparency` | `_instrument.{setup_wavelength,calib_twotheta_offset,calib_sample_displacement,calib_sample_transparency}` | `_diffrn_radiation_wavelength.value`; `_pd_calib.2theta_offset` | +| `experiment.instrument` TOF | `setup_twotheta_bank`, `calib_d_to_tof_offset`, `calib_d_to_tof_linear`, `calib_d_to_tof_quad`, `calib_d_to_tof_recip` | `_instr.2theta_bank`, `_instr.{d_to_tof_offset,d_to_tof_linear,d_to_tof_quad,d_to_tof_recip}` | `_instrument.{setup_twotheta_bank,calib_d_to_tof_offset,calib_d_to_tof_linear,calib_d_to_tof_quadratic,calib_d_to_tof_reciprocal}` | `_pd_calib_d_to_tof.{id,power,coeff,coeff_su,diffractogram_id}` loop for nonzero coefficients | +| `experiment.linked_crystal` → `linked_structure` (single crystal) | `id`, `scale` | `_sc_crystal_block.{id,scale}` | `_linked_structure.{structure_id,scale}` | | +| `experiment.linked_phases` → `linked_structures` (powder) | `id`, `scale` | `_pd_phase_block.{id,scale}` | `_linked_structure.{structure_id,scale}` | `_pd_phase_block.{id,scale}` | +| `experiment.peak` CWL profile | `type`, `broad_gauss_u`, `broad_gauss_v`, `broad_gauss_w`, `broad_lorentz_x`, `broad_lorentz_y`, `asym_empir_1`, `asym_empir_2`, `asym_empir_3`, `asym_empir_4`, `asym_fcj_1`, `asym_fcj_2` | `_peak.*` with same item names | same | no pdCIF one-to-one parametric profile tags | +| `experiment.peak` TOF profile | `broad_gauss_sigma_{0,1,2}`, `broad_lorentz_gamma_{0,1,2}`, `rise_alpha_{0,1}`, `decay_beta_{0,1}`, `dexp_rise_alpha_{1,2}`, `dexp_decay_beta_{00,01,10}`, `dexp_switch_r_{01,02,03}` | `_peak.{gauss_sigma_0,gauss_sigma_1,gauss_sigma_2,lorentz_gamma_0,lorentz_gamma_1,lorentz_gamma_2,rise_alpha_0,rise_alpha_1,decay_beta_0,decay_beta_1,dexp_rise_alpha_1,dexp_rise_alpha_2,dexp_decay_beta_00,dexp_decay_beta_01,dexp_decay_beta_10,dexp_switch_r_01,dexp_switch_r_02,dexp_switch_r_03}` | `_peak.{broad_gauss_sigma_0,broad_gauss_sigma_1,broad_gauss_sigma_2,broad_lorentz_gamma_0,broad_lorentz_gamma_1,broad_lorentz_gamma_2,rise_alpha_0,rise_alpha_1,decay_beta_0,decay_beta_1,dexp_rise_alpha_1,dexp_rise_alpha_2,dexp_decay_beta_00,dexp_decay_beta_01,dexp_decay_beta_10,dexp_switch_r_01,dexp_switch_r_02,dexp_switch_r_03}` | no pdCIF one-to-one parametric profile tags | +| `experiment.peak` total scattering | `damp_q`, `broad_q`, `cutoff_q`, `sharp_delta_1`, `sharp_delta_2`, `damp_particle_diameter` | `_peak.*` with same item names | same | no finalized PDF-specific CIF tags | +| `experiment.preferred_orientation` | `phase_id`, `march_r`, `index_h`, `index_k`, `index_l`, `march_random_fract` | `_pref_orient.*` with same item names | `_preferred_orientation.{structure_id,march_r,index_h,index_k,index_l,march_random_fract}` | `_pd_pref_orient_March_Dollase.{phase_id,r,index_h,index_k,index_l}`; random fraction is an EasyDiffraction report extension | +| `experiment.refln` powder calculated | `id`, `d_spacing`, `sin_theta_over_lambda`, `index_h`, `index_k`, `index_l`, `phase_id`, `f_calc`, `f_squared_calc`, `two_theta`, `time_of_flight` | `_refln.*` with same item names | `_refln.{id,d_spacing,sin_theta_over_lambda,index_h,index_k,index_l,structure_id,f_calc,f_squared_calc,two_theta,time_of_flight}` | report powder reflection loop uses `_refln.index_*`, `_refln.F_squared_*`, `_pd_refln.phase_id`, `_refln.d_spacing` | +| `experiment.refln` single crystal | `id`, `d_spacing`, `sin_theta_over_lambda`, `index_h`, `index_k`, `index_l`, `intensity_meas`, `intensity_meas_su`, `intensity_calc`, `wavelength` | `_refln.*` with same item names | same | `_refln.*`; report maps intensities to `_refln.F_squared_*` where applicable | +| `structure.atom_sites` identity and coordinates | `label`, `type_symbol`, `fract_x`, `fract_y`, `fract_z`, `occupancy` | `_atom_site.*` with same item names | `_atom_site.{id,type_symbol,fract_x,fract_y,fract_z,occupancy}` | `_atom_site.{label,type_symbol,fract_x,fract_y,fract_z,occupancy}` | +| `structure.atom_sites` Wyckoff | `wyckoff_letter`, `multiplicity` | `_atom_site.Wyckoff_symbol`, `_atom_site.site_symmetry_multiplicity` | `_atom_site.{wyckoff_letter,multiplicity}` | `_atom_site.Wyckoff_symbol`, `_atom_site.site_symmetry_multiplicity` | +| `structure.atom_sites` ADP | `adp_iso`, `adp_type` | `_atom_site.B_iso_or_equiv`, `_atom_site.ADP_type` | `_atom_site.{adp_iso,adp_type}` | `_atom_site.{B_iso_or_equiv,U_iso_or_equiv}`, `_atom_site.ADP_type` | +| `structure.atom_site_aniso` | `label`, `adp_11`, `adp_22`, `adp_33`, `adp_12`, `adp_13`, `adp_23` | `_atom_site_aniso.label`, `_atom_site_aniso.B_11`, `_atom_site_aniso.B_22`, `_atom_site_aniso.B_33`, `_atom_site_aniso.B_12`, `_atom_site_aniso.B_13`, `_atom_site_aniso.B_23` | `_atom_site_aniso.{id,adp_11,adp_22,adp_33,adp_12,adp_13,adp_23}` | `_atom_site_aniso.{label,B_*,U_*,beta_*}` by `adp_type` | +| `structure.cell` | `length_a`, `length_b`, `length_c`, `angle_alpha`, `angle_beta`, `angle_gamma` | `_cell.*` with same item names | same | same | +| `structure.geom` | `min_bond_distance_cutoff`, `bond_distance_incr` | `_geom.*` with same item names | `_geom.{min_bond_distance_cutoff,bond_distance_inc}` | same | +| `structure.space_group` | `name_h_m`, `it_coordinate_system_code` | `_space_group.name_H-M_alt`, `_space_group.IT_coordinate_system_code` | `_space_group.{name_h_m,coord_system_code}` | `_space_group.name_H-M_alt`, `_space_group.IT_coordinate_system_code` | +| `structure.space_group_wyckoff` (derived; **not persisted**) | `id`, `letter`, `multiplicity`, `site_symmetry`, `coords_xyz` | `_space_group_Wyckoff.{id,letter,multiplicity,site_symmetry,coords_xyz}` | — (regenerated from `space_group` on load; never written) | `_space_group_Wyckoff.*` | +| `project.info` → `metadata` | `name`, `title`, `description`, `created`, `last_modified`; relocated `analysis.software.timestamp` as `timestamp` | `_project.id`, `_project.title`, `_project.description`, `_project.created`, `_project.last_modified`; `_software.timestamp` | `_metadata.{name,title,description,created,last_modified,timestamp}` | | +| `project.rendering_plot` | `type` | `_rendering_plot.type` | same | | +| `project.rendering_structure` | `type` | `_rendering_structure.type` | same | | +| `project.rendering_table` | `type` | `_rendering_table.type` | same | | +| `project.report` | `cif`, `html`, `tex`, `pdf`, `html_offline` | `_report.*` with same item names | same | report-output configuration only | +| `project.structure_style` | `atom_view`, `color_scheme`, `adp_probability`, `atom_scale` | `_structure_style.*` with same item names | same | | +| `project.structure_view` | `show_labels`, `show_moments`, `range_a_min`, `range_a_max`, `range_b_min`, `range_b_max`, `range_c_min`, `range_c_max` | `_structure_view.*` with same item names | same | | +| `project.verbosity` | `fit` | `_verbosity.fit` | same | | + +## Naming Precedent and Guard Notes + +These notes record why several names are intentional, so a future +reviewer does not "correct" them toward a different precedent. + +- **Structure links use "structure", not "phase"/"crystal".** The model + datablock is `Structure` and the API is `project.structures`, so an + experiment links structures. `experiment.linked_phases` → + `experiment.linked_structures` (powder, collection) and + `experiment.linked_crystal` → `experiment.linked_structure` (single + crystal); both serialize to the `_linked_structure` category. Every + structure reference uses `structure_id` (mirroring the existing + `_joint_fit.experiment_id`). `phase`/`crystal`/`phase_id` and the IUCr + `_pd_phase_block` / `_sc_crystal_block` tags remain read aliases. + These are public-API renames, not only file-tag changes (blast radius + ~100+ files); they belong in Phase 1. Accessing the wrong cardinality + for the experiment family, such as `linked_structure` on a powder + experiment or `linked_structures` on a single-crystal experiment, must + fail with a clear sample-form-aware error rather than silently + ignoring user input. +- **`id` is the universal own-key.** `_atom_site.id` and `_alias.id` + replace CIF's `label` so the rule "every row has an `id`; `_id` + columns point elsewhere" has no exceptions. Edi already departs from + strict CIF (e.g. `adp_iso` for `B_iso_or_equiv`), and report CIF still + writes the official `_atom_site.label`, so compatibility is + unaffected. +- **`damp_` groups by effect, not mechanism.** In the total-scattering + peak, `damp_q` (resolution) and `damp_particle_diameter` (finite size) + both attenuate the G(r) amplitude envelope, so they share the `damp_` + family; `broad_` grows peak width, `sharp_` shrinks it, `cutoff_` is + the Fourier truncation. `damp_particle_diameter` is intentional — do + not rename it to `particle_diameter`. +- **`two_theta_inc` keeps `inc`.** `inc` is the IUCr pdCIF term + (`_pd_meas_2theta_range_inc`); `step` is FullProf-internal only. +- **`march_random_fract` is not IUCr `fract`.** IUCr + `_pd_pref_orient_March_Dollase.fract` is the multi-direction + fractional contribution; Edi's field is the random/untextured fraction + (cryspy `_texture_g_2`). Do not collapse them. +- **Type-neutral ADPs are deliberate.** `adp_iso` / `adp_type` / + `adp_11` stay generic because `adp_type` is co-persisted and + load-validated, making the B↔U distinction lossless while keeping + parameter identity stable across a type switch. +- **Report-CIF casing.** Edi lowercases for Python/STAR friendliness, + but the report writer emits IUCr canonical casing: `_refln.F_calc`, + `_refln.F_squared_calc`, `_space_group.name_H-M_alt`. The uppercase + forms are also accepted as read aliases. +- **`coord_system_code` uses existing coordinate wording.** The value is + still the International Tables coordinate-system qualifier, and report + CIF still writes `_space_group.IT_coordinate_system_code`, but the + project-facing API and Edi field use `coord_system_code`. `coord` is + already used in EasyDiffraction's Wyckoff-coordinate vocabulary + (`coord_code`, `coords_xyz`), while `it` is not otherwise used in + project-facing parameter names. +- **`_data` is intentional.** `experiment.data` is already a mass-noun + owner attribute, so Edi keeps `_data` instead of inventing + `_data_point`. The row identity is still `_data.id`; the category name + follows the public owner attribute when the owner is not plural. + +## Code/Edi 1-to-1 Correspondence + +Edi targets a strict 1-to-1 correspondence between the public Python API +path and the persisted data name: a saved `_category.field` equals +`object.category.field` in code. The **only** systematic divergence is +that a collection category is plural in the API (`structure.atom_sites`) +and singular in the file (`_atom_site`), because the file names the +per-row item — the universal STAR/CIF convention. + +Achieving 1-to-1 at v1.0.0 requires these **public-API renames** (they +are API changes, not only file-tag changes; official import aliases stay +available, report CIF keeps the official names, and pre-release Edi +names are not preserved as legacy aliases): + +| Code today | Code at v1.0.0 | Edi | +| ------------------------------------------------- | ----------------------------------------- | ------------------------------------- | +| `atom_sites[*].label` | `atom_sites[*].id` | `_atom_site.id` | +| `atom_site_aniso[*].label` | `atom_site_aniso[*].id` | `_atom_site_aniso.id` | +| `aliases[*].label` | `aliases[*].id` | `_alias.id` | +| `experiment.linked_phases` | `experiment.linked_structures` | `_linked_structure` | +| `experiment.linked_crystal` | `experiment.linked_structure` | `_linked_structure` | +| linked item `id` | linked item `structure_id` | `_linked_structure.structure_id` | +| `refln` (powder) `phase_id` | `refln` `structure_id` | `_refln.structure_id` | +| `preferred_orientation.phase_id` | `preferred_orientation.structure_id` | `_preferred_orientation.structure_id` | +| `experiment.type` | `experiment.experiment_type` | `_experiment_type` | +| `project.info` | `project.metadata` | `_metadata` | +| `structure.space_group.it_coordinate_system_code` | `structure.space_group.coord_system_code` | `_space_group.coord_system_code` | + +Plus the Abbreviation Policy renames (§Naming Policy), listed +per-parameter in the Per-Parameter Map: `bond_distance_incr`→`inc`, +`calib_d_to_tof_quad`/`recip`→`quadratic`/`reciprocal`, +`param_unique_name`→`parameter_unique_name`, `data.point_id`→`id`, +`fit_bounds_uncertainty_multiplier`→`bounds_uncertainty_multiplier`, and +background line-segment `x`/`y`→`position`/`intensity`. + +### Python-Side Migration + +The public-API renames above remove the old Python attribute names when +the migration lands. The project is still in beta, so there are no +transitional Python properties, no deprecation warnings, and no +dual-write public API. "Read alias" in this ADR means only that the +loader accepts old **file data names** through `import_names`; it does +not mean `atom_site.label`, `experiment.type`, `linked_phases`, +`param_unique_name`, or other renamed Python attributes remain usable. + +The same rule applies to every public creation surface. `add()` and +`create()` keyword arguments use the v1.0.0 names in the table: for +example `atom_sites.add(id='Si', ...)`, +`background.add(position=..., intensity=...)`, and +`fit_parameters.add(parameter_unique_name=...)`. Implementation must +migrate tutorials and tests at those call sites and must make stale +keywords fail clearly at the public boundary rather than being silently +ignored by guarded assignment. + +Documented exceptions where strict 1-to-1 is intentionally not pursued: + +- **`_linked_structure` maps to two owner attributes** — the powder + collection `linked_structures` and the single-crystal item + `linked_structure`. They have different cardinality (collection vs + single item) but identical fields, and an experiment is either powder + or single crystal, so only one appears per file. This is one file + category ↔ two Python attributes by design. +- **`analysis.software` is modelled as a role loop** (not an exception — + it becomes 1-to-1 like the rest). The three roles (framework, + calculator, minimizer) are persisted as a `_software` loop keyed by + `id`, accessed in code as `software[role].{name,version,url}`, + following the same singular-loop + `id` rules as every other + collection. The role id is a closed value set and must be backed by a + `(str, Enum)` such as `SoftwareRoleEnum`, not ad hoc strings. The + save-time `timestamp` moves to `project.metadata`. + +## Per-Parameter Map + +A human-readable, one-row-per-parameter companion to the compact +inventory above. Shorthands: `structure` = +`project.structures['']`, `experiment` = +`project.experiments['']`, `analysis` = `project.analysis`, +`project` = the project facade. `['']` marks a loop (collection) +row. In the **v1.0.0 API** column, `same` means the public name is +unchanged from today; an explicit path marks a rename. The **Edi** +column is the persisted data name. + +### Structure + +| Current API | v1.0.0 API | Edi | +| ------------------------------------------------- | ----------------------------------------- | -------------------------------- | +| `structure.cell.length_a` | same | `_cell.length_a` | +| `structure.cell.length_b` | same | `_cell.length_b` | +| `structure.cell.length_c` | same | `_cell.length_c` | +| `structure.cell.angle_alpha` | same | `_cell.angle_alpha` | +| `structure.cell.angle_beta` | same | `_cell.angle_beta` | +| `structure.cell.angle_gamma` | same | `_cell.angle_gamma` | +| `structure.space_group.name_h_m` | same | `_space_group.name_h_m` | +| `structure.space_group.it_coordinate_system_code` | `structure.space_group.coord_system_code` | `_space_group.coord_system_code` | +| `structure.atom_sites[''].label` | `structure.atom_sites[''].id` | `_atom_site.id` | +| `structure.atom_sites[''].type_symbol` | same | `_atom_site.type_symbol` | +| `structure.atom_sites[''].fract_x` | same | `_atom_site.fract_x` | +| `structure.atom_sites[''].fract_y` | same | `_atom_site.fract_y` | +| `structure.atom_sites[''].fract_z` | same | `_atom_site.fract_z` | +| `structure.atom_sites[''].occupancy` | same | `_atom_site.occupancy` | +| `structure.atom_sites[''].wyckoff_letter` | same | `_atom_site.wyckoff_letter` | +| `structure.atom_sites[''].multiplicity` | same | `_atom_site.multiplicity` | +| `structure.atom_sites[''].adp_iso` | same | `_atom_site.adp_iso` | +| `structure.atom_sites[''].adp_type` | same | `_atom_site.adp_type` | +| `structure.atom_site_aniso[''].label` | `structure.atom_site_aniso[''].id` | `_atom_site_aniso.id` | +| `structure.atom_site_aniso[''].adp_11` | same | `_atom_site_aniso.adp_11` | +| `structure.atom_site_aniso[''].adp_22` | same | `_atom_site_aniso.adp_22` | +| `structure.atom_site_aniso[''].adp_33` | same | `_atom_site_aniso.adp_33` | +| `structure.atom_site_aniso[''].adp_12` | same | `_atom_site_aniso.adp_12` | +| `structure.atom_site_aniso[''].adp_13` | same | `_atom_site_aniso.adp_13` | +| `structure.atom_site_aniso[''].adp_23` | same | `_atom_site_aniso.adp_23` | +| `structure.geom.min_bond_distance_cutoff` | same | `_geom.min_bond_distance_cutoff` | +| `structure.geom.bond_distance_incr` | `structure.geom.bond_distance_inc` | `_geom.bond_distance_inc` | +| `structure.space_group_wyckoff[''].*` | same | — (derived; not persisted) | + +### Experiment + +| Current API | v1.0.0 API | Edi | +| ----------------------------------------------------- | --------------------------------------------------- | ------------------------------------------- | +| `experiment.type.sample_form` | `experiment.experiment_type.sample_form` | `_experiment_type.sample_form` | +| `experiment.type.beam_mode` | `experiment.experiment_type.beam_mode` | `_experiment_type.beam_mode` | +| `experiment.type.radiation_probe` | `experiment.experiment_type.radiation_probe` | `_experiment_type.radiation_probe` | +| `experiment.type.scattering_type` | `experiment.experiment_type.scattering_type` | `_experiment_type.scattering_type` | +| `experiment.calculator.type` | same | `_calculator.type` | +| `experiment.background.type` | same | `_background.type` | +| `experiment.background[''].x` | `experiment.background[''].position` | `_background.position` | +| `experiment.background[''].y` | `experiment.background[''].intensity` | `_background.intensity` | +| `experiment.background[''].order` | same | `_background.order` | +| `experiment.background[''].coef` | same | `_background.coef` | +| `experiment.instrument.setup_wavelength` | same | `_instrument.setup_wavelength` | +| `experiment.instrument.calib_twotheta_offset` | same | `_instrument.calib_twotheta_offset` | +| `experiment.instrument.calib_sample_displacement` | same | `_instrument.calib_sample_displacement` | +| `experiment.instrument.calib_sample_transparency` | same | `_instrument.calib_sample_transparency` | +| `experiment.instrument.setup_twotheta_bank` | same | `_instrument.setup_twotheta_bank` | +| `experiment.instrument.calib_d_to_tof_offset` | same | `_instrument.calib_d_to_tof_offset` | +| `experiment.instrument.calib_d_to_tof_linear` | same | `_instrument.calib_d_to_tof_linear` | +| `experiment.instrument.calib_d_to_tof_quad` | `experiment.instrument.calib_d_to_tof_quadratic` | `_instrument.calib_d_to_tof_quadratic` | +| `experiment.instrument.calib_d_to_tof_recip` | `experiment.instrument.calib_d_to_tof_reciprocal` | `_instrument.calib_d_to_tof_reciprocal` | +| `experiment.peak.type` | same | `_peak.type` | +| `experiment.peak.broad_gauss_u` | same | `_peak.broad_gauss_u` | +| `experiment.peak.broad_gauss_v` | same | `_peak.broad_gauss_v` | +| `experiment.peak.broad_gauss_w` | same | `_peak.broad_gauss_w` | +| `experiment.peak.broad_lorentz_x` | same | `_peak.broad_lorentz_x` | +| `experiment.peak.broad_lorentz_y` | same | `_peak.broad_lorentz_y` | +| `experiment.peak.asym_empir_1` | same | `_peak.asym_empir_1` | +| `experiment.peak.asym_empir_2` | same | `_peak.asym_empir_2` | +| `experiment.peak.asym_empir_3` | same | `_peak.asym_empir_3` | +| `experiment.peak.asym_empir_4` | same | `_peak.asym_empir_4` | +| `experiment.peak.asym_fcj_1` | same | `_peak.asym_fcj_1` | +| `experiment.peak.asym_fcj_2` | same | `_peak.asym_fcj_2` | +| `experiment.peak.broad_gauss_sigma_0` | same | `_peak.broad_gauss_sigma_0` | +| `experiment.peak.broad_gauss_sigma_1` | same | `_peak.broad_gauss_sigma_1` | +| `experiment.peak.broad_gauss_sigma_2` | same | `_peak.broad_gauss_sigma_2` | +| `experiment.peak.broad_lorentz_gamma_0` | same | `_peak.broad_lorentz_gamma_0` | +| `experiment.peak.broad_lorentz_gamma_1` | same | `_peak.broad_lorentz_gamma_1` | +| `experiment.peak.broad_lorentz_gamma_2` | same | `_peak.broad_lorentz_gamma_2` | +| `experiment.peak.rise_alpha_0` | same | `_peak.rise_alpha_0` | +| `experiment.peak.rise_alpha_1` | same | `_peak.rise_alpha_1` | +| `experiment.peak.decay_beta_0` | same | `_peak.decay_beta_0` | +| `experiment.peak.decay_beta_1` | same | `_peak.decay_beta_1` | +| `experiment.peak.dexp_rise_alpha_1` | same | `_peak.dexp_rise_alpha_1` | +| `experiment.peak.dexp_rise_alpha_2` | same | `_peak.dexp_rise_alpha_2` | +| `experiment.peak.dexp_decay_beta_00` | same | `_peak.dexp_decay_beta_00` | +| `experiment.peak.dexp_decay_beta_01` | same | `_peak.dexp_decay_beta_01` | +| `experiment.peak.dexp_decay_beta_10` | same | `_peak.dexp_decay_beta_10` | +| `experiment.peak.dexp_switch_r_01` | same | `_peak.dexp_switch_r_01` | +| `experiment.peak.dexp_switch_r_02` | same | `_peak.dexp_switch_r_02` | +| `experiment.peak.dexp_switch_r_03` | same | `_peak.dexp_switch_r_03` | +| `experiment.peak.damp_q` | same | `_peak.damp_q` | +| `experiment.peak.broad_q` | same | `_peak.broad_q` | +| `experiment.peak.cutoff_q` | same | `_peak.cutoff_q` | +| `experiment.peak.sharp_delta_1` | same | `_peak.sharp_delta_1` | +| `experiment.peak.sharp_delta_2` | same | `_peak.sharp_delta_2` | +| `experiment.peak.damp_particle_diameter` | same | `_peak.damp_particle_diameter` | +| `experiment.extinction.type` | same | `_extinction.type` | +| `experiment.extinction.model` | same | `_extinction.model` | +| `experiment.extinction.mosaicity` | same | `_extinction.mosaicity` | +| `experiment.extinction.radius` | same | `_extinction.radius` | +| `experiment.diffrn.ambient_temperature` | same | `_diffrn.ambient_temperature` | +| `experiment.diffrn.ambient_pressure` | same | `_diffrn.ambient_pressure` | +| `experiment.diffrn.ambient_magnetic_field` | same | `_diffrn.ambient_magnetic_field` | +| `experiment.diffrn.ambient_electric_field` | same | `_diffrn.ambient_electric_field` | +| `experiment.data_range.two_theta_min` | same | `_data_range.two_theta_min` | +| `experiment.data_range.two_theta_max` | same | `_data_range.two_theta_max` | +| `experiment.data_range.two_theta_inc` | same | `_data_range.two_theta_inc` | +| `experiment.data_range.sin_theta_over_lambda_min` | same | `_data_range.sin_theta_over_lambda_min` | +| `experiment.data_range.sin_theta_over_lambda_max` | same | `_data_range.sin_theta_over_lambda_max` | +| `experiment.data_range.time_of_flight_min` | same | `_data_range.time_of_flight_min` | +| `experiment.data_range.time_of_flight_max` | same | `_data_range.time_of_flight_max` | +| `experiment.data_range.time_of_flight_inc` | same | `_data_range.time_of_flight_inc` | +| `experiment.data[''].point_id` | `experiment.data[''].id` | `_data.id` | +| `experiment.data[''].d_spacing` | same | `_data.d_spacing` | +| `experiment.data[''].intensity_meas` | same | `_data.intensity_meas` | +| `experiment.data[''].intensity_meas_su` | same | `_data.intensity_meas_su` | +| `experiment.data[''].intensity_calc` | same | `_data.intensity_calc` | +| `experiment.data[''].intensity_bkg` | same | `_data.intensity_bkg` | +| `experiment.data[''].calc_status` | same | `_data.calc_status` | +| `experiment.data[''].two_theta` | same | `_data.two_theta` | +| `experiment.data[''].time_of_flight` | same | `_data.time_of_flight` | +| `experiment.data[''].r` (PDF) | same | `_data.r` | +| `experiment.data[''].g_r_meas` (PDF) | same | `_data.g_r_meas` | +| `experiment.data[''].g_r_meas_su` (PDF) | same | `_data.g_r_meas_su` | +| `experiment.data[''].g_r_calc` (PDF) | same | `_data.g_r_calc` | +| `experiment.refln[''].id` | same | `_refln.id` | +| `experiment.refln[''].d_spacing` | same | `_refln.d_spacing` | +| `experiment.refln[''].sin_theta_over_lambda` | same | `_refln.sin_theta_over_lambda` | +| `experiment.refln[''].index_h` | same | `_refln.index_h` | +| `experiment.refln[''].index_k` | same | `_refln.index_k` | +| `experiment.refln[''].index_l` | same | `_refln.index_l` | +| `experiment.refln[''].phase_id` (powder) | `experiment.refln[''].structure_id` | `_refln.structure_id` | +| `experiment.refln[''].f_calc` | same | `_refln.f_calc` | +| `experiment.refln[''].f_squared_calc` | same | `_refln.f_squared_calc` | +| `experiment.refln[''].two_theta` | same | `_refln.two_theta` | +| `experiment.refln[''].time_of_flight` | same | `_refln.time_of_flight` | +| `experiment.refln[''].intensity_meas` (SC) | same | `_refln.intensity_meas` | +| `experiment.refln[''].intensity_meas_su` (SC) | same | `_refln.intensity_meas_su` | +| `experiment.refln[''].intensity_calc` (SC) | same | `_refln.intensity_calc` | +| `experiment.refln[''].wavelength` (SC) | same | `_refln.wavelength` | +| `experiment.excluded_regions[''].id` | same | `_excluded_region.id` | +| `experiment.excluded_regions[''].start` | same | `_excluded_region.start` | +| `experiment.excluded_regions[''].end` | same | `_excluded_region.end` | +| `experiment.linked_phases[''].id` | `experiment.linked_structures[''].structure_id` | `_linked_structure.structure_id` | +| `experiment.linked_phases[''].scale` | `experiment.linked_structures[''].scale` | `_linked_structure.scale` | +| `experiment.linked_crystal.id` | `experiment.linked_structure.structure_id` | `_linked_structure.structure_id` | +| `experiment.linked_crystal.scale` | `experiment.linked_structure.scale` | `_linked_structure.scale` | +| `experiment.preferred_orientation.phase_id` | `experiment.preferred_orientation.structure_id` | `_preferred_orientation.structure_id` | +| `experiment.preferred_orientation.march_r` | same | `_preferred_orientation.march_r` | +| `experiment.preferred_orientation.index_h` | same | `_preferred_orientation.index_h` | +| `experiment.preferred_orientation.index_k` | same | `_preferred_orientation.index_k` | +| `experiment.preferred_orientation.index_l` | same | `_preferred_orientation.index_l` | +| `experiment.preferred_orientation.march_random_fract` | same | `_preferred_orientation.march_random_fract` | + +### Analysis + +| Current API | v1.0.0 API | Edi | +| ---------------------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------- | +| `analysis.aliases[''].label` | `analysis.aliases[''].id` | `_alias.id` | +| `analysis.aliases[''].param_unique_name` | `analysis.aliases[''].parameter_unique_name` | `_alias.parameter_unique_name` | +| `analysis.constraints[''].id` | same | `_constraint.id` | +| `analysis.constraints[''].expression` | same | `_constraint.expression` | +| `analysis.fit_parameters[''].param_unique_name` | `analysis.fit_parameters[''].parameter_unique_name` | `_fit_parameter.parameter_unique_name` | +| `analysis.fit_parameters[''].fit_min` | same | `_fit_parameter.fit_min` | +| `analysis.fit_parameters[''].fit_max` | same | `_fit_parameter.fit_max` | +| `analysis.fit_parameters[''].fit_bounds_uncertainty_multiplier` | `analysis.fit_parameters[''].bounds_uncertainty_multiplier` | `_fit_parameter.bounds_uncertainty_multiplier` | +| `analysis.fit_parameters[''].start_value` | same | `_fit_parameter.start_value` | +| `analysis.fit_parameters[''].start_uncertainty` | same | `_fit_parameter.start_uncertainty` | +| `analysis.fit_parameters[''].posterior_best_sample_value` | same | `_fit_parameter.posterior_best_sample_value` | +| `analysis.fit_parameters[''].posterior_median` | same | `_fit_parameter.posterior_median` | +| `analysis.fit_parameters[''].posterior_uncertainty` | same | `_fit_parameter.posterior_uncertainty` | +| `analysis.fit_parameters[''].posterior_interval_68_low` | same | `_fit_parameter.posterior_interval_68_low` | +| `analysis.fit_parameters[''].posterior_interval_68_high` | same | `_fit_parameter.posterior_interval_68_high` | +| `analysis.fit_parameters[''].posterior_interval_95_low` | same | `_fit_parameter.posterior_interval_95_low` | +| `analysis.fit_parameters[''].posterior_interval_95_high` | same | `_fit_parameter.posterior_interval_95_high` | +| `analysis.fit_parameters[''].posterior_gelman_rubin` | same | `_fit_parameter.posterior_gelman_rubin` | +| `analysis.fit_parameters[''].posterior_effective_sample_size_bulk` | same | `_fit_parameter.posterior_effective_sample_size_bulk` | +| `analysis.fit_parameter_correlations[''].id` | same | `_fit_parameter_correlation.id` | +| `analysis.fit_parameter_correlations[''].source_kind` | same | `_fit_parameter_correlation.source_kind` | +| `analysis.fit_parameter_correlations[''].param_unique_name_i` | `…parameter_unique_name_i` | `_fit_parameter_correlation.parameter_unique_name_i` | +| `analysis.fit_parameter_correlations[''].param_unique_name_j` | `…parameter_unique_name_j` | `_fit_parameter_correlation.parameter_unique_name_j` | +| `analysis.fit_parameter_correlations[''].correlation` | same | `_fit_parameter_correlation.correlation` | +| `analysis.fit_result.result_kind` | same | `_fit_result.result_kind` | +| `analysis.fit_result.success` | same | `_fit_result.success` | +| `analysis.fit_result.message` | same | `_fit_result.message` | +| `analysis.fit_result.iterations` | same | `_fit_result.iterations` | +| `analysis.fit_result.fitting_time` | same | `_fit_result.fitting_time` | +| `analysis.fit_result.reduced_chi_square` | same | `_fit_result.reduced_chi_square` | +| `analysis.fit_result.objective_name` | same | `_fit_result.objective_name` | +| `analysis.fit_result.objective_value` | same | `_fit_result.objective_value` | +| `analysis.fit_result.n_data_points` | same | `_fit_result.n_data_points` | +| `analysis.fit_result.n_parameters` | same | `_fit_result.n_parameters` | +| `analysis.fit_result.n_free_parameters` | same | `_fit_result.n_free_parameters` | +| `analysis.fit_result.degrees_of_freedom` | same | `_fit_result.degrees_of_freedom` | +| `analysis.fit_result.covariance_available` | same | `_fit_result.covariance_available` | +| `analysis.fit_result.correlation_available` | same | `_fit_result.correlation_available` | +| `analysis.fit_result.exit_reason` | same | `_fit_result.exit_reason` | +| `analysis.fit_result.r_factor_all` | same | `_fit_result.r_factor_all` | +| `analysis.fit_result.wr_factor_all` | same | `_fit_result.wr_factor_all` | +| `analysis.fit_result.r_factor_gt` | same | `_fit_result.r_factor_gt` | +| `analysis.fit_result.wr_factor_gt` | same | `_fit_result.wr_factor_gt` | +| `analysis.fit_result.prof_r_factor` | same | `_fit_result.prof_r_factor` | +| `analysis.fit_result.prof_wr_factor` | same | `_fit_result.prof_wr_factor` | +| `analysis.fit_result.prof_wr_expected` | same | `_fit_result.prof_wr_expected` | +| `analysis.fit_result.profile_function` | same | `_fit_result.profile_function` | +| `analysis.fit_result.background_function` | same | `_fit_result.background_function` | +| `analysis.fit_result.number_restraints` | same | `_fit_result.number_restraints` | +| `analysis.fit_result.number_constraints` | same | `_fit_result.number_constraints` | +| `analysis.fit_result.shift_over_su_max` | same | `_fit_result.shift_over_su_max` | +| `analysis.fit_result.shift_over_su_mean` | same | `_fit_result.shift_over_su_mean` | +| `analysis.fit_result.threshold_expression` | same | `_fit_result.threshold_expression` | +| `analysis.fit_result.number_reflns_total` | same | `_fit_result.number_reflns_total` | +| `analysis.fit_result.number_reflns_gt` | same | `_fit_result.number_reflns_gt` | +| `analysis.fit_result.point_estimate_name` | same | `_fit_result.point_estimate_name` | +| `analysis.fit_result.sampler_completed` | same | `_fit_result.sampler_completed` | +| `analysis.fit_result.credible_interval_inner` | same | `_fit_result.credible_interval_inner` | +| `analysis.fit_result.credible_interval_outer` | same | `_fit_result.credible_interval_outer` | +| `analysis.fit_result.acceptance_rate_mean` | same | `_fit_result.acceptance_rate_mean` | +| `analysis.fit_result.resolved_random_seed` | same | `_fit_result.resolved_random_seed` | +| `analysis.fit_result.gelman_rubin_max` | same | `_fit_result.gelman_rubin_max` | +| `analysis.fit_result.effective_sample_size_min` | same | `_fit_result.effective_sample_size_min` | +| `analysis.fit_result.best_log_posterior` | same | `_fit_result.best_log_posterior` | +| `analysis.fitting_mode.type` | same | `_fitting_mode.type` | +| `analysis.minimizer.type` | same | `_minimizer.type` | +| `analysis.minimizer.max_iterations` | same | `_minimizer.max_iterations` | +| `analysis.minimizer.sampling_steps` | same | `_minimizer.sampling_steps` | +| `analysis.minimizer.burn_in_steps` | same | `_minimizer.burn_in_steps` | +| `analysis.minimizer.thinning_interval` | same | `_minimizer.thinning_interval` | +| `analysis.minimizer.population_size` | same | `_minimizer.population_size` | +| `analysis.minimizer.parallel_workers` | same | `_minimizer.parallel_workers` | +| `analysis.minimizer.initialization_method` | same | `_minimizer.initialization_method` | +| `analysis.minimizer.random_seed` | same | `_minimizer.random_seed` | +| `analysis.minimizer.proposal_moves` | same | `_minimizer.proposal_moves` | +| `analysis.joint_fit[''].experiment_id` | same | `_joint_fit.experiment_id` | +| `analysis.joint_fit[''].weight` | same | `_joint_fit.weight` | +| `analysis.sequential_fit.data_dir` | same | `_sequential_fit.data_dir` | +| `analysis.sequential_fit.file_pattern` | same | `_sequential_fit.file_pattern` | +| `analysis.sequential_fit.max_workers` | same | `_sequential_fit.max_workers` | +| `analysis.sequential_fit.chunk_size` | same | `_sequential_fit.chunk_size` | +| `analysis.sequential_fit.reverse` | same | `_sequential_fit.reverse` | +| `analysis.sequential_fit_extract[''].id` | same | `_sequential_fit_extract.id` | +| `analysis.sequential_fit_extract[''].target` | same | `_sequential_fit_extract.target` | +| `analysis.sequential_fit_extract[''].pattern` | same | `_sequential_fit_extract.pattern` | +| `analysis.sequential_fit_extract[''].required` | same | `_sequential_fit_extract.required` | +| `analysis.software.framework.name` | `analysis.software['framework'].name` | `_software.name` (`id`=framework) | +| `analysis.software.framework.version` | `analysis.software['framework'].version` | `_software.version` | +| `analysis.software.framework.url` | `analysis.software['framework'].url` | `_software.url` | +| `analysis.software.calculator.{name,version,url}` | `analysis.software['calculator'].{name,version,url}` | `_software.{name,version,url}` (`id`=calculator) | +| `analysis.software.minimizer.{name,version,url}` | `analysis.software['minimizer'].{name,version,url}` | `_software.{name,version,url}` (`id`=minimizer) | +| `analysis.software.timestamp` | `project.metadata.timestamp` | `_metadata.timestamp` | + +### Project + +| Current API | v1.0.0 API | Edi | +| ----------------------------------------- | -------------------------------- | ---------------------------------- | +| `project.info.name` | `project.metadata.name` | `_metadata.name` | +| `project.info.title` | `project.metadata.title` | `_metadata.title` | +| `project.info.description` | `project.metadata.description` | `_metadata.description` | +| `project.info.created` | `project.metadata.created` | `_metadata.created` | +| `project.info.last_modified` | `project.metadata.last_modified` | `_metadata.last_modified` | +| `analysis.software.timestamp` | `project.metadata.timestamp` | `_metadata.timestamp` | +| `project.rendering_plot.type` | same | `_rendering_plot.type` | +| `project.rendering_structure.type` | same | `_rendering_structure.type` | +| `project.rendering_table.type` | same | `_rendering_table.type` | +| `project.report.cif` | same | `_report.cif` | +| `project.report.html` | same | `_report.html` | +| `project.report.tex` | same | `_report.tex` | +| `project.report.pdf` | same | `_report.pdf` | +| `project.report.html_offline` | same | `_report.html_offline` | +| `project.structure_style.atom_view` | same | `_structure_style.atom_view` | +| `project.structure_style.color_scheme` | same | `_structure_style.color_scheme` | +| `project.structure_style.adp_probability` | same | `_structure_style.adp_probability` | +| `project.structure_style.atom_scale` | same | `_structure_style.atom_scale` | +| `project.structure_view.show_labels` | same | `_structure_view.show_labels` | +| `project.structure_view.show_moments` | same | `_structure_view.show_moments` | +| `project.structure_view.range_a_min` | same | `_structure_view.range_a_min` | +| `project.structure_view.range_a_max` | same | `_structure_view.range_a_max` | +| `project.structure_view.range_b_min` | same | `_structure_view.range_b_min` | +| `project.structure_view.range_b_max` | same | `_structure_view.range_b_max` | +| `project.structure_view.range_c_min` | same | `_structure_view.range_c_min` | +| `project.structure_view.range_c_max` | same | `_structure_view.range_c_max` | +| `project.verbosity.fit` | same | `_verbosity.fit` | + +## Documentation: Parameter-Reference Pages + +The user-guide parameter reference must be reworked to reflect the Edi +split between the friendly project format and the strict report CIF. Two +documentation surfaces are in scope: + +- The index page + [`docs/docs/user-guide/parameters.md`](../../../docs/user-guide/parameters.md), + which today renders two-tab content-tab tables + (`=== "How to access in the code"` and + `=== "CIF name for serialization"`). +- The per-category detail pages under `docs/docs/user-guide/parameters/` + (for example `atom_site.md`, `cell.md`, `peak.md`, `background.md`, + `instrument.md`, `space_group.md`, `expt_type.md`, `linked_phases.md`, + `pref_orient.md`), whose section titles are currently the CIF data + names linked directly to the IUCr definition. + +The rework keeps the existing MkDocs Material machinery so the pages +still build to static HTML: `pymdownx.tabbed` content tabs, `attr_list` +for the `:material-*:` icons and the `{:.label-cif}` / +`{:.label-experiment}` chips, reference-style links, and the +`` fences around the link +definitions. Only the table content and the per-parameter titles change, +not the rendering mechanism. + +### Three-Tab Mapping Tables + +Each mapping table in `parameters.md` gains a third tab. The first two +columns (Category, Parameter) stay identical across all three tabs; only +the right-hand column(s) differ: + +1. **"How to access in the code"** (kept) — the public Python access + path in its **v1.0.0** form, with this ADR's renames applied: + `atom_sites['ID'].id` (not `.label`), `linked_structures['ID'].scale` + (not `linked_phases`), `experiment_type.beam_mode` (not `expt_type`), + `instrument.calib_d_to_tof_quadratic` (not `_quad`), and so on. +2. **"Keys in Edi"** (new) — the persisted `.edi` data name taken from + this ADR's Parameter Inventory and Per-Parameter Map, for example + `_atom_site.id`, `_atom_site.adp_iso`, `_cell.length_a`, + `_instrument.setup_wavelength`, `_peak.broad_gauss_sigma_0`, + `_background.position`. This tab has **no** "CIF dictionary" column: + Edi is an EasyDiffraction-owned schema, and the `.edi` suffix plus + the `_edi.schema_*` marker already identify the dialect. +3. **"Keys in CIF"** (new; replaces the old "CIF name for + serialization") — the strict name emitted by + `project.report.save_cif()` into `reports/.cif`, with a "CIF + dictionary" column: + - where an official IUCr/pdCIF name exists, list that name and tag it + `[coreCIF]` or `[pdCIF]` (for example `_atom_site.B_iso_or_equiv` → + coreCIF, `_pd_phase_block.scale` → pdCIF); + - where no official name exists, list the EasyDiffraction extension + name and tag it `[coreCIF]` (for example the parametric `_peak.*` + profile coefficients and `_pref_orient.march_random_fract`). Only + parameters the report writer actually emits appear in this tab; + derived/never-persisted entries (for example `space_group_wyckoff`) + are omitted. + +The crucial change from today is the separation of concerns: the +**project-save** names now live in tab 2 (Edi), and the **official-CIF** +names live in tab 3, explicitly labelled as the _report_ boundary. This +matches the ADR's thesis that project files are Edi and only +`reports/.cif` is strict IUCr. The current page conflates the +two by labelling the old project write tags +(`_pd_background.line_segment_X`, `_instr.wavelength`, +`_atom_site.B_iso_or_equiv`) as "CIF name for serialization". + +Worked example for the `cell` category, in the exact content-tab syntax +to reproduce (note the four-space body indent each tab requires): + +```text +=== "How to access in the code" + + | Category | Parameter | How to access in the code | + |--------------------------------------|-----------------------------------|---------------------------| + | :material-cube-outline: [cell][cell] | :material-ruler: [length_a][cell] | cell.length_a | + +=== "Keys in Edi" + + | Category | Parameter | Edi key | + |--------------------------------------|-----------------------------------|-----------------| + | :material-cube-outline: [cell][cell] | :material-ruler: [length_a][cell] | \_cell.length_a | + +=== "Keys in CIF" + + | Category | Parameter | CIF key | CIF dictionary | + |--------------------------------------|-----------------------------------|-----------------|---------------------------| + | :material-cube-outline: [cell][cell] | :material-ruler: [length_a][cell] | \_cell.length_a | [coreCIF][1]{:.label-cif} | +``` + +### Icons for Every Category and Parameter + +Every category row and every parameter row must carry a `:material-*:` +icon. Fill in icons for any rows that lack one today, and reuse the same +icon for the same concept across the three tabs (the Category and +Parameter columns are shared, so each row's icon is chosen once). New +Edi categories/parameters introduced by the renames — for example +`experiment_type`, `linked_structure(s)`, `preferred_orientation`, +`metadata` — get icons consistent with their nearest existing sibling. + +### Tables Must Track the Code + +The access paths (tab 1) and the Edi keys (tab 2) must equal the +implemented v1.0.0 API and this ADR's Parameter Inventory exactly. The +same generated `CifHandler` inventory used as the migration audit +(Migration Sketch step 4) is the audit source for these hand-maintained +tables: implementation must compare the generated inventory with the +documentation rows before completing the migration, and any mismatch is +a migration blocker. Automatic generation of the docs tables or detail +pages is out of scope for this ADR and may be proposed separately. +Renamed names appear only in their post-migration form; pre-Edi names +survive only as loader read aliases, never in the docs. + +### Per-Parameter Detail Pages Own Edi Names and Descriptions + +The detail pages become EasyDiffraction-owned: + +- **Page and section names use Edi, not CIF.** Each section title is the + Edi data name — `## _atom_site.id`, `## _atom_site.adp_iso`, + `## _instrument.setup_wavelength` — and the body is EasyDiffraction's + own description of that parameter, not a verbatim copy of the IUCr + definition. Pages currently named for CIF categories + (`_exptl_crystal.md`, `_pd_calib.md`, `_diffrn_radiation*.md`, + `_extinction.md`) and for soon-to-be-renamed owners + (`linked_phases.md` → `linked_structure.md`, `pref_orient.md` → + `preferred_orientation.md`, `expt_type.md` → `experiment_type.md`) are + renamed to their Edi category and have their reference links updated. +- **Every category and parameter in the tables links to its detail + section.** Add the pages/anchors that are missing today and update the + existing reference-link definitions to the Edi names. Inter-page + cross-references (for example `_atom_site.fract` pointing at + `_cell_length`) move to the Edi names as well. + +### Versioned Parameter Documentation URLs + +Every persisted descriptor/parameter must expose a read-only `url` +attribute that points to the online documentation page for that specific +parameter. This URL is display metadata only: it is not written into +Edi, and loaders do not trust persisted URLs from project files. + +The simplest long-term rule is to derive URLs from the Edi data name, +not to hand-maintain a separate absolute URL on every descriptor. The +implementation should add a small documentation-url resolver that: + +- uses the same installed-version resolution already used for tutorial + downloads: released packages resolve to their public version folder, + development/local builds resolve to `dev`; +- uses the versioned documentation base published by `mike`, for example + `https://easyscience.github.io/diffraction-lib/{version}/`; +- maps an Edi name to the parameter-reference route, for example + `_cell.length_a` -> + `user-guide/parameters/structure/cell/#cell-length-a`; +- falls back to a descriptor-specific override only for rare cases where + a page or anchor cannot be derived from the Edi name. + +The shared resolver should be the only code that knows the absolute site +base and version folder. `TagSpec.edi_name` (the canonical Edi tag) is +the source value for ordinary descriptors; descriptor classes expose +`param.url` by asking the resolver to build the URL from that project +name. This keeps old installed versions linked to the documentation +version they were tested against, while future docs reorganizations +require changing one resolver or adding redirects rather than editing +every parameter declaration. + +Parameter-reference detail pages must publish stable explicit anchors +derived by the same helper used by the resolver. Do not rely on MkDocs' +implicit heading slug rules as the contract. A docs verification check +must compare the generated descriptor inventory with the parameter docs +and fail if any persisted descriptor lacks a resolvable page/anchor. + +Runtime display surfaces must consume the descriptor `url` instead of +hard-coding links. In notebooks, parameter tables such as +`project.display.parameters.all()` and fittable/free-parameter tables +should link the parameter label to `param.url`. The GUI/application +should use the same attribute for help links, tooltips, or details +panels. + +Static MkDocs parameter tables remain hand-maintained. Their "How to +access in the code" tab should link parameter labels to the stable +relative anchors owned by the same documentation page, not to runtime +`param.url`. The generated inventory/docs check verifies that these +static anchors match the resolver's derived page/anchor contract. + +### IUCr Links Become an Icon, Not the Title + +Today the link to the official IUCr definition is placed on the +section-title text itself (`## [\_atom_site.label](IUCr URL)`) and on a +prose "see the IUCr page" sentence. Under Edi the title is the plain Edi +name, and an explicit external-resource icon follows it, linking to the +official IUCr description **only where one exists**: + +```text +## _atom_site.adp_iso [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.B_iso_or_equiv.html "IUCr definition") +``` + +The icon makes "this opens an external IUCr page" unambiguous, and its +absence makes EasyDiffraction-owned parameters (no official IUCr +definition — most `_peak.*`, `_pref_orient.march_random_fract`, and the +like) equally clear at a glance. The same icon convention replaces the +category-level "IUCr page" prose link. `:material-open-in-new:` renders +through the existing `pymdownx.emoji` (Material) configuration, so no +new extension is required. + +## Migration Sketch + +1. Introduce explicit handler names (`project_name`, `import_names`, + `iucr_name`) while keeping current behavior. +2. Teach save/load helpers to require `.edi` project files and to reject + legacy-only beta `.cif` project layouts with a clear migration error. +3. Add schema-marker validation and selector/body consistency checks to + the Edi load path. +4. Generate an implementation audit from all `CifHandler`-declared + descriptors and compare it with the inventory in this ADR before + changing write tags. +5. Rename project write tags to the suggested Edi names category by + category, preserving current and official tags as read aliases. +6. Add the shared versioned documentation-url resolver and descriptor + `url` property, deriving ordinary parameter URLs from + `TagSpec.edi_name`. +7. Rework the user-guide parameter reference per §Documentation: + Parameter-Reference Pages — three-tab tables ("How to access in the + code", "Keys in Edi", "Keys in CIF"), Edi-named and + EasyDiffraction-described detail pages, stable parameter anchors, + relative links in static docs tables, icons on every + category/parameter row, descriptor `url` links in runtime parameter + tables/application displays, and the IUCr external-link icon — then + update tutorials, ZIP project detection, and CLI help to say Edi + project files and report CIF exports. +8. Keep `project.report.save_cif()` as the only strict report-CIF + writer. + +## Open Questions + +- Whether `.edi` should be exposed as a named public format in CLI + commands or remain an implementation detail of project directories. +- Whether parameter-reference fields in the analysis categories + (`_alias.parameter_unique_name`, + `_fit_parameter.parameter_unique_name`, + `_fit_parameter_correlation.parameter_unique_name_i/j`) should also + adopt the `_id` convention (e.g. `parameter_id`). They + reference a parameter by its descriptive unique path rather than a + datablock `id`, so the path name was kept (only `param`→`parameter` + applied); switching to `parameter_id` is a separate decision. + +## Suggested Pull Request + +Title: Define Edi project persistence + +Description: Clarifies that saved EasyDiffraction projects use a +readable STAR-based schema, while generated report CIFs remain the +strict IUCr-facing export for publication and interoperability. diff --git a/docs/dev/adrs/accepted/edstar-project-persistence/handler-inventory.json b/docs/dev/adrs/accepted/edstar-project-persistence/handler-inventory.json new file mode 100644 index 000000000..8d16eb185 --- /dev/null +++ b/docs/dev/adrs/accepted/edstar-project-persistence/handler-inventory.json @@ -0,0 +1,13724 @@ +{ + "entries": [ + { + "category_code": "alias", + "category_entry_name": "_", + "cif_name": "_easydiffraction_alias.id", + "cif_names": ["_easydiffraction_alias.id", "_alias.label"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "analysis.aliases[].id", + "docs_anchor": "alias-id", + "docs_page": "alias", + "edi_name": "_alias.id", + "edi_names": ["_alias.id"], + "owner_class": "Alias", + "read_names": ["_alias.id", "_easydiffraction_alias.id", "_alias.label"], + "unique_name": "alias._.id" + }, + { + "category_code": "alias", + "category_entry_name": "_", + "cif_name": "_easydiffraction_alias.parameter_unique_name", + "cif_names": [ + "_easydiffraction_alias.parameter_unique_name", + "_alias.param_unique_name" + ], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name", + "descriptor_path": "analysis.aliases[].parameter_unique_name", + "docs_anchor": "alias-parameter-unique-name", + "docs_page": "alias", + "edi_name": "_alias.parameter_unique_name", + "edi_names": ["_alias.parameter_unique_name"], + "owner_class": "Alias", + "read_names": [ + "_alias.parameter_unique_name", + "_easydiffraction_alias.parameter_unique_name", + "_alias.param_unique_name" + ], + "unique_name": "alias._.parameter_unique_name" + }, + { + "category_code": "constraint", + "category_entry_name": "_", + "cif_name": "_easydiffraction_constraint.expression", + "cif_names": ["_easydiffraction_constraint.expression"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "expression", + "descriptor_path": "analysis.constraints[].expression", + "docs_anchor": "constraint-expression", + "docs_page": "constraint", + "edi_name": "_constraint.expression", + "edi_names": ["_constraint.expression"], + "owner_class": "Constraint", + "read_names": [ + "_constraint.expression", + "_easydiffraction_constraint.expression" + ], + "unique_name": "constraint._.expression" + }, + { + "category_code": "constraint", + "category_entry_name": "_", + "cif_name": "_easydiffraction_constraint.id", + "cif_names": ["_easydiffraction_constraint.id"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "analysis.constraints[].id", + "docs_anchor": "constraint-id", + "docs_page": "constraint", + "edi_name": "_constraint.id", + "edi_names": ["_constraint.id"], + "owner_class": "Constraint", + "read_names": ["_constraint.id", "_easydiffraction_constraint.id"], + "unique_name": "constraint._.id" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.correlation", + "cif_names": ["_fit_parameter_correlation.correlation"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "correlation", + "descriptor_path": "analysis.fit_parameter_correlations[].correlation", + "docs_anchor": "fit-parameter-correlation-correlation", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.correlation", + "edi_names": ["_fit_parameter_correlation.correlation"], + "owner_class": "FitParameterCorrelationItem", + "read_names": ["_fit_parameter_correlation.correlation"], + "unique_name": "fit_parameter_correlation._.correlation" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.id", + "cif_names": ["_fit_parameter_correlation.id"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "analysis.fit_parameter_correlations[].id", + "docs_anchor": "fit-parameter-correlation-id", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.id", + "edi_names": ["_fit_parameter_correlation.id"], + "owner_class": "FitParameterCorrelationItem", + "read_names": ["_fit_parameter_correlation.id"], + "unique_name": "fit_parameter_correlation._.id" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.param_unique_name_i", + "cif_names": ["_fit_parameter_correlation.param_unique_name_i"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name_i", + "descriptor_path": "analysis.fit_parameter_correlations[].parameter_unique_name_i", + "docs_anchor": "fit-parameter-correlation-parameter-unique-name-i", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.parameter_unique_name_i", + "edi_names": ["_fit_parameter_correlation.parameter_unique_name_i"], + "owner_class": "FitParameterCorrelationItem", + "read_names": [ + "_fit_parameter_correlation.parameter_unique_name_i", + "_fit_parameter_correlation.param_unique_name_i" + ], + "unique_name": "fit_parameter_correlation._.parameter_unique_name_i" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.param_unique_name_j", + "cif_names": ["_fit_parameter_correlation.param_unique_name_j"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name_j", + "descriptor_path": "analysis.fit_parameter_correlations[].parameter_unique_name_j", + "docs_anchor": "fit-parameter-correlation-parameter-unique-name-j", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.parameter_unique_name_j", + "edi_names": ["_fit_parameter_correlation.parameter_unique_name_j"], + "owner_class": "FitParameterCorrelationItem", + "read_names": [ + "_fit_parameter_correlation.parameter_unique_name_j", + "_fit_parameter_correlation.param_unique_name_j" + ], + "unique_name": "fit_parameter_correlation._.parameter_unique_name_j" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.source_kind", + "cif_names": ["_fit_parameter_correlation.source_kind"], + "context": "analysis", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "source_kind", + "descriptor_path": "analysis.fit_parameter_correlations[].source_kind", + "docs_anchor": "fit-parameter-correlation-source-kind", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.source_kind", + "edi_names": ["_fit_parameter_correlation.source_kind"], + "owner_class": "FitParameterCorrelationItem", + "read_names": ["_fit_parameter_correlation.source_kind"], + "unique_name": "fit_parameter_correlation._.source_kind" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.fit_bounds_uncertainty_multiplier", + "cif_names": ["_fit_parameter.fit_bounds_uncertainty_multiplier"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "bounds_uncertainty_multiplier", + "descriptor_path": "analysis.fit_parameters[].bounds_uncertainty_multiplier", + "docs_anchor": "fit-parameter-bounds-uncertainty-multiplier", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.bounds_uncertainty_multiplier", + "edi_names": ["_fit_parameter.bounds_uncertainty_multiplier"], + "owner_class": "FitParameterItem", + "read_names": [ + "_fit_parameter.bounds_uncertainty_multiplier", + "_fit_parameter.fit_bounds_uncertainty_multiplier" + ], + "unique_name": "fit_parameter._.bounds_uncertainty_multiplier" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.fit_max", + "cif_names": ["_fit_parameter.fit_max"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fit_max", + "descriptor_path": "analysis.fit_parameters[].fit_max", + "docs_anchor": "fit-parameter-fit-max", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.fit_max", + "edi_names": ["_fit_parameter.fit_max"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.fit_max"], + "unique_name": "fit_parameter._.fit_max" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.fit_min", + "cif_names": ["_fit_parameter.fit_min"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fit_min", + "descriptor_path": "analysis.fit_parameters[].fit_min", + "docs_anchor": "fit-parameter-fit-min", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.fit_min", + "edi_names": ["_fit_parameter.fit_min"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.fit_min"], + "unique_name": "fit_parameter._.fit_min" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.param_unique_name", + "cif_names": ["_fit_parameter.param_unique_name"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name", + "descriptor_path": "analysis.fit_parameters[].parameter_unique_name", + "docs_anchor": "fit-parameter-parameter-unique-name", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.parameter_unique_name", + "edi_names": ["_fit_parameter.parameter_unique_name"], + "owner_class": "FitParameterItem", + "read_names": [ + "_fit_parameter.parameter_unique_name", + "_fit_parameter.param_unique_name" + ], + "unique_name": "fit_parameter._.parameter_unique_name" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_best_sample_value", + "cif_names": ["_fit_parameter.posterior_best_sample_value"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_best_sample_value", + "descriptor_path": "analysis.fit_parameters[].posterior_best_sample_value", + "docs_anchor": "fit-parameter-posterior-best-sample-value", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_best_sample_value", + "edi_names": ["_fit_parameter.posterior_best_sample_value"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_best_sample_value"], + "unique_name": "fit_parameter._.posterior_best_sample_value" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_effective_sample_size_bulk", + "cif_names": ["_fit_parameter.posterior_effective_sample_size_bulk"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_effective_sample_size_bulk", + "descriptor_path": "analysis.fit_parameters[].posterior_effective_sample_size_bulk", + "docs_anchor": "fit-parameter-posterior-effective-sample-size-bulk", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_effective_sample_size_bulk", + "edi_names": ["_fit_parameter.posterior_effective_sample_size_bulk"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_effective_sample_size_bulk"], + "unique_name": "fit_parameter._.posterior_effective_sample_size_bulk" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_gelman_rubin", + "cif_names": ["_fit_parameter.posterior_gelman_rubin"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_gelman_rubin", + "descriptor_path": "analysis.fit_parameters[].posterior_gelman_rubin", + "docs_anchor": "fit-parameter-posterior-gelman-rubin", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_gelman_rubin", + "edi_names": ["_fit_parameter.posterior_gelman_rubin"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_gelman_rubin"], + "unique_name": "fit_parameter._.posterior_gelman_rubin" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_68_high", + "cif_names": ["_fit_parameter.posterior_interval_68_high"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_68_high", + "descriptor_path": "analysis.fit_parameters[].posterior_interval_68_high", + "docs_anchor": "fit-parameter-posterior-interval-68-high", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_68_high", + "edi_names": ["_fit_parameter.posterior_interval_68_high"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_68_high"], + "unique_name": "fit_parameter._.posterior_interval_68_high" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_68_low", + "cif_names": ["_fit_parameter.posterior_interval_68_low"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_68_low", + "descriptor_path": "analysis.fit_parameters[].posterior_interval_68_low", + "docs_anchor": "fit-parameter-posterior-interval-68-low", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_68_low", + "edi_names": ["_fit_parameter.posterior_interval_68_low"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_68_low"], + "unique_name": "fit_parameter._.posterior_interval_68_low" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_95_high", + "cif_names": ["_fit_parameter.posterior_interval_95_high"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_95_high", + "descriptor_path": "analysis.fit_parameters[].posterior_interval_95_high", + "docs_anchor": "fit-parameter-posterior-interval-95-high", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_95_high", + "edi_names": ["_fit_parameter.posterior_interval_95_high"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_95_high"], + "unique_name": "fit_parameter._.posterior_interval_95_high" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_95_low", + "cif_names": ["_fit_parameter.posterior_interval_95_low"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_95_low", + "descriptor_path": "analysis.fit_parameters[].posterior_interval_95_low", + "docs_anchor": "fit-parameter-posterior-interval-95-low", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_95_low", + "edi_names": ["_fit_parameter.posterior_interval_95_low"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_95_low"], + "unique_name": "fit_parameter._.posterior_interval_95_low" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_median", + "cif_names": ["_fit_parameter.posterior_median"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_median", + "descriptor_path": "analysis.fit_parameters[].posterior_median", + "docs_anchor": "fit-parameter-posterior-median", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_median", + "edi_names": ["_fit_parameter.posterior_median"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_median"], + "unique_name": "fit_parameter._.posterior_median" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_uncertainty", + "cif_names": ["_fit_parameter.posterior_uncertainty"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_uncertainty", + "descriptor_path": "analysis.fit_parameters[].posterior_uncertainty", + "docs_anchor": "fit-parameter-posterior-uncertainty", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_uncertainty", + "edi_names": ["_fit_parameter.posterior_uncertainty"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_uncertainty"], + "unique_name": "fit_parameter._.posterior_uncertainty" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.start_uncertainty", + "cif_names": ["_fit_parameter.start_uncertainty"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start_uncertainty", + "descriptor_path": "analysis.fit_parameters[].start_uncertainty", + "docs_anchor": "fit-parameter-start-uncertainty", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.start_uncertainty", + "edi_names": ["_fit_parameter.start_uncertainty"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.start_uncertainty"], + "unique_name": "fit_parameter._.start_uncertainty" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.start_value", + "cif_names": ["_fit_parameter.start_value"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start_value", + "descriptor_path": "analysis.fit_parameters[].start_value", + "docs_anchor": "fit-parameter-start-value", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.start_value", + "edi_names": ["_fit_parameter.start_value"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.start_value"], + "unique_name": "fit_parameter._.start_value" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.background_function", + "cif_names": ["_fit_result.background_function"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "background_function", + "descriptor_path": "analysis.fit_result.background_function", + "docs_anchor": "fit-result-background-function", + "docs_page": "fit_result", + "edi_name": "_fit_result.background_function", + "edi_names": ["_fit_result.background_function"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.background_function"], + "unique_name": "fit_result.background_function" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.correlation_available", + "cif_names": ["_fit_result.correlation_available"], + "context": "analysis", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "correlation_available", + "descriptor_path": "analysis.fit_result.correlation_available", + "docs_anchor": "fit-result-correlation-available", + "docs_page": "fit_result", + "edi_name": "_fit_result.correlation_available", + "edi_names": ["_fit_result.correlation_available"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.correlation_available"], + "unique_name": "fit_result.correlation_available" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.covariance_available", + "cif_names": ["_fit_result.covariance_available"], + "context": "analysis", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "covariance_available", + "descriptor_path": "analysis.fit_result.covariance_available", + "docs_anchor": "fit-result-covariance-available", + "docs_page": "fit_result", + "edi_name": "_fit_result.covariance_available", + "edi_names": ["_fit_result.covariance_available"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.covariance_available"], + "unique_name": "fit_result.covariance_available" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.degrees_of_freedom", + "cif_names": ["_fit_result.degrees_of_freedom"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "degrees_of_freedom", + "descriptor_path": "analysis.fit_result.degrees_of_freedom", + "docs_anchor": "fit-result-degrees-of-freedom", + "docs_page": "fit_result", + "edi_name": "_fit_result.degrees_of_freedom", + "edi_names": ["_fit_result.degrees_of_freedom"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.degrees_of_freedom"], + "unique_name": "fit_result.degrees_of_freedom" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.exit_reason", + "cif_names": ["_fit_result.exit_reason"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "exit_reason", + "descriptor_path": "analysis.fit_result.exit_reason", + "docs_anchor": "fit-result-exit-reason", + "docs_page": "fit_result", + "edi_name": "_fit_result.exit_reason", + "edi_names": ["_fit_result.exit_reason"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.exit_reason"], + "unique_name": "fit_result.exit_reason" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.fitting_time", + "cif_names": ["_fit_result.fitting_time"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fitting_time", + "descriptor_path": "analysis.fit_result.fitting_time", + "docs_anchor": "fit-result-fitting-time", + "docs_page": "fit_result", + "edi_name": "_fit_result.fitting_time", + "edi_names": ["_fit_result.fitting_time"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.fitting_time"], + "unique_name": "fit_result.fitting_time" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.iterations", + "cif_names": ["_fit_result.iterations"], + "context": "analysis", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "iterations", + "descriptor_path": "analysis.fit_result.iterations", + "docs_anchor": "fit-result-iterations", + "docs_page": "fit_result", + "edi_name": "_fit_result.iterations", + "edi_names": ["_fit_result.iterations"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.iterations"], + "unique_name": "fit_result.iterations" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.message", + "cif_names": ["_fit_result.message"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "message", + "descriptor_path": "analysis.fit_result.message", + "docs_anchor": "fit-result-message", + "docs_page": "fit_result", + "edi_name": "_fit_result.message", + "edi_names": ["_fit_result.message"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.message"], + "unique_name": "fit_result.message" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.n_data_points", + "cif_names": ["_fit_result.n_data_points"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "n_data_points", + "descriptor_path": "analysis.fit_result.n_data_points", + "docs_anchor": "fit-result-n-data-points", + "docs_page": "fit_result", + "edi_name": "_fit_result.n_data_points", + "edi_names": ["_fit_result.n_data_points"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.n_data_points"], + "unique_name": "fit_result.n_data_points" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.n_free_parameters", + "cif_names": ["_fit_result.n_free_parameters"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "n_free_parameters", + "descriptor_path": "analysis.fit_result.n_free_parameters", + "docs_anchor": "fit-result-n-free-parameters", + "docs_page": "fit_result", + "edi_name": "_fit_result.n_free_parameters", + "edi_names": ["_fit_result.n_free_parameters"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.n_free_parameters"], + "unique_name": "fit_result.n_free_parameters" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.n_parameters", + "cif_names": ["_fit_result.n_parameters"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "n_parameters", + "descriptor_path": "analysis.fit_result.n_parameters", + "docs_anchor": "fit-result-n-parameters", + "docs_page": "fit_result", + "edi_name": "_fit_result.n_parameters", + "edi_names": ["_fit_result.n_parameters"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.n_parameters"], + "unique_name": "fit_result.n_parameters" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_constraints", + "cif_names": ["_fit_result.number_constraints"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_constraints", + "descriptor_path": "analysis.fit_result.number_constraints", + "docs_anchor": "fit-result-number-constraints", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_constraints", + "edi_names": ["_fit_result.number_constraints"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_constraints"], + "unique_name": "fit_result.number_constraints" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_reflns_gt", + "cif_names": ["_fit_result.number_reflns_gt"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_reflns_gt", + "descriptor_path": "analysis.fit_result.number_reflns_gt", + "docs_anchor": "fit-result-number-reflns-gt", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_reflns_gt", + "edi_names": ["_fit_result.number_reflns_gt"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_reflns_gt"], + "unique_name": "fit_result.number_reflns_gt" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_reflns_total", + "cif_names": ["_fit_result.number_reflns_total"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_reflns_total", + "descriptor_path": "analysis.fit_result.number_reflns_total", + "docs_anchor": "fit-result-number-reflns-total", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_reflns_total", + "edi_names": ["_fit_result.number_reflns_total"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_reflns_total"], + "unique_name": "fit_result.number_reflns_total" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_restraints", + "cif_names": ["_fit_result.number_restraints"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_restraints", + "descriptor_path": "analysis.fit_result.number_restraints", + "docs_anchor": "fit-result-number-restraints", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_restraints", + "edi_names": ["_fit_result.number_restraints"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_restraints"], + "unique_name": "fit_result.number_restraints" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.objective_name", + "cif_names": ["_fit_result.objective_name"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "objective_name", + "descriptor_path": "analysis.fit_result.objective_name", + "docs_anchor": "fit-result-objective-name", + "docs_page": "fit_result", + "edi_name": "_fit_result.objective_name", + "edi_names": ["_fit_result.objective_name"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.objective_name"], + "unique_name": "fit_result.objective_name" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.objective_value", + "cif_names": ["_fit_result.objective_value"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "objective_value", + "descriptor_path": "analysis.fit_result.objective_value", + "docs_anchor": "fit-result-objective-value", + "docs_page": "fit_result", + "edi_name": "_fit_result.objective_value", + "edi_names": ["_fit_result.objective_value"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.objective_value"], + "unique_name": "fit_result.objective_value" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.prof_R_factor", + "cif_names": ["_fit_result.prof_R_factor"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "prof_r_factor", + "descriptor_path": "analysis.fit_result.prof_r_factor", + "docs_anchor": "fit-result-prof-r-factor", + "docs_page": "fit_result", + "edi_name": "_fit_result.prof_r_factor", + "edi_names": ["_fit_result.prof_r_factor"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.prof_r_factor", "_fit_result.prof_R_factor"], + "unique_name": "fit_result.prof_r_factor" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.prof_wR_expected", + "cif_names": ["_fit_result.prof_wR_expected"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "prof_wr_expected", + "descriptor_path": "analysis.fit_result.prof_wr_expected", + "docs_anchor": "fit-result-prof-wr-expected", + "docs_page": "fit_result", + "edi_name": "_fit_result.prof_wr_expected", + "edi_names": ["_fit_result.prof_wr_expected"], + "owner_class": "LeastSquaresFitResult", + "read_names": [ + "_fit_result.prof_wr_expected", + "_fit_result.prof_wR_expected" + ], + "unique_name": "fit_result.prof_wr_expected" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.prof_wR_factor", + "cif_names": ["_fit_result.prof_wR_factor"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "prof_wr_factor", + "descriptor_path": "analysis.fit_result.prof_wr_factor", + "docs_anchor": "fit-result-prof-wr-factor", + "docs_page": "fit_result", + "edi_name": "_fit_result.prof_wr_factor", + "edi_names": ["_fit_result.prof_wr_factor"], + "owner_class": "LeastSquaresFitResult", + "read_names": [ + "_fit_result.prof_wr_factor", + "_fit_result.prof_wR_factor" + ], + "unique_name": "fit_result.prof_wr_factor" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.profile_function", + "cif_names": ["_fit_result.profile_function"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "profile_function", + "descriptor_path": "analysis.fit_result.profile_function", + "docs_anchor": "fit-result-profile-function", + "docs_page": "fit_result", + "edi_name": "_fit_result.profile_function", + "edi_names": ["_fit_result.profile_function"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.profile_function"], + "unique_name": "fit_result.profile_function" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.R_factor_all", + "cif_names": ["_fit_result.R_factor_all"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "r_factor_all", + "descriptor_path": "analysis.fit_result.r_factor_all", + "docs_anchor": "fit-result-r-factor-all", + "docs_page": "fit_result", + "edi_name": "_fit_result.r_factor_all", + "edi_names": ["_fit_result.r_factor_all"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.r_factor_all", "_fit_result.R_factor_all"], + "unique_name": "fit_result.r_factor_all" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.R_factor_gt", + "cif_names": ["_fit_result.R_factor_gt"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "r_factor_gt", + "descriptor_path": "analysis.fit_result.r_factor_gt", + "docs_anchor": "fit-result-r-factor-gt", + "docs_page": "fit_result", + "edi_name": "_fit_result.r_factor_gt", + "edi_names": ["_fit_result.r_factor_gt"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.r_factor_gt", "_fit_result.R_factor_gt"], + "unique_name": "fit_result.r_factor_gt" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.reduced_chi_square", + "cif_names": ["_fit_result.reduced_chi_square"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "reduced_chi_square", + "descriptor_path": "analysis.fit_result.reduced_chi_square", + "docs_anchor": "fit-result-reduced-chi-square", + "docs_page": "fit_result", + "edi_name": "_fit_result.reduced_chi_square", + "edi_names": ["_fit_result.reduced_chi_square"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.reduced_chi_square"], + "unique_name": "fit_result.reduced_chi_square" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.result_kind", + "cif_names": ["_fit_result.result_kind"], + "context": "analysis", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "result_kind", + "descriptor_path": "analysis.fit_result.result_kind", + "docs_anchor": "fit-result-result-kind", + "docs_page": "fit_result", + "edi_name": "_fit_result.result_kind", + "edi_names": ["_fit_result.result_kind"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.result_kind"], + "unique_name": "fit_result.result_kind" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.shift_over_su_max", + "cif_names": ["_fit_result.shift_over_su_max"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "shift_over_su_max", + "descriptor_path": "analysis.fit_result.shift_over_su_max", + "docs_anchor": "fit-result-shift-over-su-max", + "docs_page": "fit_result", + "edi_name": "_fit_result.shift_over_su_max", + "edi_names": ["_fit_result.shift_over_su_max"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.shift_over_su_max"], + "unique_name": "fit_result.shift_over_su_max" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.shift_over_su_mean", + "cif_names": ["_fit_result.shift_over_su_mean"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "shift_over_su_mean", + "descriptor_path": "analysis.fit_result.shift_over_su_mean", + "docs_anchor": "fit-result-shift-over-su-mean", + "docs_page": "fit_result", + "edi_name": "_fit_result.shift_over_su_mean", + "edi_names": ["_fit_result.shift_over_su_mean"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.shift_over_su_mean"], + "unique_name": "fit_result.shift_over_su_mean" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.success", + "cif_names": ["_fit_result.success"], + "context": "analysis", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "success", + "descriptor_path": "analysis.fit_result.success", + "docs_anchor": "fit-result-success", + "docs_page": "fit_result", + "edi_name": "_fit_result.success", + "edi_names": ["_fit_result.success"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.success"], + "unique_name": "fit_result.success" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.threshold_expression", + "cif_names": ["_fit_result.threshold_expression"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "threshold_expression", + "descriptor_path": "analysis.fit_result.threshold_expression", + "docs_anchor": "fit-result-threshold-expression", + "docs_page": "fit_result", + "edi_name": "_fit_result.threshold_expression", + "edi_names": ["_fit_result.threshold_expression"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.threshold_expression"], + "unique_name": "fit_result.threshold_expression" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.wR_factor_all", + "cif_names": ["_fit_result.wR_factor_all"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "wr_factor_all", + "descriptor_path": "analysis.fit_result.wr_factor_all", + "docs_anchor": "fit-result-wr-factor-all", + "docs_page": "fit_result", + "edi_name": "_fit_result.wr_factor_all", + "edi_names": ["_fit_result.wr_factor_all"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.wr_factor_all", "_fit_result.wR_factor_all"], + "unique_name": "fit_result.wr_factor_all" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.wR_factor_gt", + "cif_names": ["_fit_result.wR_factor_gt"], + "context": "analysis", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "wr_factor_gt", + "descriptor_path": "analysis.fit_result.wr_factor_gt", + "docs_anchor": "fit-result-wr-factor-gt", + "docs_page": "fit_result", + "edi_name": "_fit_result.wr_factor_gt", + "edi_names": ["_fit_result.wr_factor_gt"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.wr_factor_gt", "_fit_result.wR_factor_gt"], + "unique_name": "fit_result.wr_factor_gt" + }, + { + "category_code": "fitting_mode", + "category_entry_name": null, + "cif_name": "_easydiffraction_fitting_mode.type", + "cif_names": ["_easydiffraction_fitting_mode.type"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "analysis.fitting_mode.type", + "docs_anchor": "fitting-mode-type", + "docs_page": "fitting_mode", + "edi_name": "_fitting_mode.type", + "edi_names": ["_fitting_mode.type"], + "owner_class": "FittingMode", + "read_names": [ + "_fitting_mode.type", + "_easydiffraction_fitting_mode.type" + ], + "unique_name": "fitting_mode.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "analysis", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "analysis.minimizer.max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "LmfitLeastsqMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "analysis.minimizer.type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "LmfitLeastsqMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.chunk_size", + "cif_names": ["_easydiffraction_sequential_fit.chunk_size"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "chunk_size", + "descriptor_path": "analysis.sequential_fit.chunk_size", + "docs_anchor": "sequential-fit-chunk-size", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.chunk_size", + "edi_names": ["_sequential_fit.chunk_size"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.chunk_size", + "_easydiffraction_sequential_fit.chunk_size" + ], + "unique_name": "sequential_fit.chunk_size" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.data_dir", + "cif_names": ["_easydiffraction_sequential_fit.data_dir"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "data_dir", + "descriptor_path": "analysis.sequential_fit.data_dir", + "docs_anchor": "sequential-fit-data-dir", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.data_dir", + "edi_names": ["_sequential_fit.data_dir"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.data_dir", + "_easydiffraction_sequential_fit.data_dir" + ], + "unique_name": "sequential_fit.data_dir" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.file_pattern", + "cif_names": ["_easydiffraction_sequential_fit.file_pattern"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "file_pattern", + "descriptor_path": "analysis.sequential_fit.file_pattern", + "docs_anchor": "sequential-fit-file-pattern", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.file_pattern", + "edi_names": ["_sequential_fit.file_pattern"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.file_pattern", + "_easydiffraction_sequential_fit.file_pattern" + ], + "unique_name": "sequential_fit.file_pattern" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.max_workers", + "cif_names": ["_easydiffraction_sequential_fit.max_workers"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "max_workers", + "descriptor_path": "analysis.sequential_fit.max_workers", + "docs_anchor": "sequential-fit-max-workers", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.max_workers", + "edi_names": ["_sequential_fit.max_workers"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.max_workers", + "_easydiffraction_sequential_fit.max_workers" + ], + "unique_name": "sequential_fit.max_workers" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.reverse", + "cif_names": ["_easydiffraction_sequential_fit.reverse"], + "context": "analysis", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "reverse", + "descriptor_path": "analysis.sequential_fit.reverse", + "docs_anchor": "sequential-fit-reverse", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.reverse", + "edi_names": ["_sequential_fit.reverse"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.reverse", + "_easydiffraction_sequential_fit.reverse" + ], + "unique_name": "sequential_fit.reverse" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.id", + "cif_names": ["_software.id"], + "context": "analysis", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "id", + "descriptor_path": "analysis.software[].id", + "docs_anchor": "software-id", + "docs_page": "software", + "edi_name": "_software.id", + "edi_names": ["_software.id"], + "owner_class": "SoftwareRole", + "read_names": ["_software.id"], + "unique_name": "software.framework.id" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.name", + "cif_names": ["_software.name"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "name", + "descriptor_path": "analysis.software[].name", + "docs_anchor": "software-name", + "docs_page": "software", + "edi_name": "_software.name", + "edi_names": ["_software.name"], + "owner_class": "SoftwareRole", + "read_names": ["_software.name"], + "unique_name": "software.framework.name" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.url", + "cif_names": ["_software.url"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "url", + "descriptor_path": "analysis.software[].url", + "docs_anchor": "software-url", + "docs_page": "software", + "edi_name": "_software.url", + "edi_names": ["_software.url"], + "owner_class": "SoftwareRole", + "read_names": ["_software.url"], + "unique_name": "software.framework.url" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.version", + "cif_names": ["_software.version"], + "context": "analysis", + "descriptor_class": "StringDescriptor", + "descriptor_name": "version", + "descriptor_path": "analysis.software[].version", + "docs_anchor": "software-version", + "docs_page": "software", + "edi_name": "_software.version", + "edi_names": ["_software.version"], + "owner_class": "SoftwareRole", + "read_names": ["_software.version"], + "unique_name": "software.framework.version" + }, + { + "category_code": null, + "category_entry_name": null, + "cif_name": "_easydiffraction_background.type", + "cif_names": ["_easydiffraction_background.type"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_pd_cwl.background.type", + "docs_anchor": "background-type", + "docs_page": "background", + "edi_name": "_background.type", + "edi_names": ["_background.type"], + "owner_class": "LineSegmentBackground", + "read_names": ["_background.type", "_easydiffraction_background.type"], + "unique_name": "type" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.id", + "cif_names": ["_pd_background.id"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_pd_cwl.background[].id", + "docs_anchor": "background-id", + "docs_page": "background", + "edi_name": "_background.id", + "edi_names": ["_background.id"], + "owner_class": "LineSegment", + "read_names": ["_background.id", "_pd_background.id"], + "unique_name": "background.0.id" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.line_segment_intensity", + "cif_names": [ + "_pd_background.line_segment_intensity", + "_pd_background_line_segment_intensity" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "intensity", + "descriptor_path": "experiment.bragg_pd_cwl.background[].intensity", + "docs_anchor": "background-intensity", + "docs_page": "background", + "edi_name": "_background.intensity", + "edi_names": ["_background.intensity"], + "owner_class": "LineSegment", + "read_names": [ + "_background.intensity", + "_pd_background.line_segment_intensity", + "_pd_background_line_segment_intensity" + ], + "unique_name": "background.0.intensity" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.line_segment_X", + "cif_names": [ + "_pd_background.line_segment_X", + "_pd_background_line_segment_X" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "position", + "descriptor_path": "experiment.bragg_pd_cwl.background[].position", + "docs_anchor": "background-position", + "docs_page": "background", + "edi_name": "_background.position", + "edi_names": ["_background.position"], + "owner_class": "LineSegment", + "read_names": [ + "_background.position", + "_pd_background.line_segment_X", + "_pd_background_line_segment_X" + ], + "unique_name": "background.0.position" + }, + { + "category_code": "calculator", + "category_entry_name": null, + "cif_name": "_easydiffraction_calculator.type", + "cif_names": ["_easydiffraction_calculator.type"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_pd_cwl.calculator.type", + "docs_anchor": "calculator-type", + "docs_page": "calculator", + "edi_name": "_calculator.type", + "edi_names": ["_calculator.type"], + "owner_class": "Calculator", + "read_names": ["_calculator.type", "_easydiffraction_calculator.type"], + "unique_name": "inventory_experiment.calculator.type" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.refinement_status", + "cif_names": ["_pd_data.refinement_status"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "calc_status", + "descriptor_path": "experiment.bragg_pd_cwl.data[].calc_status", + "docs_anchor": "data-calc-status", + "docs_page": "data", + "edi_name": "_data.calc_status", + "edi_names": ["_data.calc_status"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.calc_status", "_pd_data.refinement_status"], + "unique_name": "data.0.calc_status" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.d_spacing", + "cif_names": ["_pd_proc.d_spacing"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "experiment.bragg_pd_cwl.data[].d_spacing", + "docs_anchor": "data-d-spacing", + "docs_page": "data", + "edi_name": "_data.d_spacing", + "edi_names": ["_data.d_spacing"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.d_spacing", "_pd_proc.d_spacing"], + "unique_name": "data.0.d_spacing" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.point_id", + "cif_names": ["_pd_data.point_id"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_pd_cwl.data[].id", + "docs_anchor": "data-id", + "docs_page": "data", + "edi_name": "_data.id", + "edi_names": ["_data.id"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.id", "_pd_data.point_id"], + "unique_name": "data.0.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_bkg", + "cif_names": ["_pd_calc.intensity_bkg"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_bkg", + "descriptor_path": "experiment.bragg_pd_cwl.data[].intensity_bkg", + "docs_anchor": "data-intensity-bkg", + "docs_page": "data", + "edi_name": "_data.intensity_bkg", + "edi_names": ["_data.intensity_bkg"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.intensity_bkg", "_pd_calc.intensity_bkg"], + "unique_name": "data.0.intensity_bkg" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_total", + "cif_names": ["_pd_calc.intensity_total"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "experiment.bragg_pd_cwl.data[].intensity_calc", + "docs_anchor": "data-intensity-calc", + "docs_page": "data", + "edi_name": "_data.intensity_calc", + "edi_names": ["_data.intensity_calc"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.intensity_calc", "_pd_calc.intensity_total"], + "unique_name": "data.0.intensity_calc" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total", + "cif_names": ["_pd_meas.intensity_total", "_pd_proc.intensity_norm"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "experiment.bragg_pd_cwl.data[].intensity_meas", + "docs_anchor": "data-intensity-meas", + "docs_page": "data", + "edi_name": "_data.intensity_meas", + "edi_names": ["_data.intensity_meas"], + "owner_class": "PdCwlDataPoint", + "read_names": [ + "_data.intensity_meas", + "_pd_meas.intensity_total", + "_pd_proc.intensity_norm" + ], + "unique_name": "data.0.intensity_meas" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total_su", + "cif_names": [ + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "experiment.bragg_pd_cwl.data[].intensity_meas_su", + "docs_anchor": "data-intensity-meas-su", + "docs_page": "data", + "edi_name": "_data.intensity_meas_su", + "edi_names": ["_data.intensity_meas_su"], + "owner_class": "PdCwlDataPoint", + "read_names": [ + "_data.intensity_meas_su", + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "unique_name": "data.0.intensity_meas_su" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.2theta_scan", + "cif_names": ["_pd_proc.2theta_scan", "_pd_meas.2theta_scan"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta", + "descriptor_path": "experiment.bragg_pd_cwl.data[].two_theta", + "docs_anchor": "data-two-theta", + "docs_page": "data", + "edi_name": "_data.two_theta", + "edi_names": ["_data.two_theta"], + "owner_class": "PdCwlDataPoint", + "read_names": [ + "_data.two_theta", + "_pd_proc.2theta_scan", + "_pd_meas.2theta_scan" + ], + "unique_name": "data.0.two_theta" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_inc", + "cif_names": ["_pd_meas.2theta_range_inc"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_inc", + "descriptor_path": "experiment.bragg_pd_cwl.data_range.two_theta_inc", + "docs_anchor": "data-range-two-theta-inc", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_inc", + "edi_names": ["_data_range.two_theta_inc"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_inc", "_pd_meas.2theta_range_inc"], + "unique_name": "inventory_experiment.data_range.two_theta_inc" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_max", + "cif_names": ["_pd_meas.2theta_range_max"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_max", + "descriptor_path": "experiment.bragg_pd_cwl.data_range.two_theta_max", + "docs_anchor": "data-range-two-theta-max", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_max", + "edi_names": ["_data_range.two_theta_max"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_max", "_pd_meas.2theta_range_max"], + "unique_name": "inventory_experiment.data_range.two_theta_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_min", + "cif_names": ["_pd_meas.2theta_range_min"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_min", + "descriptor_path": "experiment.bragg_pd_cwl.data_range.two_theta_min", + "docs_anchor": "data-range-two-theta-min", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_min", + "edi_names": ["_data_range.two_theta_min"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_min", "_pd_meas.2theta_range_min"], + "unique_name": "inventory_experiment.data_range.two_theta_min" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_electric_field", + "cif_names": ["_easydiffraction_diffrn.ambient_electric_field"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_electric_field", + "descriptor_path": "experiment.bragg_pd_cwl.diffrn.ambient_electric_field", + "docs_anchor": "diffrn-ambient-electric-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_electric_field", + "edi_names": ["_diffrn.ambient_electric_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_electric_field", + "_easydiffraction_diffrn.ambient_electric_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_electric_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_magnetic_field", + "cif_names": ["_easydiffraction_diffrn.ambient_magnetic_field"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_magnetic_field", + "descriptor_path": "experiment.bragg_pd_cwl.diffrn.ambient_magnetic_field", + "docs_anchor": "diffrn-ambient-magnetic-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_magnetic_field", + "edi_names": ["_diffrn.ambient_magnetic_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_magnetic_field", + "_easydiffraction_diffrn.ambient_magnetic_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_magnetic_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_pressure", + "cif_names": ["_diffrn.ambient_pressure"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_pressure", + "descriptor_path": "experiment.bragg_pd_cwl.diffrn.ambient_pressure", + "docs_anchor": "diffrn-ambient-pressure", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_pressure", + "edi_names": ["_diffrn.ambient_pressure"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_pressure"], + "unique_name": "inventory_experiment.diffrn.ambient_pressure" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_temperature", + "cif_names": ["_diffrn.ambient_temperature"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_temperature", + "descriptor_path": "experiment.bragg_pd_cwl.diffrn.ambient_temperature", + "docs_anchor": "diffrn-ambient-temperature", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_temperature", + "edi_names": ["_diffrn.ambient_temperature"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_temperature"], + "unique_name": "inventory_experiment.diffrn.ambient_temperature" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.end", + "cif_names": ["_easydiffraction_excluded_region.end"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "end", + "descriptor_path": "experiment.bragg_pd_cwl.excluded_regions[].end", + "docs_anchor": "excluded-region-end", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.end", + "edi_names": ["_excluded_region.end"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.end", + "_easydiffraction_excluded_region.end" + ], + "unique_name": "excluded_regions.0.end" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.id", + "cif_names": ["_easydiffraction_excluded_region.id"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_pd_cwl.excluded_regions[].id", + "docs_anchor": "excluded-region-id", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.id", + "edi_names": ["_excluded_region.id"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.id", + "_easydiffraction_excluded_region.id" + ], + "unique_name": "excluded_regions.0.id" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.start", + "cif_names": ["_easydiffraction_excluded_region.start"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start", + "descriptor_path": "experiment.bragg_pd_cwl.excluded_regions[].start", + "docs_anchor": "excluded-region-start", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.start", + "edi_names": ["_excluded_region.start"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.start", + "_easydiffraction_excluded_region.start" + ], + "unique_name": "excluded_regions.0.start" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.beam_mode", + "cif_names": [ + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "beam_mode", + "descriptor_path": "experiment.bragg_pd_cwl.experiment_type.beam_mode", + "docs_anchor": "experiment-type-beam-mode", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.beam_mode", + "edi_names": ["_experiment_type.beam_mode"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.beam_mode", + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "unique_name": "inventory_experiment.experiment_type.beam_mode" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.radiation_probe", + "cif_names": [ + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "radiation_probe", + "descriptor_path": "experiment.bragg_pd_cwl.experiment_type.radiation_probe", + "docs_anchor": "experiment-type-radiation-probe", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.radiation_probe", + "edi_names": ["_experiment_type.radiation_probe"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.radiation_probe", + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "unique_name": "inventory_experiment.experiment_type.radiation_probe" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.sample_form", + "cif_names": [ + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "sample_form", + "descriptor_path": "experiment.bragg_pd_cwl.experiment_type.sample_form", + "docs_anchor": "experiment-type-sample-form", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.sample_form", + "edi_names": ["_experiment_type.sample_form"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.sample_form", + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "unique_name": "inventory_experiment.experiment_type.sample_form" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.scattering_type", + "cif_names": [ + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "scattering_type", + "descriptor_path": "experiment.bragg_pd_cwl.experiment_type.scattering_type", + "docs_anchor": "experiment-type-scattering-type", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.scattering_type", + "edi_names": ["_experiment_type.scattering_type"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.scattering_type", + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "unique_name": "inventory_experiment.experiment_type.scattering_type" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.sample_displacement", + "cif_names": ["_instr.sample_displacement"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "sample_displacement", + "descriptor_path": "experiment.bragg_pd_cwl.instrument.calib_sample_displacement", + "docs_anchor": "instrument-calib-sample-displacement", + "docs_page": "instrument", + "edi_name": "_instrument.calib_sample_displacement", + "edi_names": ["_instrument.calib_sample_displacement"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.calib_sample_displacement", + "_instr.sample_displacement" + ], + "unique_name": "inventory_experiment.instrument.sample_displacement" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.sample_transparency", + "cif_names": ["_instr.sample_transparency"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "sample_transparency", + "descriptor_path": "experiment.bragg_pd_cwl.instrument.calib_sample_transparency", + "docs_anchor": "instrument-calib-sample-transparency", + "docs_page": "instrument", + "edi_name": "_instrument.calib_sample_transparency", + "edi_names": ["_instrument.calib_sample_transparency"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.calib_sample_transparency", + "_instr.sample_transparency" + ], + "unique_name": "inventory_experiment.instrument.sample_transparency" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_pd_calib.2theta_offset", + "cif_names": ["_pd_calib.2theta_offset", "_instr.2theta_offset"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "twotheta_offset", + "descriptor_path": "experiment.bragg_pd_cwl.instrument.calib_twotheta_offset", + "docs_anchor": "instrument-calib-twotheta-offset", + "docs_page": "instrument", + "edi_name": "_instrument.calib_twotheta_offset", + "edi_names": ["_instrument.calib_twotheta_offset"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.calib_twotheta_offset", + "_pd_calib.2theta_offset", + "_instr.2theta_offset" + ], + "unique_name": "inventory_experiment.instrument.twotheta_offset" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_diffrn_radiation_wavelength.value", + "cif_names": ["_diffrn_radiation_wavelength.value", "_instr.wavelength"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "wavelength", + "descriptor_path": "experiment.bragg_pd_cwl.instrument.setup_wavelength", + "docs_anchor": "instrument-setup-wavelength", + "docs_page": "instrument", + "edi_name": "_instrument.setup_wavelength", + "edi_names": ["_instrument.setup_wavelength"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.setup_wavelength", + "_diffrn_radiation_wavelength.value", + "_instr.wavelength" + ], + "unique_name": "inventory_experiment.instrument.wavelength" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.scale", + "cif_names": ["_pd_phase_block.scale"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "experiment.bragg_pd_cwl.linked_structures[].scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.scale", "_pd_phase_block.scale"], + "unique_name": "linked_structure.Si.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.id", + "cif_names": ["_pd_phase_block.id"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_pd_cwl.linked_structures[].structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.structure_id", "_pd_phase_block.id"], + "unique_name": "linked_structure.Si.structure_id" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_u", + "cif_names": ["_easydiffraction_peak.broad_gauss_u"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_u", + "descriptor_path": "experiment.bragg_pd_cwl.peak.broad_gauss_u", + "docs_anchor": "peak-broad-gauss-u", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_u", + "edi_names": ["_peak.broad_gauss_u"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_u", + "_easydiffraction_peak.broad_gauss_u" + ], + "unique_name": "inventory_experiment.peak.broad_gauss_u" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_v", + "cif_names": ["_easydiffraction_peak.broad_gauss_v"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_v", + "descriptor_path": "experiment.bragg_pd_cwl.peak.broad_gauss_v", + "docs_anchor": "peak-broad-gauss-v", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_v", + "edi_names": ["_peak.broad_gauss_v"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_v", + "_easydiffraction_peak.broad_gauss_v" + ], + "unique_name": "inventory_experiment.peak.broad_gauss_v" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_w", + "cif_names": ["_easydiffraction_peak.broad_gauss_w"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_w", + "descriptor_path": "experiment.bragg_pd_cwl.peak.broad_gauss_w", + "docs_anchor": "peak-broad-gauss-w", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_w", + "edi_names": ["_peak.broad_gauss_w"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_w", + "_easydiffraction_peak.broad_gauss_w" + ], + "unique_name": "inventory_experiment.peak.broad_gauss_w" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_x", + "cif_names": ["_easydiffraction_peak.broad_lorentz_x"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_x", + "descriptor_path": "experiment.bragg_pd_cwl.peak.broad_lorentz_x", + "docs_anchor": "peak-broad-lorentz-x", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_x", + "edi_names": ["_peak.broad_lorentz_x"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_lorentz_x", + "_easydiffraction_peak.broad_lorentz_x" + ], + "unique_name": "inventory_experiment.peak.broad_lorentz_x" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_y", + "cif_names": ["_easydiffraction_peak.broad_lorentz_y"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_y", + "descriptor_path": "experiment.bragg_pd_cwl.peak.broad_lorentz_y", + "docs_anchor": "peak-broad-lorentz-y", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_y", + "edi_names": ["_peak.broad_lorentz_y"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_lorentz_y", + "_easydiffraction_peak.broad_lorentz_y" + ], + "unique_name": "inventory_experiment.peak.broad_lorentz_y" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_pd_cwl.peak.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "CwlPseudoVoigt", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "inventory_experiment.peak.type" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_h", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_h", + "_pref_orient.index_h" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "experiment.bragg_pd_cwl.pref_orient[].index_h", + "docs_anchor": "preferred-orientation-index-h", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_h", + "edi_names": ["_preferred_orientation.index_h"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_h", + "_pd_pref_orient_March_Dollase.index_h", + "_pref_orient.index_h" + ], + "unique_name": "preferred_orientation.Si.index_h" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_k", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_k", + "_pref_orient.index_k" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "experiment.bragg_pd_cwl.pref_orient[].index_k", + "docs_anchor": "preferred-orientation-index-k", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_k", + "edi_names": ["_preferred_orientation.index_k"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_k", + "_pd_pref_orient_March_Dollase.index_k", + "_pref_orient.index_k" + ], + "unique_name": "preferred_orientation.Si.index_k" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_l", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_l", + "_pref_orient.index_l" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "experiment.bragg_pd_cwl.pref_orient[].index_l", + "docs_anchor": "preferred-orientation-index-l", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_l", + "edi_names": ["_preferred_orientation.index_l"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_l", + "_pd_pref_orient_March_Dollase.index_l", + "_pref_orient.index_l" + ], + "unique_name": "preferred_orientation.Si.index_l" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.r", + "cif_names": ["_pd_pref_orient_March_Dollase.r", "_pref_orient.march_r"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "march_r", + "descriptor_path": "experiment.bragg_pd_cwl.pref_orient[].march_r", + "docs_anchor": "preferred-orientation-march-r", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.march_r", + "edi_names": ["_preferred_orientation.march_r"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.march_r", + "_pd_pref_orient_March_Dollase.r", + "_pref_orient.march_r" + ], + "unique_name": "preferred_orientation.Si.march_r" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_easydiffraction_pref_orient.march_random_fract", + "cif_names": [ + "_easydiffraction_pref_orient.march_random_fract", + "_pref_orient.march_random_fract" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "march_random_fract", + "descriptor_path": "experiment.bragg_pd_cwl.pref_orient[].march_random_fract", + "docs_anchor": "preferred-orientation-march-random-fract", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.march_random_fract", + "edi_names": ["_preferred_orientation.march_random_fract"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.march_random_fract", + "_easydiffraction_pref_orient.march_random_fract", + "_pref_orient.march_random_fract" + ], + "unique_name": "preferred_orientation.Si.march_random_fract" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.phase_id", + "cif_names": [ + "_pd_pref_orient_March_Dollase.phase_id", + "_pref_orient.phase_id" + ], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_pd_cwl.pref_orient[].structure_id", + "docs_anchor": "preferred-orientation-structure-id", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.structure_id", + "edi_names": ["_preferred_orientation.structure_id"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.structure_id", + "_pd_pref_orient_March_Dollase.phase_id", + "_pref_orient.phase_id" + ], + "unique_name": "preferred_orientation.Si.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_calc", + "cif_names": ["_refln.f_calc"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_calc", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].f_calc", + "docs_anchor": "refln-f-calc", + "docs_page": "refln", + "edi_name": "_refln.f_calc", + "edi_names": ["_refln.f_calc"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.f_calc"], + "unique_name": "refln.0.f_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_squared_calc", + "cif_names": ["_refln.f_squared_calc"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_squared_calc", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].f_squared_calc", + "docs_anchor": "refln-f-squared-calc", + "docs_page": "refln", + "edi_name": "_refln.f_squared_calc", + "edi_names": ["_refln.f_squared_calc"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.f_squared_calc"], + "unique_name": "refln.0.f_squared_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_pd_refln.phase_id", + "cif_names": ["_pd_refln.phase_id", "_refln.phase_id"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].structure_id", + "docs_anchor": "refln-structure-id", + "docs_page": "refln", + "edi_name": "_refln.structure_id", + "edi_names": ["_refln.structure_id"], + "owner_class": "PowderCwlRefln", + "read_names": [ + "_refln.structure_id", + "_pd_refln.phase_id", + "_refln.phase_id" + ], + "unique_name": "refln.0.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.two_theta", + "cif_names": ["_refln.two_theta"], + "context": "experiment.bragg_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta", + "descriptor_path": "experiment.bragg_pd_cwl.refln[].two_theta", + "docs_anchor": "refln-two-theta", + "docs_page": "refln", + "edi_name": "_refln.two_theta", + "edi_names": ["_refln.two_theta"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.two_theta"], + "unique_name": "refln.0.two_theta" + }, + { + "category_code": null, + "category_entry_name": null, + "cif_name": "_easydiffraction_background.type", + "cif_names": ["_easydiffraction_background.type"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_pd_tof.background.type", + "docs_anchor": "background-type", + "docs_page": "background", + "edi_name": "_background.type", + "edi_names": ["_background.type"], + "owner_class": "LineSegmentBackground", + "read_names": ["_background.type", "_easydiffraction_background.type"], + "unique_name": "type" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.id", + "cif_names": ["_pd_background.id"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_pd_tof.background[].id", + "docs_anchor": "background-id", + "docs_page": "background", + "edi_name": "_background.id", + "edi_names": ["_background.id"], + "owner_class": "LineSegment", + "read_names": ["_background.id", "_pd_background.id"], + "unique_name": "background.0.id" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.line_segment_intensity", + "cif_names": [ + "_pd_background.line_segment_intensity", + "_pd_background_line_segment_intensity" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "intensity", + "descriptor_path": "experiment.bragg_pd_tof.background[].intensity", + "docs_anchor": "background-intensity", + "docs_page": "background", + "edi_name": "_background.intensity", + "edi_names": ["_background.intensity"], + "owner_class": "LineSegment", + "read_names": [ + "_background.intensity", + "_pd_background.line_segment_intensity", + "_pd_background_line_segment_intensity" + ], + "unique_name": "background.0.intensity" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.line_segment_X", + "cif_names": [ + "_pd_background.line_segment_X", + "_pd_background_line_segment_X" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "position", + "descriptor_path": "experiment.bragg_pd_tof.background[].position", + "docs_anchor": "background-position", + "docs_page": "background", + "edi_name": "_background.position", + "edi_names": ["_background.position"], + "owner_class": "LineSegment", + "read_names": [ + "_background.position", + "_pd_background.line_segment_X", + "_pd_background_line_segment_X" + ], + "unique_name": "background.0.position" + }, + { + "category_code": "calculator", + "category_entry_name": null, + "cif_name": "_easydiffraction_calculator.type", + "cif_names": ["_easydiffraction_calculator.type"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_pd_tof.calculator.type", + "docs_anchor": "calculator-type", + "docs_page": "calculator", + "edi_name": "_calculator.type", + "edi_names": ["_calculator.type"], + "owner_class": "Calculator", + "read_names": ["_calculator.type", "_easydiffraction_calculator.type"], + "unique_name": "inventory_experiment.calculator.type" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.refinement_status", + "cif_names": ["_pd_data.refinement_status"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "calc_status", + "descriptor_path": "experiment.bragg_pd_tof.data[].calc_status", + "docs_anchor": "data-calc-status", + "docs_page": "data", + "edi_name": "_data.calc_status", + "edi_names": ["_data.calc_status"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.calc_status", "_pd_data.refinement_status"], + "unique_name": "data.0.calc_status" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.d_spacing", + "cif_names": ["_pd_proc.d_spacing"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "experiment.bragg_pd_tof.data[].d_spacing", + "docs_anchor": "data-d-spacing", + "docs_page": "data", + "edi_name": "_data.d_spacing", + "edi_names": ["_data.d_spacing"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.d_spacing", "_pd_proc.d_spacing"], + "unique_name": "data.0.d_spacing" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.point_id", + "cif_names": ["_pd_data.point_id"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_pd_tof.data[].id", + "docs_anchor": "data-id", + "docs_page": "data", + "edi_name": "_data.id", + "edi_names": ["_data.id"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.id", "_pd_data.point_id"], + "unique_name": "data.0.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_bkg", + "cif_names": ["_pd_calc.intensity_bkg"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_bkg", + "descriptor_path": "experiment.bragg_pd_tof.data[].intensity_bkg", + "docs_anchor": "data-intensity-bkg", + "docs_page": "data", + "edi_name": "_data.intensity_bkg", + "edi_names": ["_data.intensity_bkg"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.intensity_bkg", "_pd_calc.intensity_bkg"], + "unique_name": "data.0.intensity_bkg" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_total", + "cif_names": ["_pd_calc.intensity_total"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "experiment.bragg_pd_tof.data[].intensity_calc", + "docs_anchor": "data-intensity-calc", + "docs_page": "data", + "edi_name": "_data.intensity_calc", + "edi_names": ["_data.intensity_calc"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.intensity_calc", "_pd_calc.intensity_total"], + "unique_name": "data.0.intensity_calc" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total", + "cif_names": ["_pd_meas.intensity_total", "_pd_proc.intensity_norm"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "experiment.bragg_pd_tof.data[].intensity_meas", + "docs_anchor": "data-intensity-meas", + "docs_page": "data", + "edi_name": "_data.intensity_meas", + "edi_names": ["_data.intensity_meas"], + "owner_class": "PdTofDataPoint", + "read_names": [ + "_data.intensity_meas", + "_pd_meas.intensity_total", + "_pd_proc.intensity_norm" + ], + "unique_name": "data.0.intensity_meas" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total_su", + "cif_names": [ + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "experiment.bragg_pd_tof.data[].intensity_meas_su", + "docs_anchor": "data-intensity-meas-su", + "docs_page": "data", + "edi_name": "_data.intensity_meas_su", + "edi_names": ["_data.intensity_meas_su"], + "owner_class": "PdTofDataPoint", + "read_names": [ + "_data.intensity_meas_su", + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "unique_name": "data.0.intensity_meas_su" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.time_of_flight", + "cif_names": ["_pd_meas.time_of_flight"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight", + "descriptor_path": "experiment.bragg_pd_tof.data[].time_of_flight", + "docs_anchor": "data-time-of-flight", + "docs_page": "data", + "edi_name": "_data.time_of_flight", + "edi_names": ["_data.time_of_flight"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.time_of_flight", "_pd_meas.time_of_flight"], + "unique_name": "data.0.time_of_flight" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_inc", + "cif_names": ["_pd_meas.time_of_flight_range_inc"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_inc", + "descriptor_path": "experiment.bragg_pd_tof.data_range.time_of_flight_inc", + "docs_anchor": "data-range-time-of-flight-inc", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_inc", + "edi_names": ["_data_range.time_of_flight_inc"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_inc", + "_pd_meas.time_of_flight_range_inc" + ], + "unique_name": "inventory_experiment.data_range.time_of_flight_inc" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_max", + "cif_names": ["_pd_meas.time_of_flight_range_max"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_max", + "descriptor_path": "experiment.bragg_pd_tof.data_range.time_of_flight_max", + "docs_anchor": "data-range-time-of-flight-max", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_max", + "edi_names": ["_data_range.time_of_flight_max"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_max", + "_pd_meas.time_of_flight_range_max" + ], + "unique_name": "inventory_experiment.data_range.time_of_flight_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_min", + "cif_names": ["_pd_meas.time_of_flight_range_min"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_min", + "descriptor_path": "experiment.bragg_pd_tof.data_range.time_of_flight_min", + "docs_anchor": "data-range-time-of-flight-min", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_min", + "edi_names": ["_data_range.time_of_flight_min"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_min", + "_pd_meas.time_of_flight_range_min" + ], + "unique_name": "inventory_experiment.data_range.time_of_flight_min" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_electric_field", + "cif_names": ["_easydiffraction_diffrn.ambient_electric_field"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_electric_field", + "descriptor_path": "experiment.bragg_pd_tof.diffrn.ambient_electric_field", + "docs_anchor": "diffrn-ambient-electric-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_electric_field", + "edi_names": ["_diffrn.ambient_electric_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_electric_field", + "_easydiffraction_diffrn.ambient_electric_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_electric_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_magnetic_field", + "cif_names": ["_easydiffraction_diffrn.ambient_magnetic_field"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_magnetic_field", + "descriptor_path": "experiment.bragg_pd_tof.diffrn.ambient_magnetic_field", + "docs_anchor": "diffrn-ambient-magnetic-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_magnetic_field", + "edi_names": ["_diffrn.ambient_magnetic_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_magnetic_field", + "_easydiffraction_diffrn.ambient_magnetic_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_magnetic_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_pressure", + "cif_names": ["_diffrn.ambient_pressure"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_pressure", + "descriptor_path": "experiment.bragg_pd_tof.diffrn.ambient_pressure", + "docs_anchor": "diffrn-ambient-pressure", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_pressure", + "edi_names": ["_diffrn.ambient_pressure"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_pressure"], + "unique_name": "inventory_experiment.diffrn.ambient_pressure" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_temperature", + "cif_names": ["_diffrn.ambient_temperature"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_temperature", + "descriptor_path": "experiment.bragg_pd_tof.diffrn.ambient_temperature", + "docs_anchor": "diffrn-ambient-temperature", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_temperature", + "edi_names": ["_diffrn.ambient_temperature"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_temperature"], + "unique_name": "inventory_experiment.diffrn.ambient_temperature" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.beam_mode", + "cif_names": [ + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "beam_mode", + "descriptor_path": "experiment.bragg_pd_tof.experiment_type.beam_mode", + "docs_anchor": "experiment-type-beam-mode", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.beam_mode", + "edi_names": ["_experiment_type.beam_mode"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.beam_mode", + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "unique_name": "inventory_experiment.experiment_type.beam_mode" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.radiation_probe", + "cif_names": [ + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "radiation_probe", + "descriptor_path": "experiment.bragg_pd_tof.experiment_type.radiation_probe", + "docs_anchor": "experiment-type-radiation-probe", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.radiation_probe", + "edi_names": ["_experiment_type.radiation_probe"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.radiation_probe", + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "unique_name": "inventory_experiment.experiment_type.radiation_probe" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.sample_form", + "cif_names": [ + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "sample_form", + "descriptor_path": "experiment.bragg_pd_tof.experiment_type.sample_form", + "docs_anchor": "experiment-type-sample-form", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.sample_form", + "edi_names": ["_experiment_type.sample_form"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.sample_form", + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "unique_name": "inventory_experiment.experiment_type.sample_form" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.scattering_type", + "cif_names": [ + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "scattering_type", + "descriptor_path": "experiment.bragg_pd_tof.experiment_type.scattering_type", + "docs_anchor": "experiment-type-scattering-type", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.scattering_type", + "edi_names": ["_experiment_type.scattering_type"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.scattering_type", + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "unique_name": "inventory_experiment.experiment_type.scattering_type" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_linear", + "cif_names": ["_instr.d_to_tof_linear"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_linear", + "descriptor_path": "experiment.bragg_pd_tof.instrument.calib_d_to_tof_linear", + "docs_anchor": "instrument-calib-d-to-tof-linear", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_linear", + "edi_names": ["_instrument.calib_d_to_tof_linear"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_linear", + "_instr.d_to_tof_linear" + ], + "unique_name": "inventory_experiment.instrument.d_to_tof_linear" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_offset", + "cif_names": ["_instr.d_to_tof_offset"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_offset", + "descriptor_path": "experiment.bragg_pd_tof.instrument.calib_d_to_tof_offset", + "docs_anchor": "instrument-calib-d-to-tof-offset", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_offset", + "edi_names": ["_instrument.calib_d_to_tof_offset"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_offset", + "_instr.d_to_tof_offset" + ], + "unique_name": "inventory_experiment.instrument.d_to_tof_offset" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_quad", + "cif_names": ["_instr.d_to_tof_quad"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_quadratic", + "descriptor_path": "experiment.bragg_pd_tof.instrument.calib_d_to_tof_quadratic", + "docs_anchor": "instrument-calib-d-to-tof-quadratic", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_quadratic", + "edi_names": ["_instrument.calib_d_to_tof_quadratic"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_quadratic", + "_instr.d_to_tof_quad" + ], + "unique_name": "inventory_experiment.instrument.d_to_tof_quadratic" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_recip", + "cif_names": ["_instr.d_to_tof_recip"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_reciprocal", + "descriptor_path": "experiment.bragg_pd_tof.instrument.calib_d_to_tof_reciprocal", + "docs_anchor": "instrument-calib-d-to-tof-reciprocal", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_reciprocal", + "edi_names": ["_instrument.calib_d_to_tof_reciprocal"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_reciprocal", + "_instr.d_to_tof_recip" + ], + "unique_name": "inventory_experiment.instrument.d_to_tof_reciprocal" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.2theta_bank", + "cif_names": ["_instr.2theta_bank"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "twotheta_bank", + "descriptor_path": "experiment.bragg_pd_tof.instrument.setup_twotheta_bank", + "docs_anchor": "instrument-setup-twotheta-bank", + "docs_page": "instrument", + "edi_name": "_instrument.setup_twotheta_bank", + "edi_names": ["_instrument.setup_twotheta_bank"], + "owner_class": "TofPdInstrument", + "read_names": ["_instrument.setup_twotheta_bank", "_instr.2theta_bank"], + "unique_name": "inventory_experiment.instrument.twotheta_bank" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.scale", + "cif_names": ["_pd_phase_block.scale"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "experiment.bragg_pd_tof.linked_structures[].scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.scale", "_pd_phase_block.scale"], + "unique_name": "linked_structure.Si.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.id", + "cif_names": ["_pd_phase_block.id"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_pd_tof.linked_structures[].structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.structure_id", "_pd_phase_block.id"], + "unique_name": "linked_structure.Si.structure_id" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_0", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_0"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_0", + "descriptor_path": "experiment.bragg_pd_tof.peak.broad_gauss_sigma_0", + "docs_anchor": "peak-broad-gauss-sigma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_0", + "edi_names": ["_peak.broad_gauss_sigma_0"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.broad_gauss_sigma_0", + "_easydiffraction_peak.broad_gauss_sigma_0" + ], + "unique_name": "inventory_experiment.peak.broad_gauss_sigma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_1", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_1"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_1", + "descriptor_path": "experiment.bragg_pd_tof.peak.broad_gauss_sigma_1", + "docs_anchor": "peak-broad-gauss-sigma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_1", + "edi_names": ["_peak.broad_gauss_sigma_1"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.broad_gauss_sigma_1", + "_easydiffraction_peak.broad_gauss_sigma_1" + ], + "unique_name": "inventory_experiment.peak.broad_gauss_sigma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_2", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_2"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_2", + "descriptor_path": "experiment.bragg_pd_tof.peak.broad_gauss_sigma_2", + "docs_anchor": "peak-broad-gauss-sigma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_2", + "edi_names": ["_peak.broad_gauss_sigma_2"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.broad_gauss_sigma_2", + "_easydiffraction_peak.broad_gauss_sigma_2" + ], + "unique_name": "inventory_experiment.peak.broad_gauss_sigma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.decay_beta_0", + "cif_names": ["_easydiffraction_peak.decay_beta_0"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "decay_beta_0", + "descriptor_path": "experiment.bragg_pd_tof.peak.decay_beta_0", + "docs_anchor": "peak-decay-beta-0", + "docs_page": "peak", + "edi_name": "_peak.decay_beta_0", + "edi_names": ["_peak.decay_beta_0"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.decay_beta_0", + "_easydiffraction_peak.decay_beta_0" + ], + "unique_name": "inventory_experiment.peak.decay_beta_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.decay_beta_1", + "cif_names": ["_easydiffraction_peak.decay_beta_1"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "decay_beta_1", + "descriptor_path": "experiment.bragg_pd_tof.peak.decay_beta_1", + "docs_anchor": "peak-decay-beta-1", + "docs_page": "peak", + "edi_name": "_peak.decay_beta_1", + "edi_names": ["_peak.decay_beta_1"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.decay_beta_1", + "_easydiffraction_peak.decay_beta_1" + ], + "unique_name": "inventory_experiment.peak.decay_beta_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.rise_alpha_0", + "cif_names": ["_easydiffraction_peak.rise_alpha_0"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "rise_alpha_0", + "descriptor_path": "experiment.bragg_pd_tof.peak.rise_alpha_0", + "docs_anchor": "peak-rise-alpha-0", + "docs_page": "peak", + "edi_name": "_peak.rise_alpha_0", + "edi_names": ["_peak.rise_alpha_0"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.rise_alpha_0", + "_easydiffraction_peak.rise_alpha_0" + ], + "unique_name": "inventory_experiment.peak.rise_alpha_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.rise_alpha_1", + "cif_names": ["_easydiffraction_peak.rise_alpha_1"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "rise_alpha_1", + "descriptor_path": "experiment.bragg_pd_tof.peak.rise_alpha_1", + "docs_anchor": "peak-rise-alpha-1", + "docs_page": "peak", + "edi_name": "_peak.rise_alpha_1", + "edi_names": ["_peak.rise_alpha_1"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.rise_alpha_1", + "_easydiffraction_peak.rise_alpha_1" + ], + "unique_name": "inventory_experiment.peak.rise_alpha_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_pd_tof.peak.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TofJorgensen", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "inventory_experiment.peak.type" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_h", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_h", + "_pref_orient.index_h" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "experiment.bragg_pd_tof.pref_orient[].index_h", + "docs_anchor": "preferred-orientation-index-h", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_h", + "edi_names": ["_preferred_orientation.index_h"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_h", + "_pd_pref_orient_March_Dollase.index_h", + "_pref_orient.index_h" + ], + "unique_name": "preferred_orientation.Si.index_h" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_k", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_k", + "_pref_orient.index_k" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "experiment.bragg_pd_tof.pref_orient[].index_k", + "docs_anchor": "preferred-orientation-index-k", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_k", + "edi_names": ["_preferred_orientation.index_k"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_k", + "_pd_pref_orient_March_Dollase.index_k", + "_pref_orient.index_k" + ], + "unique_name": "preferred_orientation.Si.index_k" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_l", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_l", + "_pref_orient.index_l" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "experiment.bragg_pd_tof.pref_orient[].index_l", + "docs_anchor": "preferred-orientation-index-l", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_l", + "edi_names": ["_preferred_orientation.index_l"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_l", + "_pd_pref_orient_March_Dollase.index_l", + "_pref_orient.index_l" + ], + "unique_name": "preferred_orientation.Si.index_l" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.r", + "cif_names": ["_pd_pref_orient_March_Dollase.r", "_pref_orient.march_r"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "march_r", + "descriptor_path": "experiment.bragg_pd_tof.pref_orient[].march_r", + "docs_anchor": "preferred-orientation-march-r", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.march_r", + "edi_names": ["_preferred_orientation.march_r"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.march_r", + "_pd_pref_orient_March_Dollase.r", + "_pref_orient.march_r" + ], + "unique_name": "preferred_orientation.Si.march_r" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_easydiffraction_pref_orient.march_random_fract", + "cif_names": [ + "_easydiffraction_pref_orient.march_random_fract", + "_pref_orient.march_random_fract" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "march_random_fract", + "descriptor_path": "experiment.bragg_pd_tof.pref_orient[].march_random_fract", + "docs_anchor": "preferred-orientation-march-random-fract", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.march_random_fract", + "edi_names": ["_preferred_orientation.march_random_fract"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.march_random_fract", + "_easydiffraction_pref_orient.march_random_fract", + "_pref_orient.march_random_fract" + ], + "unique_name": "preferred_orientation.Si.march_random_fract" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.phase_id", + "cif_names": [ + "_pd_pref_orient_March_Dollase.phase_id", + "_pref_orient.phase_id" + ], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_pd_tof.pref_orient[].structure_id", + "docs_anchor": "preferred-orientation-structure-id", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.structure_id", + "edi_names": ["_preferred_orientation.structure_id"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.structure_id", + "_pd_pref_orient_March_Dollase.phase_id", + "_pref_orient.phase_id" + ], + "unique_name": "preferred_orientation.Si.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "experiment.bragg_pd_tof.refln[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_calc", + "cif_names": ["_refln.f_calc"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_calc", + "descriptor_path": "experiment.bragg_pd_tof.refln[].f_calc", + "docs_anchor": "refln-f-calc", + "docs_page": "refln", + "edi_name": "_refln.f_calc", + "edi_names": ["_refln.f_calc"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.f_calc"], + "unique_name": "refln.0.f_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_squared_calc", + "cif_names": ["_refln.f_squared_calc"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_squared_calc", + "descriptor_path": "experiment.bragg_pd_tof.refln[].f_squared_calc", + "docs_anchor": "refln-f-squared-calc", + "docs_page": "refln", + "edi_name": "_refln.f_squared_calc", + "edi_names": ["_refln.f_squared_calc"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.f_squared_calc"], + "unique_name": "refln.0.f_squared_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_pd_tof.refln[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "experiment.bragg_pd_tof.refln[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "experiment.bragg_pd_tof.refln[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "experiment.bragg_pd_tof.refln[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "experiment.bragg_pd_tof.refln[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "experiment.bragg_pd_tof.refln[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "experiment.bragg_pd_tof.refln[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "experiment.bragg_pd_tof.refln[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_pd_refln.phase_id", + "cif_names": ["_pd_refln.phase_id", "_refln.phase_id"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_pd_tof.refln[].structure_id", + "docs_anchor": "refln-structure-id", + "docs_page": "refln", + "edi_name": "_refln.structure_id", + "edi_names": ["_refln.structure_id"], + "owner_class": "PowderTofRefln", + "read_names": [ + "_refln.structure_id", + "_pd_refln.phase_id", + "_refln.phase_id" + ], + "unique_name": "refln.0.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.time_of_flight", + "cif_names": ["_refln.time_of_flight"], + "context": "experiment.bragg_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight", + "descriptor_path": "experiment.bragg_pd_tof.refln[].time_of_flight", + "docs_anchor": "refln-time-of-flight", + "docs_page": "refln", + "edi_name": "_refln.time_of_flight", + "edi_names": ["_refln.time_of_flight"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.time_of_flight"], + "unique_name": "refln.0.time_of_flight" + }, + { + "category_code": "calculator", + "category_entry_name": null, + "cif_name": "_easydiffraction_calculator.type", + "cif_names": ["_easydiffraction_calculator.type"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_sc_cwl.calculator.type", + "docs_anchor": "calculator-type", + "docs_page": "calculator", + "edi_name": "_calculator.type", + "edi_names": ["_calculator.type"], + "owner_class": "Calculator", + "read_names": ["_calculator.type", "_easydiffraction_calculator.type"], + "unique_name": "inventory_experiment.calculator.type" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_refln.sin_theta_over_lambda_range_max", + "cif_names": ["_refln.sin_theta_over_lambda_range_max"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda_max", + "descriptor_path": "experiment.bragg_sc_cwl.data_range.sin_theta_over_lambda_max", + "docs_anchor": "data-range-sin-theta-over-lambda-max", + "docs_page": "data_range", + "edi_name": "_data_range.sin_theta_over_lambda_max", + "edi_names": ["_data_range.sin_theta_over_lambda_max"], + "owner_class": "ScDataRange", + "read_names": [ + "_data_range.sin_theta_over_lambda_max", + "_refln.sin_theta_over_lambda_range_max" + ], + "unique_name": "inventory_experiment.data_range.sin_theta_over_lambda_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_refln.sin_theta_over_lambda_range_min", + "cif_names": ["_refln.sin_theta_over_lambda_range_min"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda_min", + "descriptor_path": "experiment.bragg_sc_cwl.data_range.sin_theta_over_lambda_min", + "docs_anchor": "data-range-sin-theta-over-lambda-min", + "docs_page": "data_range", + "edi_name": "_data_range.sin_theta_over_lambda_min", + "edi_names": ["_data_range.sin_theta_over_lambda_min"], + "owner_class": "ScDataRange", + "read_names": [ + "_data_range.sin_theta_over_lambda_min", + "_refln.sin_theta_over_lambda_range_min" + ], + "unique_name": "inventory_experiment.data_range.sin_theta_over_lambda_min" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_electric_field", + "cif_names": ["_easydiffraction_diffrn.ambient_electric_field"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_electric_field", + "descriptor_path": "experiment.bragg_sc_cwl.diffrn.ambient_electric_field", + "docs_anchor": "diffrn-ambient-electric-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_electric_field", + "edi_names": ["_diffrn.ambient_electric_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_electric_field", + "_easydiffraction_diffrn.ambient_electric_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_electric_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_magnetic_field", + "cif_names": ["_easydiffraction_diffrn.ambient_magnetic_field"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_magnetic_field", + "descriptor_path": "experiment.bragg_sc_cwl.diffrn.ambient_magnetic_field", + "docs_anchor": "diffrn-ambient-magnetic-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_magnetic_field", + "edi_names": ["_diffrn.ambient_magnetic_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_magnetic_field", + "_easydiffraction_diffrn.ambient_magnetic_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_magnetic_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_pressure", + "cif_names": ["_diffrn.ambient_pressure"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_pressure", + "descriptor_path": "experiment.bragg_sc_cwl.diffrn.ambient_pressure", + "docs_anchor": "diffrn-ambient-pressure", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_pressure", + "edi_names": ["_diffrn.ambient_pressure"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_pressure"], + "unique_name": "inventory_experiment.diffrn.ambient_pressure" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_temperature", + "cif_names": ["_diffrn.ambient_temperature"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_temperature", + "descriptor_path": "experiment.bragg_sc_cwl.diffrn.ambient_temperature", + "docs_anchor": "diffrn-ambient-temperature", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_temperature", + "edi_names": ["_diffrn.ambient_temperature"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_temperature"], + "unique_name": "inventory_experiment.diffrn.ambient_temperature" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.beam_mode", + "cif_names": [ + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "beam_mode", + "descriptor_path": "experiment.bragg_sc_cwl.experiment_type.beam_mode", + "docs_anchor": "experiment-type-beam-mode", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.beam_mode", + "edi_names": ["_experiment_type.beam_mode"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.beam_mode", + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "unique_name": "inventory_experiment.experiment_type.beam_mode" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.radiation_probe", + "cif_names": [ + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "radiation_probe", + "descriptor_path": "experiment.bragg_sc_cwl.experiment_type.radiation_probe", + "docs_anchor": "experiment-type-radiation-probe", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.radiation_probe", + "edi_names": ["_experiment_type.radiation_probe"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.radiation_probe", + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "unique_name": "inventory_experiment.experiment_type.radiation_probe" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.sample_form", + "cif_names": [ + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "sample_form", + "descriptor_path": "experiment.bragg_sc_cwl.experiment_type.sample_form", + "docs_anchor": "experiment-type-sample-form", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.sample_form", + "edi_names": ["_experiment_type.sample_form"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.sample_form", + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "unique_name": "inventory_experiment.experiment_type.sample_form" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.scattering_type", + "cif_names": [ + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "scattering_type", + "descriptor_path": "experiment.bragg_sc_cwl.experiment_type.scattering_type", + "docs_anchor": "experiment-type-scattering-type", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.scattering_type", + "edi_names": ["_experiment_type.scattering_type"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.scattering_type", + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "unique_name": "inventory_experiment.experiment_type.scattering_type" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.model", + "cif_names": ["_easydiffraction_extinction.model"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "model", + "descriptor_path": "experiment.bragg_sc_cwl.extinction.model", + "docs_anchor": "extinction-model", + "docs_page": "extinction", + "edi_name": "_extinction.model", + "edi_names": ["_extinction.model"], + "owner_class": "BeckerCoppensExtinction", + "read_names": ["_extinction.model", "_easydiffraction_extinction.model"], + "unique_name": "inventory_experiment.extinction.model" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.mosaicity", + "cif_names": ["_easydiffraction_extinction.mosaicity"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "mosaicity", + "descriptor_path": "experiment.bragg_sc_cwl.extinction.mosaicity", + "docs_anchor": "extinction-mosaicity", + "docs_page": "extinction", + "edi_name": "_extinction.mosaicity", + "edi_names": ["_extinction.mosaicity"], + "owner_class": "BeckerCoppensExtinction", + "read_names": [ + "_extinction.mosaicity", + "_easydiffraction_extinction.mosaicity" + ], + "unique_name": "inventory_experiment.extinction.mosaicity" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.radius", + "cif_names": ["_easydiffraction_extinction.radius"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "radius", + "descriptor_path": "experiment.bragg_sc_cwl.extinction.radius", + "docs_anchor": "extinction-radius", + "docs_page": "extinction", + "edi_name": "_extinction.radius", + "edi_names": ["_extinction.radius"], + "owner_class": "BeckerCoppensExtinction", + "read_names": [ + "_extinction.radius", + "_easydiffraction_extinction.radius" + ], + "unique_name": "inventory_experiment.extinction.radius" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.type", + "cif_names": ["_easydiffraction_extinction.type"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_sc_cwl.extinction.type", + "docs_anchor": "extinction-type", + "docs_page": "extinction", + "edi_name": "_extinction.type", + "edi_names": ["_extinction.type"], + "owner_class": "BeckerCoppensExtinction", + "read_names": ["_extinction.type", "_easydiffraction_extinction.type"], + "unique_name": "inventory_experiment.extinction.type" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_diffrn_radiation_wavelength.value", + "cif_names": ["_diffrn_radiation_wavelength.value", "_instr.wavelength"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "wavelength", + "descriptor_path": "experiment.bragg_sc_cwl.instrument.setup_wavelength", + "docs_anchor": "instrument-setup-wavelength", + "docs_page": "instrument", + "edi_name": "_instrument.setup_wavelength", + "edi_names": ["_instrument.setup_wavelength"], + "owner_class": "CwlScInstrument", + "read_names": [ + "_instrument.setup_wavelength", + "_diffrn_radiation_wavelength.value", + "_instr.wavelength" + ], + "unique_name": "inventory_experiment.instrument.wavelength" + }, + { + "category_code": "linked_structure", + "category_entry_name": null, + "cif_name": "_easydiffraction_sc_crystal_block.scale", + "cif_names": [ + "_easydiffraction_sc_crystal_block.scale", + "_sc_crystal_block.scale" + ], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "experiment.bragg_sc_cwl.linked_structure.scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": [ + "_linked_structure.scale", + "_easydiffraction_sc_crystal_block.scale", + "_sc_crystal_block.scale" + ], + "unique_name": "inventory_experiment.linked_structure.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": null, + "cif_name": "_easydiffraction_sc_crystal_block.id", + "cif_names": [ + "_easydiffraction_sc_crystal_block.id", + "_sc_crystal_block.id" + ], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_sc_cwl.linked_structure.structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": [ + "_linked_structure.structure_id", + "_easydiffraction_sc_crystal_block.id", + "_sc_crystal_block.id" + ], + "unique_name": "inventory_experiment.linked_structure.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "Refln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "Refln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "Refln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "Refln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "Refln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "Refln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "Refln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "Refln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "experiment.bragg_sc_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "experiment.bragg_sc_cwl.refln[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "Refln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "calculator", + "category_entry_name": null, + "cif_name": "_easydiffraction_calculator.type", + "cif_names": ["_easydiffraction_calculator.type"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_sc_tof.calculator.type", + "docs_anchor": "calculator-type", + "docs_page": "calculator", + "edi_name": "_calculator.type", + "edi_names": ["_calculator.type"], + "owner_class": "Calculator", + "read_names": ["_calculator.type", "_easydiffraction_calculator.type"], + "unique_name": "inventory_experiment.calculator.type" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_refln.sin_theta_over_lambda_range_max", + "cif_names": ["_refln.sin_theta_over_lambda_range_max"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda_max", + "descriptor_path": "experiment.bragg_sc_tof.data_range.sin_theta_over_lambda_max", + "docs_anchor": "data-range-sin-theta-over-lambda-max", + "docs_page": "data_range", + "edi_name": "_data_range.sin_theta_over_lambda_max", + "edi_names": ["_data_range.sin_theta_over_lambda_max"], + "owner_class": "ScDataRange", + "read_names": [ + "_data_range.sin_theta_over_lambda_max", + "_refln.sin_theta_over_lambda_range_max" + ], + "unique_name": "inventory_experiment.data_range.sin_theta_over_lambda_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_refln.sin_theta_over_lambda_range_min", + "cif_names": ["_refln.sin_theta_over_lambda_range_min"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda_min", + "descriptor_path": "experiment.bragg_sc_tof.data_range.sin_theta_over_lambda_min", + "docs_anchor": "data-range-sin-theta-over-lambda-min", + "docs_page": "data_range", + "edi_name": "_data_range.sin_theta_over_lambda_min", + "edi_names": ["_data_range.sin_theta_over_lambda_min"], + "owner_class": "ScDataRange", + "read_names": [ + "_data_range.sin_theta_over_lambda_min", + "_refln.sin_theta_over_lambda_range_min" + ], + "unique_name": "inventory_experiment.data_range.sin_theta_over_lambda_min" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_electric_field", + "cif_names": ["_easydiffraction_diffrn.ambient_electric_field"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_electric_field", + "descriptor_path": "experiment.bragg_sc_tof.diffrn.ambient_electric_field", + "docs_anchor": "diffrn-ambient-electric-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_electric_field", + "edi_names": ["_diffrn.ambient_electric_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_electric_field", + "_easydiffraction_diffrn.ambient_electric_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_electric_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_magnetic_field", + "cif_names": ["_easydiffraction_diffrn.ambient_magnetic_field"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_magnetic_field", + "descriptor_path": "experiment.bragg_sc_tof.diffrn.ambient_magnetic_field", + "docs_anchor": "diffrn-ambient-magnetic-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_magnetic_field", + "edi_names": ["_diffrn.ambient_magnetic_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_magnetic_field", + "_easydiffraction_diffrn.ambient_magnetic_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_magnetic_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_pressure", + "cif_names": ["_diffrn.ambient_pressure"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_pressure", + "descriptor_path": "experiment.bragg_sc_tof.diffrn.ambient_pressure", + "docs_anchor": "diffrn-ambient-pressure", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_pressure", + "edi_names": ["_diffrn.ambient_pressure"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_pressure"], + "unique_name": "inventory_experiment.diffrn.ambient_pressure" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_temperature", + "cif_names": ["_diffrn.ambient_temperature"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_temperature", + "descriptor_path": "experiment.bragg_sc_tof.diffrn.ambient_temperature", + "docs_anchor": "diffrn-ambient-temperature", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_temperature", + "edi_names": ["_diffrn.ambient_temperature"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_temperature"], + "unique_name": "inventory_experiment.diffrn.ambient_temperature" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.beam_mode", + "cif_names": [ + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "beam_mode", + "descriptor_path": "experiment.bragg_sc_tof.experiment_type.beam_mode", + "docs_anchor": "experiment-type-beam-mode", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.beam_mode", + "edi_names": ["_experiment_type.beam_mode"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.beam_mode", + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "unique_name": "inventory_experiment.experiment_type.beam_mode" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.radiation_probe", + "cif_names": [ + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "radiation_probe", + "descriptor_path": "experiment.bragg_sc_tof.experiment_type.radiation_probe", + "docs_anchor": "experiment-type-radiation-probe", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.radiation_probe", + "edi_names": ["_experiment_type.radiation_probe"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.radiation_probe", + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "unique_name": "inventory_experiment.experiment_type.radiation_probe" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.sample_form", + "cif_names": [ + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "sample_form", + "descriptor_path": "experiment.bragg_sc_tof.experiment_type.sample_form", + "docs_anchor": "experiment-type-sample-form", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.sample_form", + "edi_names": ["_experiment_type.sample_form"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.sample_form", + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "unique_name": "inventory_experiment.experiment_type.sample_form" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.scattering_type", + "cif_names": [ + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "scattering_type", + "descriptor_path": "experiment.bragg_sc_tof.experiment_type.scattering_type", + "docs_anchor": "experiment-type-scattering-type", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.scattering_type", + "edi_names": ["_experiment_type.scattering_type"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.scattering_type", + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "unique_name": "inventory_experiment.experiment_type.scattering_type" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.model", + "cif_names": ["_easydiffraction_extinction.model"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "model", + "descriptor_path": "experiment.bragg_sc_tof.extinction.model", + "docs_anchor": "extinction-model", + "docs_page": "extinction", + "edi_name": "_extinction.model", + "edi_names": ["_extinction.model"], + "owner_class": "BeckerCoppensExtinction", + "read_names": ["_extinction.model", "_easydiffraction_extinction.model"], + "unique_name": "inventory_experiment.extinction.model" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.mosaicity", + "cif_names": ["_easydiffraction_extinction.mosaicity"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "Parameter", + "descriptor_name": "mosaicity", + "descriptor_path": "experiment.bragg_sc_tof.extinction.mosaicity", + "docs_anchor": "extinction-mosaicity", + "docs_page": "extinction", + "edi_name": "_extinction.mosaicity", + "edi_names": ["_extinction.mosaicity"], + "owner_class": "BeckerCoppensExtinction", + "read_names": [ + "_extinction.mosaicity", + "_easydiffraction_extinction.mosaicity" + ], + "unique_name": "inventory_experiment.extinction.mosaicity" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.radius", + "cif_names": ["_easydiffraction_extinction.radius"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "Parameter", + "descriptor_name": "radius", + "descriptor_path": "experiment.bragg_sc_tof.extinction.radius", + "docs_anchor": "extinction-radius", + "docs_page": "extinction", + "edi_name": "_extinction.radius", + "edi_names": ["_extinction.radius"], + "owner_class": "BeckerCoppensExtinction", + "read_names": [ + "_extinction.radius", + "_easydiffraction_extinction.radius" + ], + "unique_name": "inventory_experiment.extinction.radius" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.type", + "cif_names": ["_easydiffraction_extinction.type"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.bragg_sc_tof.extinction.type", + "docs_anchor": "extinction-type", + "docs_page": "extinction", + "edi_name": "_extinction.type", + "edi_names": ["_extinction.type"], + "owner_class": "BeckerCoppensExtinction", + "read_names": ["_extinction.type", "_easydiffraction_extinction.type"], + "unique_name": "inventory_experiment.extinction.type" + }, + { + "category_code": "linked_structure", + "category_entry_name": null, + "cif_name": "_easydiffraction_sc_crystal_block.scale", + "cif_names": [ + "_easydiffraction_sc_crystal_block.scale", + "_sc_crystal_block.scale" + ], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "experiment.bragg_sc_tof.linked_structure.scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": [ + "_linked_structure.scale", + "_easydiffraction_sc_crystal_block.scale", + "_sc_crystal_block.scale" + ], + "unique_name": "inventory_experiment.linked_structure.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": null, + "cif_name": "_easydiffraction_sc_crystal_block.id", + "cif_names": [ + "_easydiffraction_sc_crystal_block.id", + "_sc_crystal_block.id" + ], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.bragg_sc_tof.linked_structure.structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": [ + "_linked_structure.structure_id", + "_easydiffraction_sc_crystal_block.id", + "_sc_crystal_block.id" + ], + "unique_name": "inventory_experiment.linked_structure.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "experiment.bragg_sc_tof.refln[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "TofRefln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.bragg_sc_tof.refln[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "TofRefln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "experiment.bragg_sc_tof.refln[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "TofRefln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "experiment.bragg_sc_tof.refln[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "TofRefln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "experiment.bragg_sc_tof.refln[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "TofRefln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "experiment.bragg_sc_tof.refln[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "TofRefln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "experiment.bragg_sc_tof.refln[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "TofRefln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "experiment.bragg_sc_tof.refln[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "TofRefln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "experiment.bragg_sc_tof.refln[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "TofRefln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.wavelength", + "cif_names": ["_refln.wavelength"], + "context": "experiment.bragg_sc_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "wavelength", + "descriptor_path": "experiment.bragg_sc_tof.refln[].wavelength", + "docs_anchor": "refln-wavelength", + "docs_page": "refln", + "edi_name": "_refln.wavelength", + "edi_names": ["_refln.wavelength"], + "owner_class": "TofRefln", + "read_names": ["_refln.wavelength"], + "unique_name": "refln.0.wavelength" + }, + { + "category_code": "calculator", + "category_entry_name": null, + "cif_name": "_easydiffraction_calculator.type", + "cif_names": ["_easydiffraction_calculator.type"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.total_pd_cwl.calculator.type", + "docs_anchor": "calculator-type", + "docs_page": "calculator", + "edi_name": "_calculator.type", + "edi_names": ["_calculator.type"], + "owner_class": "Calculator", + "read_names": ["_calculator.type", "_easydiffraction_calculator.type"], + "unique_name": "inventory_experiment.calculator.type" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.refinement_status", + "cif_names": ["_pd_data.refinement_status"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "calc_status", + "descriptor_path": "experiment.total_pd_cwl.data[].calc_status", + "docs_anchor": "data-calc-status", + "docs_page": "data", + "edi_name": "_data.calc_status", + "edi_names": ["_data.calc_status"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.calc_status", "_pd_data.refinement_status"], + "unique_name": "data.0.calc_status" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_total", + "cif_names": ["_pd_calc.intensity_total"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_calc", + "descriptor_path": "experiment.total_pd_cwl.data[].g_r_calc", + "docs_anchor": "data-g-r-calc", + "docs_page": "data", + "edi_name": "_data.g_r_calc", + "edi_names": ["_data.g_r_calc"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_calc", "_pd_calc.intensity_total"], + "unique_name": "data.0.g_r_calc" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total", + "cif_names": ["_pd_meas.intensity_total"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_meas", + "descriptor_path": "experiment.total_pd_cwl.data[].g_r_meas", + "docs_anchor": "data-g-r-meas", + "docs_page": "data", + "edi_name": "_data.g_r_meas", + "edi_names": ["_data.g_r_meas"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_meas", "_pd_meas.intensity_total"], + "unique_name": "data.0.g_r_meas" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total_su", + "cif_names": ["_pd_meas.intensity_total_su"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_meas_su", + "descriptor_path": "experiment.total_pd_cwl.data[].g_r_meas_su", + "docs_anchor": "data-g-r-meas-su", + "docs_page": "data", + "edi_name": "_data.g_r_meas_su", + "edi_names": ["_data.g_r_meas_su"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_meas_su", "_pd_meas.intensity_total_su"], + "unique_name": "data.0.g_r_meas_su" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.point_id", + "cif_names": ["_pd_data.point_id"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.total_pd_cwl.data[].id", + "docs_anchor": "data-id", + "docs_page": "data", + "edi_name": "_data.id", + "edi_names": ["_data.id"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.id", "_pd_data.point_id"], + "unique_name": "data.0.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.r", + "cif_names": ["_pd_proc.r"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "r", + "descriptor_path": "experiment.total_pd_cwl.data[].r", + "docs_anchor": "data-r", + "docs_page": "data", + "edi_name": "_data.r", + "edi_names": ["_data.r"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.r", "_pd_proc.r"], + "unique_name": "data.0.r" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_inc", + "cif_names": ["_pd_meas.2theta_range_inc"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_inc", + "descriptor_path": "experiment.total_pd_cwl.data_range.two_theta_inc", + "docs_anchor": "data-range-two-theta-inc", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_inc", + "edi_names": ["_data_range.two_theta_inc"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_inc", "_pd_meas.2theta_range_inc"], + "unique_name": "inventory_experiment.data_range.two_theta_inc" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_max", + "cif_names": ["_pd_meas.2theta_range_max"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_max", + "descriptor_path": "experiment.total_pd_cwl.data_range.two_theta_max", + "docs_anchor": "data-range-two-theta-max", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_max", + "edi_names": ["_data_range.two_theta_max"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_max", "_pd_meas.2theta_range_max"], + "unique_name": "inventory_experiment.data_range.two_theta_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_min", + "cif_names": ["_pd_meas.2theta_range_min"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_min", + "descriptor_path": "experiment.total_pd_cwl.data_range.two_theta_min", + "docs_anchor": "data-range-two-theta-min", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_min", + "edi_names": ["_data_range.two_theta_min"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_min", "_pd_meas.2theta_range_min"], + "unique_name": "inventory_experiment.data_range.two_theta_min" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_electric_field", + "cif_names": ["_easydiffraction_diffrn.ambient_electric_field"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_electric_field", + "descriptor_path": "experiment.total_pd_cwl.diffrn.ambient_electric_field", + "docs_anchor": "diffrn-ambient-electric-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_electric_field", + "edi_names": ["_diffrn.ambient_electric_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_electric_field", + "_easydiffraction_diffrn.ambient_electric_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_electric_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_magnetic_field", + "cif_names": ["_easydiffraction_diffrn.ambient_magnetic_field"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_magnetic_field", + "descriptor_path": "experiment.total_pd_cwl.diffrn.ambient_magnetic_field", + "docs_anchor": "diffrn-ambient-magnetic-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_magnetic_field", + "edi_names": ["_diffrn.ambient_magnetic_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_magnetic_field", + "_easydiffraction_diffrn.ambient_magnetic_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_magnetic_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_pressure", + "cif_names": ["_diffrn.ambient_pressure"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_pressure", + "descriptor_path": "experiment.total_pd_cwl.diffrn.ambient_pressure", + "docs_anchor": "diffrn-ambient-pressure", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_pressure", + "edi_names": ["_diffrn.ambient_pressure"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_pressure"], + "unique_name": "inventory_experiment.diffrn.ambient_pressure" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_temperature", + "cif_names": ["_diffrn.ambient_temperature"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_temperature", + "descriptor_path": "experiment.total_pd_cwl.diffrn.ambient_temperature", + "docs_anchor": "diffrn-ambient-temperature", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_temperature", + "edi_names": ["_diffrn.ambient_temperature"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_temperature"], + "unique_name": "inventory_experiment.diffrn.ambient_temperature" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.end", + "cif_names": ["_easydiffraction_excluded_region.end"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "end", + "descriptor_path": "experiment.total_pd_cwl.excluded_regions[].end", + "docs_anchor": "excluded-region-end", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.end", + "edi_names": ["_excluded_region.end"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.end", + "_easydiffraction_excluded_region.end" + ], + "unique_name": "excluded_regions.0.end" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.id", + "cif_names": ["_easydiffraction_excluded_region.id"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.total_pd_cwl.excluded_regions[].id", + "docs_anchor": "excluded-region-id", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.id", + "edi_names": ["_excluded_region.id"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.id", + "_easydiffraction_excluded_region.id" + ], + "unique_name": "excluded_regions.0.id" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.start", + "cif_names": ["_easydiffraction_excluded_region.start"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start", + "descriptor_path": "experiment.total_pd_cwl.excluded_regions[].start", + "docs_anchor": "excluded-region-start", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.start", + "edi_names": ["_excluded_region.start"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.start", + "_easydiffraction_excluded_region.start" + ], + "unique_name": "excluded_regions.0.start" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.beam_mode", + "cif_names": [ + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "context": "experiment.total_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "beam_mode", + "descriptor_path": "experiment.total_pd_cwl.experiment_type.beam_mode", + "docs_anchor": "experiment-type-beam-mode", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.beam_mode", + "edi_names": ["_experiment_type.beam_mode"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.beam_mode", + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "unique_name": "inventory_experiment.experiment_type.beam_mode" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.radiation_probe", + "cif_names": [ + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "context": "experiment.total_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "radiation_probe", + "descriptor_path": "experiment.total_pd_cwl.experiment_type.radiation_probe", + "docs_anchor": "experiment-type-radiation-probe", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.radiation_probe", + "edi_names": ["_experiment_type.radiation_probe"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.radiation_probe", + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "unique_name": "inventory_experiment.experiment_type.radiation_probe" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.sample_form", + "cif_names": [ + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "context": "experiment.total_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "sample_form", + "descriptor_path": "experiment.total_pd_cwl.experiment_type.sample_form", + "docs_anchor": "experiment-type-sample-form", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.sample_form", + "edi_names": ["_experiment_type.sample_form"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.sample_form", + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "unique_name": "inventory_experiment.experiment_type.sample_form" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.scattering_type", + "cif_names": [ + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "context": "experiment.total_pd_cwl", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "scattering_type", + "descriptor_path": "experiment.total_pd_cwl.experiment_type.scattering_type", + "docs_anchor": "experiment-type-scattering-type", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.scattering_type", + "edi_names": ["_experiment_type.scattering_type"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.scattering_type", + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "unique_name": "inventory_experiment.experiment_type.scattering_type" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.scale", + "cif_names": ["_pd_phase_block.scale"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "experiment.total_pd_cwl.linked_structures[].scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.scale", "_pd_phase_block.scale"], + "unique_name": "linked_structure.Si.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.id", + "cif_names": ["_pd_phase_block.id"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.total_pd_cwl.linked_structures[].structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.structure_id", "_pd_phase_block.id"], + "unique_name": "linked_structure.Si.structure_id" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_q", + "cif_names": ["_easydiffraction_peak.broad_q"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "broad_q", + "descriptor_path": "experiment.total_pd_cwl.peak.broad_q", + "docs_anchor": "peak-broad-q", + "docs_page": "peak", + "edi_name": "_peak.broad_q", + "edi_names": ["_peak.broad_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.broad_q", "_easydiffraction_peak.broad_q"], + "unique_name": "inventory_experiment.peak.broad_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.cutoff_q", + "cif_names": ["_easydiffraction_peak.cutoff_q"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "cutoff_q", + "descriptor_path": "experiment.total_pd_cwl.peak.cutoff_q", + "docs_anchor": "peak-cutoff-q", + "docs_page": "peak", + "edi_name": "_peak.cutoff_q", + "edi_names": ["_peak.cutoff_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.cutoff_q", "_easydiffraction_peak.cutoff_q"], + "unique_name": "inventory_experiment.peak.cutoff_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.damp_particle_diameter", + "cif_names": ["_easydiffraction_peak.damp_particle_diameter"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "damp_particle_diameter", + "descriptor_path": "experiment.total_pd_cwl.peak.damp_particle_diameter", + "docs_anchor": "peak-damp-particle-diameter", + "docs_page": "peak", + "edi_name": "_peak.damp_particle_diameter", + "edi_names": ["_peak.damp_particle_diameter"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.damp_particle_diameter", + "_easydiffraction_peak.damp_particle_diameter" + ], + "unique_name": "inventory_experiment.peak.damp_particle_diameter" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.damp_q", + "cif_names": ["_easydiffraction_peak.damp_q"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "damp_q", + "descriptor_path": "experiment.total_pd_cwl.peak.damp_q", + "docs_anchor": "peak-damp-q", + "docs_page": "peak", + "edi_name": "_peak.damp_q", + "edi_names": ["_peak.damp_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.damp_q", "_easydiffraction_peak.damp_q"], + "unique_name": "inventory_experiment.peak.damp_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.sharp_delta_1", + "cif_names": ["_easydiffraction_peak.sharp_delta_1"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "sharp_delta_1", + "descriptor_path": "experiment.total_pd_cwl.peak.sharp_delta_1", + "docs_anchor": "peak-sharp-delta-1", + "docs_page": "peak", + "edi_name": "_peak.sharp_delta_1", + "edi_names": ["_peak.sharp_delta_1"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.sharp_delta_1", + "_easydiffraction_peak.sharp_delta_1" + ], + "unique_name": "inventory_experiment.peak.sharp_delta_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.sharp_delta_2", + "cif_names": ["_easydiffraction_peak.sharp_delta_2"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "Parameter", + "descriptor_name": "sharp_delta_2", + "descriptor_path": "experiment.total_pd_cwl.peak.sharp_delta_2", + "docs_anchor": "peak-sharp-delta-2", + "docs_page": "peak", + "edi_name": "_peak.sharp_delta_2", + "edi_names": ["_peak.sharp_delta_2"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.sharp_delta_2", + "_easydiffraction_peak.sharp_delta_2" + ], + "unique_name": "inventory_experiment.peak.sharp_delta_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "experiment.total_pd_cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.total_pd_cwl.peak.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "inventory_experiment.peak.type" + }, + { + "category_code": "calculator", + "category_entry_name": null, + "cif_name": "_easydiffraction_calculator.type", + "cif_names": ["_easydiffraction_calculator.type"], + "context": "experiment.total_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.total_pd_tof.calculator.type", + "docs_anchor": "calculator-type", + "docs_page": "calculator", + "edi_name": "_calculator.type", + "edi_names": ["_calculator.type"], + "owner_class": "Calculator", + "read_names": ["_calculator.type", "_easydiffraction_calculator.type"], + "unique_name": "inventory_experiment.calculator.type" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.refinement_status", + "cif_names": ["_pd_data.refinement_status"], + "context": "experiment.total_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "calc_status", + "descriptor_path": "experiment.total_pd_tof.data[].calc_status", + "docs_anchor": "data-calc-status", + "docs_page": "data", + "edi_name": "_data.calc_status", + "edi_names": ["_data.calc_status"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.calc_status", "_pd_data.refinement_status"], + "unique_name": "data.0.calc_status" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_total", + "cif_names": ["_pd_calc.intensity_total"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_calc", + "descriptor_path": "experiment.total_pd_tof.data[].g_r_calc", + "docs_anchor": "data-g-r-calc", + "docs_page": "data", + "edi_name": "_data.g_r_calc", + "edi_names": ["_data.g_r_calc"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_calc", "_pd_calc.intensity_total"], + "unique_name": "data.0.g_r_calc" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total", + "cif_names": ["_pd_meas.intensity_total"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_meas", + "descriptor_path": "experiment.total_pd_tof.data[].g_r_meas", + "docs_anchor": "data-g-r-meas", + "docs_page": "data", + "edi_name": "_data.g_r_meas", + "edi_names": ["_data.g_r_meas"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_meas", "_pd_meas.intensity_total"], + "unique_name": "data.0.g_r_meas" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total_su", + "cif_names": ["_pd_meas.intensity_total_su"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_meas_su", + "descriptor_path": "experiment.total_pd_tof.data[].g_r_meas_su", + "docs_anchor": "data-g-r-meas-su", + "docs_page": "data", + "edi_name": "_data.g_r_meas_su", + "edi_names": ["_data.g_r_meas_su"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_meas_su", "_pd_meas.intensity_total_su"], + "unique_name": "data.0.g_r_meas_su" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.point_id", + "cif_names": ["_pd_data.point_id"], + "context": "experiment.total_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.total_pd_tof.data[].id", + "docs_anchor": "data-id", + "docs_page": "data", + "edi_name": "_data.id", + "edi_names": ["_data.id"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.id", "_pd_data.point_id"], + "unique_name": "data.0.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.r", + "cif_names": ["_pd_proc.r"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "r", + "descriptor_path": "experiment.total_pd_tof.data[].r", + "docs_anchor": "data-r", + "docs_page": "data", + "edi_name": "_data.r", + "edi_names": ["_data.r"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.r", "_pd_proc.r"], + "unique_name": "data.0.r" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_inc", + "cif_names": ["_pd_meas.time_of_flight_range_inc"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_inc", + "descriptor_path": "experiment.total_pd_tof.data_range.time_of_flight_inc", + "docs_anchor": "data-range-time-of-flight-inc", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_inc", + "edi_names": ["_data_range.time_of_flight_inc"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_inc", + "_pd_meas.time_of_flight_range_inc" + ], + "unique_name": "inventory_experiment.data_range.time_of_flight_inc" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_max", + "cif_names": ["_pd_meas.time_of_flight_range_max"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_max", + "descriptor_path": "experiment.total_pd_tof.data_range.time_of_flight_max", + "docs_anchor": "data-range-time-of-flight-max", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_max", + "edi_names": ["_data_range.time_of_flight_max"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_max", + "_pd_meas.time_of_flight_range_max" + ], + "unique_name": "inventory_experiment.data_range.time_of_flight_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_min", + "cif_names": ["_pd_meas.time_of_flight_range_min"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_min", + "descriptor_path": "experiment.total_pd_tof.data_range.time_of_flight_min", + "docs_anchor": "data-range-time-of-flight-min", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_min", + "edi_names": ["_data_range.time_of_flight_min"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_min", + "_pd_meas.time_of_flight_range_min" + ], + "unique_name": "inventory_experiment.data_range.time_of_flight_min" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_electric_field", + "cif_names": ["_easydiffraction_diffrn.ambient_electric_field"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_electric_field", + "descriptor_path": "experiment.total_pd_tof.diffrn.ambient_electric_field", + "docs_anchor": "diffrn-ambient-electric-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_electric_field", + "edi_names": ["_diffrn.ambient_electric_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_electric_field", + "_easydiffraction_diffrn.ambient_electric_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_electric_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_magnetic_field", + "cif_names": ["_easydiffraction_diffrn.ambient_magnetic_field"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_magnetic_field", + "descriptor_path": "experiment.total_pd_tof.diffrn.ambient_magnetic_field", + "docs_anchor": "diffrn-ambient-magnetic-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_magnetic_field", + "edi_names": ["_diffrn.ambient_magnetic_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_magnetic_field", + "_easydiffraction_diffrn.ambient_magnetic_field" + ], + "unique_name": "inventory_experiment.diffrn.ambient_magnetic_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_pressure", + "cif_names": ["_diffrn.ambient_pressure"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_pressure", + "descriptor_path": "experiment.total_pd_tof.diffrn.ambient_pressure", + "docs_anchor": "diffrn-ambient-pressure", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_pressure", + "edi_names": ["_diffrn.ambient_pressure"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_pressure"], + "unique_name": "inventory_experiment.diffrn.ambient_pressure" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_temperature", + "cif_names": ["_diffrn.ambient_temperature"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_temperature", + "descriptor_path": "experiment.total_pd_tof.diffrn.ambient_temperature", + "docs_anchor": "diffrn-ambient-temperature", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_temperature", + "edi_names": ["_diffrn.ambient_temperature"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_temperature"], + "unique_name": "inventory_experiment.diffrn.ambient_temperature" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.end", + "cif_names": ["_easydiffraction_excluded_region.end"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "end", + "descriptor_path": "experiment.total_pd_tof.excluded_regions[].end", + "docs_anchor": "excluded-region-end", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.end", + "edi_names": ["_excluded_region.end"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.end", + "_easydiffraction_excluded_region.end" + ], + "unique_name": "excluded_regions.0.end" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.id", + "cif_names": ["_easydiffraction_excluded_region.id"], + "context": "experiment.total_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "experiment.total_pd_tof.excluded_regions[].id", + "docs_anchor": "excluded-region-id", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.id", + "edi_names": ["_excluded_region.id"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.id", + "_easydiffraction_excluded_region.id" + ], + "unique_name": "excluded_regions.0.id" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.start", + "cif_names": ["_easydiffraction_excluded_region.start"], + "context": "experiment.total_pd_tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start", + "descriptor_path": "experiment.total_pd_tof.excluded_regions[].start", + "docs_anchor": "excluded-region-start", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.start", + "edi_names": ["_excluded_region.start"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.start", + "_easydiffraction_excluded_region.start" + ], + "unique_name": "excluded_regions.0.start" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.beam_mode", + "cif_names": [ + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "context": "experiment.total_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "beam_mode", + "descriptor_path": "experiment.total_pd_tof.experiment_type.beam_mode", + "docs_anchor": "experiment-type-beam-mode", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.beam_mode", + "edi_names": ["_experiment_type.beam_mode"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.beam_mode", + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "unique_name": "inventory_experiment.experiment_type.beam_mode" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.radiation_probe", + "cif_names": [ + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "context": "experiment.total_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "radiation_probe", + "descriptor_path": "experiment.total_pd_tof.experiment_type.radiation_probe", + "docs_anchor": "experiment-type-radiation-probe", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.radiation_probe", + "edi_names": ["_experiment_type.radiation_probe"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.radiation_probe", + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "unique_name": "inventory_experiment.experiment_type.radiation_probe" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.sample_form", + "cif_names": [ + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "context": "experiment.total_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "sample_form", + "descriptor_path": "experiment.total_pd_tof.experiment_type.sample_form", + "docs_anchor": "experiment-type-sample-form", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.sample_form", + "edi_names": ["_experiment_type.sample_form"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.sample_form", + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "unique_name": "inventory_experiment.experiment_type.sample_form" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.scattering_type", + "cif_names": [ + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "context": "experiment.total_pd_tof", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "scattering_type", + "descriptor_path": "experiment.total_pd_tof.experiment_type.scattering_type", + "docs_anchor": "experiment-type-scattering-type", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.scattering_type", + "edi_names": ["_experiment_type.scattering_type"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.scattering_type", + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "unique_name": "inventory_experiment.experiment_type.scattering_type" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.scale", + "cif_names": ["_pd_phase_block.scale"], + "context": "experiment.total_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "experiment.total_pd_tof.linked_structures[].scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.scale", "_pd_phase_block.scale"], + "unique_name": "linked_structure.Si.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.id", + "cif_names": ["_pd_phase_block.id"], + "context": "experiment.total_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "experiment.total_pd_tof.linked_structures[].structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.structure_id", "_pd_phase_block.id"], + "unique_name": "linked_structure.Si.structure_id" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_q", + "cif_names": ["_easydiffraction_peak.broad_q"], + "context": "experiment.total_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "broad_q", + "descriptor_path": "experiment.total_pd_tof.peak.broad_q", + "docs_anchor": "peak-broad-q", + "docs_page": "peak", + "edi_name": "_peak.broad_q", + "edi_names": ["_peak.broad_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.broad_q", "_easydiffraction_peak.broad_q"], + "unique_name": "inventory_experiment.peak.broad_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.cutoff_q", + "cif_names": ["_easydiffraction_peak.cutoff_q"], + "context": "experiment.total_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "cutoff_q", + "descriptor_path": "experiment.total_pd_tof.peak.cutoff_q", + "docs_anchor": "peak-cutoff-q", + "docs_page": "peak", + "edi_name": "_peak.cutoff_q", + "edi_names": ["_peak.cutoff_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.cutoff_q", "_easydiffraction_peak.cutoff_q"], + "unique_name": "inventory_experiment.peak.cutoff_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.damp_particle_diameter", + "cif_names": ["_easydiffraction_peak.damp_particle_diameter"], + "context": "experiment.total_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "damp_particle_diameter", + "descriptor_path": "experiment.total_pd_tof.peak.damp_particle_diameter", + "docs_anchor": "peak-damp-particle-diameter", + "docs_page": "peak", + "edi_name": "_peak.damp_particle_diameter", + "edi_names": ["_peak.damp_particle_diameter"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.damp_particle_diameter", + "_easydiffraction_peak.damp_particle_diameter" + ], + "unique_name": "inventory_experiment.peak.damp_particle_diameter" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.damp_q", + "cif_names": ["_easydiffraction_peak.damp_q"], + "context": "experiment.total_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "damp_q", + "descriptor_path": "experiment.total_pd_tof.peak.damp_q", + "docs_anchor": "peak-damp-q", + "docs_page": "peak", + "edi_name": "_peak.damp_q", + "edi_names": ["_peak.damp_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.damp_q", "_easydiffraction_peak.damp_q"], + "unique_name": "inventory_experiment.peak.damp_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.sharp_delta_1", + "cif_names": ["_easydiffraction_peak.sharp_delta_1"], + "context": "experiment.total_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "sharp_delta_1", + "descriptor_path": "experiment.total_pd_tof.peak.sharp_delta_1", + "docs_anchor": "peak-sharp-delta-1", + "docs_page": "peak", + "edi_name": "_peak.sharp_delta_1", + "edi_names": ["_peak.sharp_delta_1"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.sharp_delta_1", + "_easydiffraction_peak.sharp_delta_1" + ], + "unique_name": "inventory_experiment.peak.sharp_delta_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.sharp_delta_2", + "cif_names": ["_easydiffraction_peak.sharp_delta_2"], + "context": "experiment.total_pd_tof", + "descriptor_class": "Parameter", + "descriptor_name": "sharp_delta_2", + "descriptor_path": "experiment.total_pd_tof.peak.sharp_delta_2", + "docs_anchor": "peak-sharp-delta-2", + "docs_page": "peak", + "edi_name": "_peak.sharp_delta_2", + "edi_names": ["_peak.sharp_delta_2"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.sharp_delta_2", + "_easydiffraction_peak.sharp_delta_2" + ], + "unique_name": "inventory_experiment.peak.sharp_delta_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "experiment.total_pd_tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "experiment.total_pd_tof.peak.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "inventory_experiment.peak.type" + }, + { + "category_code": "alias", + "category_entry_name": "_", + "cif_name": "_easydiffraction_alias.id", + "cif_names": ["_easydiffraction_alias.id", "_alias.label"], + "context": "factory.AliasesFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.AliasesFactory.default[].id", + "docs_anchor": "alias-id", + "docs_page": "alias", + "edi_name": "_alias.id", + "edi_names": ["_alias.id"], + "owner_class": "Alias", + "read_names": ["_alias.id", "_easydiffraction_alias.id", "_alias.label"], + "unique_name": "alias._.id" + }, + { + "category_code": "alias", + "category_entry_name": "_", + "cif_name": "_easydiffraction_alias.parameter_unique_name", + "cif_names": [ + "_easydiffraction_alias.parameter_unique_name", + "_alias.param_unique_name" + ], + "context": "factory.AliasesFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name", + "descriptor_path": "factory.AliasesFactory.default[].parameter_unique_name", + "docs_anchor": "alias-parameter-unique-name", + "docs_page": "alias", + "edi_name": "_alias.parameter_unique_name", + "edi_names": ["_alias.parameter_unique_name"], + "owner_class": "Alias", + "read_names": [ + "_alias.parameter_unique_name", + "_easydiffraction_alias.parameter_unique_name", + "_alias.param_unique_name" + ], + "unique_name": "alias._.parameter_unique_name" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_11", + "cif_names": [ + "_atom_site_aniso.B_11", + "_atom_site_aniso.U_11", + "_atom_site_aniso.beta_11" + ], + "context": "factory.AtomSiteAnisoFactory.default", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_11", + "descriptor_path": "factory.AtomSiteAnisoFactory.default[].adp_11", + "docs_anchor": "atom-site-aniso-adp-11", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_11", + "edi_names": ["_atom_site_aniso.adp_11"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_11", + "_atom_site_aniso.B_11", + "_atom_site_aniso.U_11", + "_atom_site_aniso.beta_11" + ], + "unique_name": "atom_site_aniso.adp_11" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_12", + "cif_names": [ + "_atom_site_aniso.B_12", + "_atom_site_aniso.U_12", + "_atom_site_aniso.beta_12" + ], + "context": "factory.AtomSiteAnisoFactory.default", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_12", + "descriptor_path": "factory.AtomSiteAnisoFactory.default[].adp_12", + "docs_anchor": "atom-site-aniso-adp-12", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_12", + "edi_names": ["_atom_site_aniso.adp_12"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_12", + "_atom_site_aniso.B_12", + "_atom_site_aniso.U_12", + "_atom_site_aniso.beta_12" + ], + "unique_name": "atom_site_aniso.adp_12" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_13", + "cif_names": [ + "_atom_site_aniso.B_13", + "_atom_site_aniso.U_13", + "_atom_site_aniso.beta_13" + ], + "context": "factory.AtomSiteAnisoFactory.default", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_13", + "descriptor_path": "factory.AtomSiteAnisoFactory.default[].adp_13", + "docs_anchor": "atom-site-aniso-adp-13", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_13", + "edi_names": ["_atom_site_aniso.adp_13"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_13", + "_atom_site_aniso.B_13", + "_atom_site_aniso.U_13", + "_atom_site_aniso.beta_13" + ], + "unique_name": "atom_site_aniso.adp_13" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_22", + "cif_names": [ + "_atom_site_aniso.B_22", + "_atom_site_aniso.U_22", + "_atom_site_aniso.beta_22" + ], + "context": "factory.AtomSiteAnisoFactory.default", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_22", + "descriptor_path": "factory.AtomSiteAnisoFactory.default[].adp_22", + "docs_anchor": "atom-site-aniso-adp-22", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_22", + "edi_names": ["_atom_site_aniso.adp_22"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_22", + "_atom_site_aniso.B_22", + "_atom_site_aniso.U_22", + "_atom_site_aniso.beta_22" + ], + "unique_name": "atom_site_aniso.adp_22" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_23", + "cif_names": [ + "_atom_site_aniso.B_23", + "_atom_site_aniso.U_23", + "_atom_site_aniso.beta_23" + ], + "context": "factory.AtomSiteAnisoFactory.default", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_23", + "descriptor_path": "factory.AtomSiteAnisoFactory.default[].adp_23", + "docs_anchor": "atom-site-aniso-adp-23", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_23", + "edi_names": ["_atom_site_aniso.adp_23"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_23", + "_atom_site_aniso.B_23", + "_atom_site_aniso.U_23", + "_atom_site_aniso.beta_23" + ], + "unique_name": "atom_site_aniso.adp_23" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_33", + "cif_names": [ + "_atom_site_aniso.B_33", + "_atom_site_aniso.U_33", + "_atom_site_aniso.beta_33" + ], + "context": "factory.AtomSiteAnisoFactory.default", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_33", + "descriptor_path": "factory.AtomSiteAnisoFactory.default[].adp_33", + "docs_anchor": "atom-site-aniso-adp-33", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_33", + "edi_names": ["_atom_site_aniso.adp_33"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_33", + "_atom_site_aniso.B_33", + "_atom_site_aniso.U_33", + "_atom_site_aniso.beta_33" + ], + "unique_name": "atom_site_aniso.adp_33" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.label", + "cif_names": ["_atom_site_aniso.label"], + "context": "factory.AtomSiteAnisoFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.AtomSiteAnisoFactory.default[].id", + "docs_anchor": "atom-site-aniso-id", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.id", + "edi_names": ["_atom_site_aniso.id"], + "owner_class": "AtomSiteAniso", + "read_names": ["_atom_site_aniso.id", "_atom_site_aniso.label"], + "unique_name": "atom_site_aniso.id" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.B_iso_or_equiv", + "cif_names": ["_atom_site.B_iso_or_equiv", "_atom_site.U_iso_or_equiv"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "adp_iso", + "descriptor_path": "factory.AtomSitesFactory.default[].adp_iso", + "docs_anchor": "atom-site-adp-iso", + "docs_page": "atom_site", + "edi_name": "_atom_site.adp_iso", + "edi_names": ["_atom_site.adp_iso"], + "owner_class": "AtomSite", + "read_names": [ + "_atom_site.adp_iso", + "_atom_site.B_iso_or_equiv", + "_atom_site.U_iso_or_equiv" + ], + "unique_name": "atom_site.Si.adp_iso" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.ADP_type", + "cif_names": ["_atom_site.ADP_type"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "adp_type", + "descriptor_path": "factory.AtomSitesFactory.default[].adp_type", + "docs_anchor": "atom-site-adp-type", + "docs_page": "atom_site", + "edi_name": "_atom_site.adp_type", + "edi_names": ["_atom_site.adp_type"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.adp_type", "_atom_site.ADP_type"], + "unique_name": "atom_site.Si.adp_type" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.fract_x", + "cif_names": ["_atom_site.fract_x"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "fract_x", + "descriptor_path": "factory.AtomSitesFactory.default[].fract_x", + "docs_anchor": "atom-site-fract-x", + "docs_page": "atom_site", + "edi_name": "_atom_site.fract_x", + "edi_names": ["_atom_site.fract_x"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.fract_x"], + "unique_name": "atom_site.Si.fract_x" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.fract_y", + "cif_names": ["_atom_site.fract_y"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "fract_y", + "descriptor_path": "factory.AtomSitesFactory.default[].fract_y", + "docs_anchor": "atom-site-fract-y", + "docs_page": "atom_site", + "edi_name": "_atom_site.fract_y", + "edi_names": ["_atom_site.fract_y"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.fract_y"], + "unique_name": "atom_site.Si.fract_y" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.fract_z", + "cif_names": ["_atom_site.fract_z"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "fract_z", + "descriptor_path": "factory.AtomSitesFactory.default[].fract_z", + "docs_anchor": "atom-site-fract-z", + "docs_page": "atom_site", + "edi_name": "_atom_site.fract_z", + "edi_names": ["_atom_site.fract_z"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.fract_z"], + "unique_name": "atom_site.Si.fract_z" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.label", + "cif_names": ["_atom_site.label"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.AtomSitesFactory.default[].id", + "docs_anchor": "atom-site-id", + "docs_page": "atom_site", + "edi_name": "_atom_site.id", + "edi_names": ["_atom_site.id"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.id", "_atom_site.label"], + "unique_name": "atom_site.Si.id" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.site_symmetry_multiplicity", + "cif_names": ["_atom_site.site_symmetry_multiplicity"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "multiplicity", + "descriptor_path": "factory.AtomSitesFactory.default[].multiplicity", + "docs_anchor": "atom-site-multiplicity", + "docs_page": "atom_site", + "edi_name": "_atom_site.multiplicity", + "edi_names": ["_atom_site.multiplicity"], + "owner_class": "AtomSite", + "read_names": [ + "_atom_site.multiplicity", + "_atom_site.site_symmetry_multiplicity" + ], + "unique_name": "atom_site.Si.multiplicity" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.occupancy", + "cif_names": ["_atom_site.occupancy"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "occupancy", + "descriptor_path": "factory.AtomSitesFactory.default[].occupancy", + "docs_anchor": "atom-site-occupancy", + "docs_page": "atom_site", + "edi_name": "_atom_site.occupancy", + "edi_names": ["_atom_site.occupancy"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.occupancy"], + "unique_name": "atom_site.Si.occupancy" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.type_symbol", + "cif_names": ["_atom_site.type_symbol"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type_symbol", + "descriptor_path": "factory.AtomSitesFactory.default[].type_symbol", + "docs_anchor": "atom-site-type-symbol", + "docs_page": "atom_site", + "edi_name": "_atom_site.type_symbol", + "edi_names": ["_atom_site.type_symbol"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.type_symbol"], + "unique_name": "atom_site.Si.type_symbol" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.Wyckoff_symbol", + "cif_names": ["_atom_site.Wyckoff_symbol", "_atom_site.Wyckoff_letter"], + "context": "factory.AtomSitesFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "wyckoff_letter", + "descriptor_path": "factory.AtomSitesFactory.default[].wyckoff_letter", + "docs_anchor": "atom-site-wyckoff-letter", + "docs_page": "atom_site", + "edi_name": "_atom_site.wyckoff_letter", + "edi_names": ["_atom_site.wyckoff_letter"], + "owner_class": "AtomSite", + "read_names": [ + "_atom_site.wyckoff_letter", + "_atom_site.Wyckoff_symbol", + "_atom_site.Wyckoff_letter" + ], + "unique_name": "atom_site.Si.wyckoff_letter" + }, + { + "category_code": null, + "category_entry_name": null, + "cif_name": "_easydiffraction_background.type", + "cif_names": ["_easydiffraction_background.type"], + "context": "factory.BackgroundFactory.chebyshev", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.BackgroundFactory.chebyshev.type", + "docs_anchor": "background-type", + "docs_page": "background", + "edi_name": "_background.type", + "edi_names": ["_background.type"], + "owner_class": "ChebyshevPolynomialBackground", + "read_names": ["_background.type", "_easydiffraction_background.type"], + "unique_name": "type" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.Chebyshev_coef", + "cif_names": ["_pd_background.Chebyshev_coef"], + "context": "factory.BackgroundFactory.chebyshev", + "descriptor_class": "Parameter", + "descriptor_name": "coef", + "descriptor_path": "factory.BackgroundFactory.chebyshev[].coef", + "docs_anchor": "background-coef", + "docs_page": "background", + "edi_name": "_background.coef", + "edi_names": ["_background.coef"], + "owner_class": "PolynomialTerm", + "read_names": ["_background.coef", "_pd_background.Chebyshev_coef"], + "unique_name": "background.0.coef" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.id", + "cif_names": ["_pd_background.id"], + "context": "factory.BackgroundFactory.chebyshev", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.BackgroundFactory.chebyshev[].id", + "docs_anchor": "background-id", + "docs_page": "background", + "edi_name": "_background.id", + "edi_names": ["_background.id"], + "owner_class": "PolynomialTerm", + "read_names": ["_background.id", "_pd_background.id"], + "unique_name": "background.0.id" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.Chebyshev_order", + "cif_names": ["_pd_background.Chebyshev_order"], + "context": "factory.BackgroundFactory.chebyshev", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "order", + "descriptor_path": "factory.BackgroundFactory.chebyshev[].order", + "docs_anchor": "background-order", + "docs_page": "background", + "edi_name": "_background.order", + "edi_names": ["_background.order"], + "owner_class": "PolynomialTerm", + "read_names": ["_background.order", "_pd_background.Chebyshev_order"], + "unique_name": "background.0.order" + }, + { + "category_code": null, + "category_entry_name": null, + "cif_name": "_easydiffraction_background.type", + "cif_names": ["_easydiffraction_background.type"], + "context": "factory.BackgroundFactory.line-segment", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.BackgroundFactory.line-segment.type", + "docs_anchor": "background-type", + "docs_page": "background", + "edi_name": "_background.type", + "edi_names": ["_background.type"], + "owner_class": "LineSegmentBackground", + "read_names": ["_background.type", "_easydiffraction_background.type"], + "unique_name": "type" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.id", + "cif_names": ["_pd_background.id"], + "context": "factory.BackgroundFactory.line-segment", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.BackgroundFactory.line-segment[].id", + "docs_anchor": "background-id", + "docs_page": "background", + "edi_name": "_background.id", + "edi_names": ["_background.id"], + "owner_class": "LineSegment", + "read_names": ["_background.id", "_pd_background.id"], + "unique_name": "background.0.id" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.line_segment_intensity", + "cif_names": [ + "_pd_background.line_segment_intensity", + "_pd_background_line_segment_intensity" + ], + "context": "factory.BackgroundFactory.line-segment", + "descriptor_class": "Parameter", + "descriptor_name": "intensity", + "descriptor_path": "factory.BackgroundFactory.line-segment[].intensity", + "docs_anchor": "background-intensity", + "docs_page": "background", + "edi_name": "_background.intensity", + "edi_names": ["_background.intensity"], + "owner_class": "LineSegment", + "read_names": [ + "_background.intensity", + "_pd_background.line_segment_intensity", + "_pd_background_line_segment_intensity" + ], + "unique_name": "background.0.intensity" + }, + { + "category_code": "background", + "category_entry_name": "0", + "cif_name": "_pd_background.line_segment_X", + "cif_names": [ + "_pd_background.line_segment_X", + "_pd_background_line_segment_X" + ], + "context": "factory.BackgroundFactory.line-segment", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "position", + "descriptor_path": "factory.BackgroundFactory.line-segment[].position", + "docs_anchor": "background-position", + "docs_page": "background", + "edi_name": "_background.position", + "edi_names": ["_background.position"], + "owner_class": "LineSegment", + "read_names": [ + "_background.position", + "_pd_background.line_segment_X", + "_pd_background_line_segment_X" + ], + "unique_name": "background.0.position" + }, + { + "category_code": "calculator", + "category_entry_name": null, + "cif_name": "_easydiffraction_calculator.type", + "cif_names": ["_easydiffraction_calculator.type"], + "context": "factory.CalculatorCategoryFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.CalculatorCategoryFactory.default.type", + "docs_anchor": "calculator-type", + "docs_page": "calculator", + "edi_name": "_calculator.type", + "edi_names": ["_calculator.type"], + "owner_class": "Calculator", + "read_names": ["_calculator.type", "_easydiffraction_calculator.type"], + "unique_name": "calculator.type" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.angle_alpha", + "cif_names": ["_cell.angle_alpha"], + "context": "factory.CellFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "angle_alpha", + "descriptor_path": "factory.CellFactory.default.angle_alpha", + "docs_anchor": "cell-angle-alpha", + "docs_page": "cell", + "edi_name": "_cell.angle_alpha", + "edi_names": ["_cell.angle_alpha"], + "owner_class": "Cell", + "read_names": ["_cell.angle_alpha"], + "unique_name": "cell.angle_alpha" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.angle_beta", + "cif_names": ["_cell.angle_beta"], + "context": "factory.CellFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "angle_beta", + "descriptor_path": "factory.CellFactory.default.angle_beta", + "docs_anchor": "cell-angle-beta", + "docs_page": "cell", + "edi_name": "_cell.angle_beta", + "edi_names": ["_cell.angle_beta"], + "owner_class": "Cell", + "read_names": ["_cell.angle_beta"], + "unique_name": "cell.angle_beta" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.angle_gamma", + "cif_names": ["_cell.angle_gamma"], + "context": "factory.CellFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "angle_gamma", + "descriptor_path": "factory.CellFactory.default.angle_gamma", + "docs_anchor": "cell-angle-gamma", + "docs_page": "cell", + "edi_name": "_cell.angle_gamma", + "edi_names": ["_cell.angle_gamma"], + "owner_class": "Cell", + "read_names": ["_cell.angle_gamma"], + "unique_name": "cell.angle_gamma" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.length_a", + "cif_names": ["_cell.length_a"], + "context": "factory.CellFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "length_a", + "descriptor_path": "factory.CellFactory.default.length_a", + "docs_anchor": "cell-length-a", + "docs_page": "cell", + "edi_name": "_cell.length_a", + "edi_names": ["_cell.length_a"], + "owner_class": "Cell", + "read_names": ["_cell.length_a"], + "unique_name": "cell.length_a" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.length_b", + "cif_names": ["_cell.length_b"], + "context": "factory.CellFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "length_b", + "descriptor_path": "factory.CellFactory.default.length_b", + "docs_anchor": "cell-length-b", + "docs_page": "cell", + "edi_name": "_cell.length_b", + "edi_names": ["_cell.length_b"], + "owner_class": "Cell", + "read_names": ["_cell.length_b"], + "unique_name": "cell.length_b" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.length_c", + "cif_names": ["_cell.length_c"], + "context": "factory.CellFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "length_c", + "descriptor_path": "factory.CellFactory.default.length_c", + "docs_anchor": "cell-length-c", + "docs_page": "cell", + "edi_name": "_cell.length_c", + "edi_names": ["_cell.length_c"], + "owner_class": "Cell", + "read_names": ["_cell.length_c"], + "unique_name": "cell.length_c" + }, + { + "category_code": "constraint", + "category_entry_name": "_", + "cif_name": "_easydiffraction_constraint.expression", + "cif_names": ["_easydiffraction_constraint.expression"], + "context": "factory.ConstraintsFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "expression", + "descriptor_path": "factory.ConstraintsFactory.default[].expression", + "docs_anchor": "constraint-expression", + "docs_page": "constraint", + "edi_name": "_constraint.expression", + "edi_names": ["_constraint.expression"], + "owner_class": "Constraint", + "read_names": [ + "_constraint.expression", + "_easydiffraction_constraint.expression" + ], + "unique_name": "constraint._.expression" + }, + { + "category_code": "constraint", + "category_entry_name": "_", + "cif_name": "_easydiffraction_constraint.id", + "cif_names": ["_easydiffraction_constraint.id"], + "context": "factory.ConstraintsFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.ConstraintsFactory.default[].id", + "docs_anchor": "constraint-id", + "docs_page": "constraint", + "edi_name": "_constraint.id", + "edi_names": ["_constraint.id"], + "owner_class": "Constraint", + "read_names": ["_constraint.id", "_easydiffraction_constraint.id"], + "unique_name": "constraint._.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.refinement_status", + "cif_names": ["_pd_data.refinement_status"], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "StringDescriptor", + "descriptor_name": "calc_status", + "descriptor_path": "factory.DataFactory.bragg-pd[].calc_status", + "docs_anchor": "data-calc-status", + "docs_page": "data", + "edi_name": "_data.calc_status", + "edi_names": ["_data.calc_status"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.calc_status", "_pd_data.refinement_status"], + "unique_name": "data.0.calc_status" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.d_spacing", + "cif_names": ["_pd_proc.d_spacing"], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "factory.DataFactory.bragg-pd[].d_spacing", + "docs_anchor": "data-d-spacing", + "docs_page": "data", + "edi_name": "_data.d_spacing", + "edi_names": ["_data.d_spacing"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.d_spacing", "_pd_proc.d_spacing"], + "unique_name": "data.0.d_spacing" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.point_id", + "cif_names": ["_pd_data.point_id"], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.DataFactory.bragg-pd[].id", + "docs_anchor": "data-id", + "docs_page": "data", + "edi_name": "_data.id", + "edi_names": ["_data.id"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.id", "_pd_data.point_id"], + "unique_name": "data.0.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_bkg", + "cif_names": ["_pd_calc.intensity_bkg"], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_bkg", + "descriptor_path": "factory.DataFactory.bragg-pd[].intensity_bkg", + "docs_anchor": "data-intensity-bkg", + "docs_page": "data", + "edi_name": "_data.intensity_bkg", + "edi_names": ["_data.intensity_bkg"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.intensity_bkg", "_pd_calc.intensity_bkg"], + "unique_name": "data.0.intensity_bkg" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_total", + "cif_names": ["_pd_calc.intensity_total"], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "factory.DataFactory.bragg-pd[].intensity_calc", + "docs_anchor": "data-intensity-calc", + "docs_page": "data", + "edi_name": "_data.intensity_calc", + "edi_names": ["_data.intensity_calc"], + "owner_class": "PdCwlDataPoint", + "read_names": ["_data.intensity_calc", "_pd_calc.intensity_total"], + "unique_name": "data.0.intensity_calc" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total", + "cif_names": ["_pd_meas.intensity_total", "_pd_proc.intensity_norm"], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "factory.DataFactory.bragg-pd[].intensity_meas", + "docs_anchor": "data-intensity-meas", + "docs_page": "data", + "edi_name": "_data.intensity_meas", + "edi_names": ["_data.intensity_meas"], + "owner_class": "PdCwlDataPoint", + "read_names": [ + "_data.intensity_meas", + "_pd_meas.intensity_total", + "_pd_proc.intensity_norm" + ], + "unique_name": "data.0.intensity_meas" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total_su", + "cif_names": [ + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "factory.DataFactory.bragg-pd[].intensity_meas_su", + "docs_anchor": "data-intensity-meas-su", + "docs_page": "data", + "edi_name": "_data.intensity_meas_su", + "edi_names": ["_data.intensity_meas_su"], + "owner_class": "PdCwlDataPoint", + "read_names": [ + "_data.intensity_meas_su", + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "unique_name": "data.0.intensity_meas_su" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.2theta_scan", + "cif_names": ["_pd_proc.2theta_scan", "_pd_meas.2theta_scan"], + "context": "factory.DataFactory.bragg-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta", + "descriptor_path": "factory.DataFactory.bragg-pd[].two_theta", + "docs_anchor": "data-two-theta", + "docs_page": "data", + "edi_name": "_data.two_theta", + "edi_names": ["_data.two_theta"], + "owner_class": "PdCwlDataPoint", + "read_names": [ + "_data.two_theta", + "_pd_proc.2theta_scan", + "_pd_meas.2theta_scan" + ], + "unique_name": "data.0.two_theta" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.refinement_status", + "cif_names": ["_pd_data.refinement_status"], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "calc_status", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].calc_status", + "docs_anchor": "data-calc-status", + "docs_page": "data", + "edi_name": "_data.calc_status", + "edi_names": ["_data.calc_status"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.calc_status", "_pd_data.refinement_status"], + "unique_name": "data.0.calc_status" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.d_spacing", + "cif_names": ["_pd_proc.d_spacing"], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].d_spacing", + "docs_anchor": "data-d-spacing", + "docs_page": "data", + "edi_name": "_data.d_spacing", + "edi_names": ["_data.d_spacing"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.d_spacing", "_pd_proc.d_spacing"], + "unique_name": "data.0.d_spacing" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.point_id", + "cif_names": ["_pd_data.point_id"], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].id", + "docs_anchor": "data-id", + "docs_page": "data", + "edi_name": "_data.id", + "edi_names": ["_data.id"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.id", "_pd_data.point_id"], + "unique_name": "data.0.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_bkg", + "cif_names": ["_pd_calc.intensity_bkg"], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_bkg", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].intensity_bkg", + "docs_anchor": "data-intensity-bkg", + "docs_page": "data", + "edi_name": "_data.intensity_bkg", + "edi_names": ["_data.intensity_bkg"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.intensity_bkg", "_pd_calc.intensity_bkg"], + "unique_name": "data.0.intensity_bkg" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_total", + "cif_names": ["_pd_calc.intensity_total"], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].intensity_calc", + "docs_anchor": "data-intensity-calc", + "docs_page": "data", + "edi_name": "_data.intensity_calc", + "edi_names": ["_data.intensity_calc"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.intensity_calc", "_pd_calc.intensity_total"], + "unique_name": "data.0.intensity_calc" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total", + "cif_names": ["_pd_meas.intensity_total", "_pd_proc.intensity_norm"], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].intensity_meas", + "docs_anchor": "data-intensity-meas", + "docs_page": "data", + "edi_name": "_data.intensity_meas", + "edi_names": ["_data.intensity_meas"], + "owner_class": "PdTofDataPoint", + "read_names": [ + "_data.intensity_meas", + "_pd_meas.intensity_total", + "_pd_proc.intensity_norm" + ], + "unique_name": "data.0.intensity_meas" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total_su", + "cif_names": [ + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].intensity_meas_su", + "docs_anchor": "data-intensity-meas-su", + "docs_page": "data", + "edi_name": "_data.intensity_meas_su", + "edi_names": ["_data.intensity_meas_su"], + "owner_class": "PdTofDataPoint", + "read_names": [ + "_data.intensity_meas_su", + "_pd_meas.intensity_total_su", + "_pd_proc.intensity_norm_su" + ], + "unique_name": "data.0.intensity_meas_su" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.time_of_flight", + "cif_names": ["_pd_meas.time_of_flight"], + "context": "factory.DataFactory.bragg-pd-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight", + "descriptor_path": "factory.DataFactory.bragg-pd-tof[].time_of_flight", + "docs_anchor": "data-time-of-flight", + "docs_page": "data", + "edi_name": "_data.time_of_flight", + "edi_names": ["_data.time_of_flight"], + "owner_class": "PdTofDataPoint", + "read_names": ["_data.time_of_flight", "_pd_meas.time_of_flight"], + "unique_name": "data.0.time_of_flight" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.refinement_status", + "cif_names": ["_pd_data.refinement_status"], + "context": "factory.DataFactory.total-pd", + "descriptor_class": "StringDescriptor", + "descriptor_name": "calc_status", + "descriptor_path": "factory.DataFactory.total-pd[].calc_status", + "docs_anchor": "data-calc-status", + "docs_page": "data", + "edi_name": "_data.calc_status", + "edi_names": ["_data.calc_status"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.calc_status", "_pd_data.refinement_status"], + "unique_name": "data.0.calc_status" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_calc.intensity_total", + "cif_names": ["_pd_calc.intensity_total"], + "context": "factory.DataFactory.total-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_calc", + "descriptor_path": "factory.DataFactory.total-pd[].g_r_calc", + "docs_anchor": "data-g-r-calc", + "docs_page": "data", + "edi_name": "_data.g_r_calc", + "edi_names": ["_data.g_r_calc"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_calc", "_pd_calc.intensity_total"], + "unique_name": "data.0.g_r_calc" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total", + "cif_names": ["_pd_meas.intensity_total"], + "context": "factory.DataFactory.total-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_meas", + "descriptor_path": "factory.DataFactory.total-pd[].g_r_meas", + "docs_anchor": "data-g-r-meas", + "docs_page": "data", + "edi_name": "_data.g_r_meas", + "edi_names": ["_data.g_r_meas"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_meas", "_pd_meas.intensity_total"], + "unique_name": "data.0.g_r_meas" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_meas.intensity_total_su", + "cif_names": ["_pd_meas.intensity_total_su"], + "context": "factory.DataFactory.total-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "g_r_meas_su", + "descriptor_path": "factory.DataFactory.total-pd[].g_r_meas_su", + "docs_anchor": "data-g-r-meas-su", + "docs_page": "data", + "edi_name": "_data.g_r_meas_su", + "edi_names": ["_data.g_r_meas_su"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.g_r_meas_su", "_pd_meas.intensity_total_su"], + "unique_name": "data.0.g_r_meas_su" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_data.point_id", + "cif_names": ["_pd_data.point_id"], + "context": "factory.DataFactory.total-pd", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.DataFactory.total-pd[].id", + "docs_anchor": "data-id", + "docs_page": "data", + "edi_name": "_data.id", + "edi_names": ["_data.id"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.id", "_pd_data.point_id"], + "unique_name": "data.0.id" + }, + { + "category_code": "data", + "category_entry_name": "0", + "cif_name": "_pd_proc.r", + "cif_names": ["_pd_proc.r"], + "context": "factory.DataFactory.total-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "r", + "descriptor_path": "factory.DataFactory.total-pd[].r", + "docs_anchor": "data-r", + "docs_page": "data", + "edi_name": "_data.r", + "edi_names": ["_data.r"], + "owner_class": "TotalDataPoint", + "read_names": ["_data.r", "_pd_proc.r"], + "unique_name": "data.0.r" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_inc", + "cif_names": ["_pd_meas.2theta_range_inc"], + "context": "factory.DataRangeFactory.cwl-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_inc", + "descriptor_path": "factory.DataRangeFactory.cwl-pd.two_theta_inc", + "docs_anchor": "data-range-two-theta-inc", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_inc", + "edi_names": ["_data_range.two_theta_inc"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_inc", "_pd_meas.2theta_range_inc"], + "unique_name": "data_range.two_theta_inc" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_max", + "cif_names": ["_pd_meas.2theta_range_max"], + "context": "factory.DataRangeFactory.cwl-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_max", + "descriptor_path": "factory.DataRangeFactory.cwl-pd.two_theta_max", + "docs_anchor": "data-range-two-theta-max", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_max", + "edi_names": ["_data_range.two_theta_max"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_max", "_pd_meas.2theta_range_max"], + "unique_name": "data_range.two_theta_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.2theta_range_min", + "cif_names": ["_pd_meas.2theta_range_min"], + "context": "factory.DataRangeFactory.cwl-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta_min", + "descriptor_path": "factory.DataRangeFactory.cwl-pd.two_theta_min", + "docs_anchor": "data-range-two-theta-min", + "docs_page": "data_range", + "edi_name": "_data_range.two_theta_min", + "edi_names": ["_data_range.two_theta_min"], + "owner_class": "CwlPdDataRange", + "read_names": ["_data_range.two_theta_min", "_pd_meas.2theta_range_min"], + "unique_name": "data_range.two_theta_min" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_refln.sin_theta_over_lambda_range_max", + "cif_names": ["_refln.sin_theta_over_lambda_range_max"], + "context": "factory.DataRangeFactory.sc", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda_max", + "descriptor_path": "factory.DataRangeFactory.sc.sin_theta_over_lambda_max", + "docs_anchor": "data-range-sin-theta-over-lambda-max", + "docs_page": "data_range", + "edi_name": "_data_range.sin_theta_over_lambda_max", + "edi_names": ["_data_range.sin_theta_over_lambda_max"], + "owner_class": "ScDataRange", + "read_names": [ + "_data_range.sin_theta_over_lambda_max", + "_refln.sin_theta_over_lambda_range_max" + ], + "unique_name": "data_range.sin_theta_over_lambda_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_refln.sin_theta_over_lambda_range_min", + "cif_names": ["_refln.sin_theta_over_lambda_range_min"], + "context": "factory.DataRangeFactory.sc", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda_min", + "descriptor_path": "factory.DataRangeFactory.sc.sin_theta_over_lambda_min", + "docs_anchor": "data-range-sin-theta-over-lambda-min", + "docs_page": "data_range", + "edi_name": "_data_range.sin_theta_over_lambda_min", + "edi_names": ["_data_range.sin_theta_over_lambda_min"], + "owner_class": "ScDataRange", + "read_names": [ + "_data_range.sin_theta_over_lambda_min", + "_refln.sin_theta_over_lambda_range_min" + ], + "unique_name": "data_range.sin_theta_over_lambda_min" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_inc", + "cif_names": ["_pd_meas.time_of_flight_range_inc"], + "context": "factory.DataRangeFactory.tof-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_inc", + "descriptor_path": "factory.DataRangeFactory.tof-pd.time_of_flight_inc", + "docs_anchor": "data-range-time-of-flight-inc", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_inc", + "edi_names": ["_data_range.time_of_flight_inc"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_inc", + "_pd_meas.time_of_flight_range_inc" + ], + "unique_name": "data_range.time_of_flight_inc" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_max", + "cif_names": ["_pd_meas.time_of_flight_range_max"], + "context": "factory.DataRangeFactory.tof-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_max", + "descriptor_path": "factory.DataRangeFactory.tof-pd.time_of_flight_max", + "docs_anchor": "data-range-time-of-flight-max", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_max", + "edi_names": ["_data_range.time_of_flight_max"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_max", + "_pd_meas.time_of_flight_range_max" + ], + "unique_name": "data_range.time_of_flight_max" + }, + { + "category_code": "data_range", + "category_entry_name": null, + "cif_name": "_pd_meas.time_of_flight_range_min", + "cif_names": ["_pd_meas.time_of_flight_range_min"], + "context": "factory.DataRangeFactory.tof-pd", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight_min", + "descriptor_path": "factory.DataRangeFactory.tof-pd.time_of_flight_min", + "docs_anchor": "data-range-time-of-flight-min", + "docs_page": "data_range", + "edi_name": "_data_range.time_of_flight_min", + "edi_names": ["_data_range.time_of_flight_min"], + "owner_class": "TofPdDataRange", + "read_names": [ + "_data_range.time_of_flight_min", + "_pd_meas.time_of_flight_range_min" + ], + "unique_name": "data_range.time_of_flight_min" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_electric_field", + "cif_names": ["_easydiffraction_diffrn.ambient_electric_field"], + "context": "factory.DiffrnFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_electric_field", + "descriptor_path": "factory.DiffrnFactory.default.ambient_electric_field", + "docs_anchor": "diffrn-ambient-electric-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_electric_field", + "edi_names": ["_diffrn.ambient_electric_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_electric_field", + "_easydiffraction_diffrn.ambient_electric_field" + ], + "unique_name": "diffrn.ambient_electric_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_easydiffraction_diffrn.ambient_magnetic_field", + "cif_names": ["_easydiffraction_diffrn.ambient_magnetic_field"], + "context": "factory.DiffrnFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_magnetic_field", + "descriptor_path": "factory.DiffrnFactory.default.ambient_magnetic_field", + "docs_anchor": "diffrn-ambient-magnetic-field", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_magnetic_field", + "edi_names": ["_diffrn.ambient_magnetic_field"], + "owner_class": "DefaultDiffrn", + "read_names": [ + "_diffrn.ambient_magnetic_field", + "_easydiffraction_diffrn.ambient_magnetic_field" + ], + "unique_name": "diffrn.ambient_magnetic_field" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_pressure", + "cif_names": ["_diffrn.ambient_pressure"], + "context": "factory.DiffrnFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_pressure", + "descriptor_path": "factory.DiffrnFactory.default.ambient_pressure", + "docs_anchor": "diffrn-ambient-pressure", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_pressure", + "edi_names": ["_diffrn.ambient_pressure"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_pressure"], + "unique_name": "diffrn.ambient_pressure" + }, + { + "category_code": "diffrn", + "category_entry_name": null, + "cif_name": "_diffrn.ambient_temperature", + "cif_names": ["_diffrn.ambient_temperature"], + "context": "factory.DiffrnFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "ambient_temperature", + "descriptor_path": "factory.DiffrnFactory.default.ambient_temperature", + "docs_anchor": "diffrn-ambient-temperature", + "docs_page": "diffrn", + "edi_name": "_diffrn.ambient_temperature", + "edi_names": ["_diffrn.ambient_temperature"], + "owner_class": "DefaultDiffrn", + "read_names": ["_diffrn.ambient_temperature"], + "unique_name": "diffrn.ambient_temperature" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.end", + "cif_names": ["_easydiffraction_excluded_region.end"], + "context": "factory.ExcludedRegionsFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "end", + "descriptor_path": "factory.ExcludedRegionsFactory.default[].end", + "docs_anchor": "excluded-region-end", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.end", + "edi_names": ["_excluded_region.end"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.end", + "_easydiffraction_excluded_region.end" + ], + "unique_name": "excluded_regions.0.end" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.id", + "cif_names": ["_easydiffraction_excluded_region.id"], + "context": "factory.ExcludedRegionsFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.ExcludedRegionsFactory.default[].id", + "docs_anchor": "excluded-region-id", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.id", + "edi_names": ["_excluded_region.id"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.id", + "_easydiffraction_excluded_region.id" + ], + "unique_name": "excluded_regions.0.id" + }, + { + "category_code": "excluded_regions", + "category_entry_name": "0", + "cif_name": "_easydiffraction_excluded_region.start", + "cif_names": ["_easydiffraction_excluded_region.start"], + "context": "factory.ExcludedRegionsFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start", + "descriptor_path": "factory.ExcludedRegionsFactory.default[].start", + "docs_anchor": "excluded-region-start", + "docs_page": "excluded_region", + "edi_name": "_excluded_region.start", + "edi_names": ["_excluded_region.start"], + "owner_class": "ExcludedRegion", + "read_names": [ + "_excluded_region.start", + "_easydiffraction_excluded_region.start" + ], + "unique_name": "excluded_regions.0.start" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.beam_mode", + "cif_names": [ + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "context": "factory.ExperimentTypeFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "beam_mode", + "descriptor_path": "factory.ExperimentTypeFactory.default.beam_mode", + "docs_anchor": "experiment-type-beam-mode", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.beam_mode", + "edi_names": ["_experiment_type.beam_mode"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.beam_mode", + "_easydiffraction_experiment_type.beam_mode", + "_expt_type.beam_mode" + ], + "unique_name": "experiment_type.beam_mode" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.radiation_probe", + "cif_names": [ + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "context": "factory.ExperimentTypeFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "radiation_probe", + "descriptor_path": "factory.ExperimentTypeFactory.default.radiation_probe", + "docs_anchor": "experiment-type-radiation-probe", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.radiation_probe", + "edi_names": ["_experiment_type.radiation_probe"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.radiation_probe", + "_easydiffraction_experiment_type.radiation_probe", + "_expt_type.radiation_probe" + ], + "unique_name": "experiment_type.radiation_probe" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.sample_form", + "cif_names": [ + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "context": "factory.ExperimentTypeFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "sample_form", + "descriptor_path": "factory.ExperimentTypeFactory.default.sample_form", + "docs_anchor": "experiment-type-sample-form", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.sample_form", + "edi_names": ["_experiment_type.sample_form"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.sample_form", + "_easydiffraction_experiment_type.sample_form", + "_expt_type.sample_form" + ], + "unique_name": "experiment_type.sample_form" + }, + { + "category_code": "experiment_type", + "category_entry_name": null, + "cif_name": "_easydiffraction_experiment_type.scattering_type", + "cif_names": [ + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "context": "factory.ExperimentTypeFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "scattering_type", + "descriptor_path": "factory.ExperimentTypeFactory.default.scattering_type", + "docs_anchor": "experiment-type-scattering-type", + "docs_page": "experiment_type", + "edi_name": "_experiment_type.scattering_type", + "edi_names": ["_experiment_type.scattering_type"], + "owner_class": "ExperimentType", + "read_names": [ + "_experiment_type.scattering_type", + "_easydiffraction_experiment_type.scattering_type", + "_expt_type.scattering_type" + ], + "unique_name": "experiment_type.scattering_type" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.model", + "cif_names": ["_easydiffraction_extinction.model"], + "context": "factory.ExtinctionFactory.becker-coppens", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "model", + "descriptor_path": "factory.ExtinctionFactory.becker-coppens.model", + "docs_anchor": "extinction-model", + "docs_page": "extinction", + "edi_name": "_extinction.model", + "edi_names": ["_extinction.model"], + "owner_class": "BeckerCoppensExtinction", + "read_names": ["_extinction.model", "_easydiffraction_extinction.model"], + "unique_name": "extinction.model" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.mosaicity", + "cif_names": ["_easydiffraction_extinction.mosaicity"], + "context": "factory.ExtinctionFactory.becker-coppens", + "descriptor_class": "Parameter", + "descriptor_name": "mosaicity", + "descriptor_path": "factory.ExtinctionFactory.becker-coppens.mosaicity", + "docs_anchor": "extinction-mosaicity", + "docs_page": "extinction", + "edi_name": "_extinction.mosaicity", + "edi_names": ["_extinction.mosaicity"], + "owner_class": "BeckerCoppensExtinction", + "read_names": [ + "_extinction.mosaicity", + "_easydiffraction_extinction.mosaicity" + ], + "unique_name": "extinction.mosaicity" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.radius", + "cif_names": ["_easydiffraction_extinction.radius"], + "context": "factory.ExtinctionFactory.becker-coppens", + "descriptor_class": "Parameter", + "descriptor_name": "radius", + "descriptor_path": "factory.ExtinctionFactory.becker-coppens.radius", + "docs_anchor": "extinction-radius", + "docs_page": "extinction", + "edi_name": "_extinction.radius", + "edi_names": ["_extinction.radius"], + "owner_class": "BeckerCoppensExtinction", + "read_names": [ + "_extinction.radius", + "_easydiffraction_extinction.radius" + ], + "unique_name": "extinction.radius" + }, + { + "category_code": "extinction", + "category_entry_name": null, + "cif_name": "_easydiffraction_extinction.type", + "cif_names": ["_easydiffraction_extinction.type"], + "context": "factory.ExtinctionFactory.becker-coppens", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.ExtinctionFactory.becker-coppens.type", + "docs_anchor": "extinction-type", + "docs_page": "extinction", + "edi_name": "_extinction.type", + "edi_names": ["_extinction.type"], + "owner_class": "BeckerCoppensExtinction", + "read_names": ["_extinction.type", "_easydiffraction_extinction.type"], + "unique_name": "extinction.type" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.correlation", + "cif_names": ["_fit_parameter_correlation.correlation"], + "context": "factory.FitParameterCorrelationsFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "correlation", + "descriptor_path": "factory.FitParameterCorrelationsFactory.default[].correlation", + "docs_anchor": "fit-parameter-correlation-correlation", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.correlation", + "edi_names": ["_fit_parameter_correlation.correlation"], + "owner_class": "FitParameterCorrelationItem", + "read_names": ["_fit_parameter_correlation.correlation"], + "unique_name": "fit_parameter_correlation._.correlation" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.id", + "cif_names": ["_fit_parameter_correlation.id"], + "context": "factory.FitParameterCorrelationsFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.FitParameterCorrelationsFactory.default[].id", + "docs_anchor": "fit-parameter-correlation-id", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.id", + "edi_names": ["_fit_parameter_correlation.id"], + "owner_class": "FitParameterCorrelationItem", + "read_names": ["_fit_parameter_correlation.id"], + "unique_name": "fit_parameter_correlation._.id" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.param_unique_name_i", + "cif_names": ["_fit_parameter_correlation.param_unique_name_i"], + "context": "factory.FitParameterCorrelationsFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name_i", + "descriptor_path": "factory.FitParameterCorrelationsFactory.default[].parameter_unique_name_i", + "docs_anchor": "fit-parameter-correlation-parameter-unique-name-i", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.parameter_unique_name_i", + "edi_names": ["_fit_parameter_correlation.parameter_unique_name_i"], + "owner_class": "FitParameterCorrelationItem", + "read_names": [ + "_fit_parameter_correlation.parameter_unique_name_i", + "_fit_parameter_correlation.param_unique_name_i" + ], + "unique_name": "fit_parameter_correlation._.parameter_unique_name_i" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.param_unique_name_j", + "cif_names": ["_fit_parameter_correlation.param_unique_name_j"], + "context": "factory.FitParameterCorrelationsFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name_j", + "descriptor_path": "factory.FitParameterCorrelationsFactory.default[].parameter_unique_name_j", + "docs_anchor": "fit-parameter-correlation-parameter-unique-name-j", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.parameter_unique_name_j", + "edi_names": ["_fit_parameter_correlation.parameter_unique_name_j"], + "owner_class": "FitParameterCorrelationItem", + "read_names": [ + "_fit_parameter_correlation.parameter_unique_name_j", + "_fit_parameter_correlation.param_unique_name_j" + ], + "unique_name": "fit_parameter_correlation._.parameter_unique_name_j" + }, + { + "category_code": "fit_parameter_correlation", + "category_entry_name": "_", + "cif_name": "_fit_parameter_correlation.source_kind", + "cif_names": ["_fit_parameter_correlation.source_kind"], + "context": "factory.FitParameterCorrelationsFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "source_kind", + "descriptor_path": "factory.FitParameterCorrelationsFactory.default[].source_kind", + "docs_anchor": "fit-parameter-correlation-source-kind", + "docs_page": "fit_parameter_correlation", + "edi_name": "_fit_parameter_correlation.source_kind", + "edi_names": ["_fit_parameter_correlation.source_kind"], + "owner_class": "FitParameterCorrelationItem", + "read_names": ["_fit_parameter_correlation.source_kind"], + "unique_name": "fit_parameter_correlation._.source_kind" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.fit_bounds_uncertainty_multiplier", + "cif_names": ["_fit_parameter.fit_bounds_uncertainty_multiplier"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "bounds_uncertainty_multiplier", + "descriptor_path": "factory.FitParametersFactory.default[].bounds_uncertainty_multiplier", + "docs_anchor": "fit-parameter-bounds-uncertainty-multiplier", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.bounds_uncertainty_multiplier", + "edi_names": ["_fit_parameter.bounds_uncertainty_multiplier"], + "owner_class": "FitParameterItem", + "read_names": [ + "_fit_parameter.bounds_uncertainty_multiplier", + "_fit_parameter.fit_bounds_uncertainty_multiplier" + ], + "unique_name": "fit_parameter._.bounds_uncertainty_multiplier" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.fit_max", + "cif_names": ["_fit_parameter.fit_max"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fit_max", + "descriptor_path": "factory.FitParametersFactory.default[].fit_max", + "docs_anchor": "fit-parameter-fit-max", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.fit_max", + "edi_names": ["_fit_parameter.fit_max"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.fit_max"], + "unique_name": "fit_parameter._.fit_max" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.fit_min", + "cif_names": ["_fit_parameter.fit_min"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fit_min", + "descriptor_path": "factory.FitParametersFactory.default[].fit_min", + "docs_anchor": "fit-parameter-fit-min", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.fit_min", + "edi_names": ["_fit_parameter.fit_min"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.fit_min"], + "unique_name": "fit_parameter._.fit_min" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.param_unique_name", + "cif_names": ["_fit_parameter.param_unique_name"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "parameter_unique_name", + "descriptor_path": "factory.FitParametersFactory.default[].parameter_unique_name", + "docs_anchor": "fit-parameter-parameter-unique-name", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.parameter_unique_name", + "edi_names": ["_fit_parameter.parameter_unique_name"], + "owner_class": "FitParameterItem", + "read_names": [ + "_fit_parameter.parameter_unique_name", + "_fit_parameter.param_unique_name" + ], + "unique_name": "fit_parameter._.parameter_unique_name" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_best_sample_value", + "cif_names": ["_fit_parameter.posterior_best_sample_value"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_best_sample_value", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_best_sample_value", + "docs_anchor": "fit-parameter-posterior-best-sample-value", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_best_sample_value", + "edi_names": ["_fit_parameter.posterior_best_sample_value"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_best_sample_value"], + "unique_name": "fit_parameter._.posterior_best_sample_value" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_effective_sample_size_bulk", + "cif_names": ["_fit_parameter.posterior_effective_sample_size_bulk"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_effective_sample_size_bulk", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_effective_sample_size_bulk", + "docs_anchor": "fit-parameter-posterior-effective-sample-size-bulk", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_effective_sample_size_bulk", + "edi_names": ["_fit_parameter.posterior_effective_sample_size_bulk"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_effective_sample_size_bulk"], + "unique_name": "fit_parameter._.posterior_effective_sample_size_bulk" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_gelman_rubin", + "cif_names": ["_fit_parameter.posterior_gelman_rubin"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_gelman_rubin", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_gelman_rubin", + "docs_anchor": "fit-parameter-posterior-gelman-rubin", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_gelman_rubin", + "edi_names": ["_fit_parameter.posterior_gelman_rubin"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_gelman_rubin"], + "unique_name": "fit_parameter._.posterior_gelman_rubin" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_68_high", + "cif_names": ["_fit_parameter.posterior_interval_68_high"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_68_high", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_interval_68_high", + "docs_anchor": "fit-parameter-posterior-interval-68-high", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_68_high", + "edi_names": ["_fit_parameter.posterior_interval_68_high"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_68_high"], + "unique_name": "fit_parameter._.posterior_interval_68_high" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_68_low", + "cif_names": ["_fit_parameter.posterior_interval_68_low"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_68_low", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_interval_68_low", + "docs_anchor": "fit-parameter-posterior-interval-68-low", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_68_low", + "edi_names": ["_fit_parameter.posterior_interval_68_low"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_68_low"], + "unique_name": "fit_parameter._.posterior_interval_68_low" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_95_high", + "cif_names": ["_fit_parameter.posterior_interval_95_high"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_95_high", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_interval_95_high", + "docs_anchor": "fit-parameter-posterior-interval-95-high", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_95_high", + "edi_names": ["_fit_parameter.posterior_interval_95_high"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_95_high"], + "unique_name": "fit_parameter._.posterior_interval_95_high" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_interval_95_low", + "cif_names": ["_fit_parameter.posterior_interval_95_low"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_interval_95_low", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_interval_95_low", + "docs_anchor": "fit-parameter-posterior-interval-95-low", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_interval_95_low", + "edi_names": ["_fit_parameter.posterior_interval_95_low"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_interval_95_low"], + "unique_name": "fit_parameter._.posterior_interval_95_low" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_median", + "cif_names": ["_fit_parameter.posterior_median"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_median", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_median", + "docs_anchor": "fit-parameter-posterior-median", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_median", + "edi_names": ["_fit_parameter.posterior_median"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_median"], + "unique_name": "fit_parameter._.posterior_median" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.posterior_uncertainty", + "cif_names": ["_fit_parameter.posterior_uncertainty"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "posterior_uncertainty", + "descriptor_path": "factory.FitParametersFactory.default[].posterior_uncertainty", + "docs_anchor": "fit-parameter-posterior-uncertainty", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.posterior_uncertainty", + "edi_names": ["_fit_parameter.posterior_uncertainty"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.posterior_uncertainty"], + "unique_name": "fit_parameter._.posterior_uncertainty" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.start_uncertainty", + "cif_names": ["_fit_parameter.start_uncertainty"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start_uncertainty", + "descriptor_path": "factory.FitParametersFactory.default[].start_uncertainty", + "docs_anchor": "fit-parameter-start-uncertainty", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.start_uncertainty", + "edi_names": ["_fit_parameter.start_uncertainty"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.start_uncertainty"], + "unique_name": "fit_parameter._.start_uncertainty" + }, + { + "category_code": "fit_parameter", + "category_entry_name": "_", + "cif_name": "_fit_parameter.start_value", + "cif_names": ["_fit_parameter.start_value"], + "context": "factory.FitParametersFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "start_value", + "descriptor_path": "factory.FitParametersFactory.default[].start_value", + "docs_anchor": "fit-parameter-start-value", + "docs_page": "fit_parameter", + "edi_name": "_fit_parameter.start_value", + "edi_names": ["_fit_parameter.start_value"], + "owner_class": "FitParameterItem", + "read_names": ["_fit_parameter.start_value"], + "unique_name": "fit_parameter._.start_value" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.acceptance_rate_mean", + "cif_names": ["_easydiffraction_fit_result.acceptance_rate_mean"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "acceptance_rate_mean", + "descriptor_path": "factory.FitResultFactory.bayesian.acceptance_rate_mean", + "docs_anchor": "fit-result-acceptance-rate-mean", + "docs_page": "fit_result", + "edi_name": "_fit_result.acceptance_rate_mean", + "edi_names": ["_fit_result.acceptance_rate_mean"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.acceptance_rate_mean", + "_easydiffraction_fit_result.acceptance_rate_mean" + ], + "unique_name": "fit_result.acceptance_rate_mean" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.best_log_posterior", + "cif_names": ["_easydiffraction_fit_result.best_log_posterior"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "best_log_posterior", + "descriptor_path": "factory.FitResultFactory.bayesian.best_log_posterior", + "docs_anchor": "fit-result-best-log-posterior", + "docs_page": "fit_result", + "edi_name": "_fit_result.best_log_posterior", + "edi_names": ["_fit_result.best_log_posterior"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.best_log_posterior", + "_easydiffraction_fit_result.best_log_posterior" + ], + "unique_name": "fit_result.best_log_posterior" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.credible_interval_inner", + "cif_names": ["_easydiffraction_fit_result.credible_interval_inner"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "credible_interval_inner", + "descriptor_path": "factory.FitResultFactory.bayesian.credible_interval_inner", + "docs_anchor": "fit-result-credible-interval-inner", + "docs_page": "fit_result", + "edi_name": "_fit_result.credible_interval_inner", + "edi_names": ["_fit_result.credible_interval_inner"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.credible_interval_inner", + "_easydiffraction_fit_result.credible_interval_inner" + ], + "unique_name": "fit_result.credible_interval_inner" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.credible_interval_outer", + "cif_names": ["_easydiffraction_fit_result.credible_interval_outer"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "credible_interval_outer", + "descriptor_path": "factory.FitResultFactory.bayesian.credible_interval_outer", + "docs_anchor": "fit-result-credible-interval-outer", + "docs_page": "fit_result", + "edi_name": "_fit_result.credible_interval_outer", + "edi_names": ["_fit_result.credible_interval_outer"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.credible_interval_outer", + "_easydiffraction_fit_result.credible_interval_outer" + ], + "unique_name": "fit_result.credible_interval_outer" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.effective_sample_size_min", + "cif_names": ["_easydiffraction_fit_result.effective_sample_size_min"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "effective_sample_size_min", + "descriptor_path": "factory.FitResultFactory.bayesian.effective_sample_size_min", + "docs_anchor": "fit-result-effective-sample-size-min", + "docs_page": "fit_result", + "edi_name": "_fit_result.effective_sample_size_min", + "edi_names": ["_fit_result.effective_sample_size_min"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.effective_sample_size_min", + "_easydiffraction_fit_result.effective_sample_size_min" + ], + "unique_name": "fit_result.effective_sample_size_min" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.fitting_time", + "cif_names": ["_fit_result.fitting_time"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fitting_time", + "descriptor_path": "factory.FitResultFactory.bayesian.fitting_time", + "docs_anchor": "fit-result-fitting-time", + "docs_page": "fit_result", + "edi_name": "_fit_result.fitting_time", + "edi_names": ["_fit_result.fitting_time"], + "owner_class": "BayesianFitResult", + "read_names": ["_fit_result.fitting_time"], + "unique_name": "fit_result.fitting_time" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.gelman_rubin_max", + "cif_names": ["_easydiffraction_fit_result.gelman_rubin_max"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "gelman_rubin_max", + "descriptor_path": "factory.FitResultFactory.bayesian.gelman_rubin_max", + "docs_anchor": "fit-result-gelman-rubin-max", + "docs_page": "fit_result", + "edi_name": "_fit_result.gelman_rubin_max", + "edi_names": ["_fit_result.gelman_rubin_max"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.gelman_rubin_max", + "_easydiffraction_fit_result.gelman_rubin_max" + ], + "unique_name": "fit_result.gelman_rubin_max" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.iterations", + "cif_names": ["_fit_result.iterations"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "iterations", + "descriptor_path": "factory.FitResultFactory.bayesian.iterations", + "docs_anchor": "fit-result-iterations", + "docs_page": "fit_result", + "edi_name": "_fit_result.iterations", + "edi_names": ["_fit_result.iterations"], + "owner_class": "BayesianFitResult", + "read_names": ["_fit_result.iterations"], + "unique_name": "fit_result.iterations" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.message", + "cif_names": ["_fit_result.message"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "StringDescriptor", + "descriptor_name": "message", + "descriptor_path": "factory.FitResultFactory.bayesian.message", + "docs_anchor": "fit-result-message", + "docs_page": "fit_result", + "edi_name": "_fit_result.message", + "edi_names": ["_fit_result.message"], + "owner_class": "BayesianFitResult", + "read_names": ["_fit_result.message"], + "unique_name": "fit_result.message" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.point_estimate_name", + "cif_names": ["_easydiffraction_fit_result.point_estimate_name"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "StringDescriptor", + "descriptor_name": "point_estimate_name", + "descriptor_path": "factory.FitResultFactory.bayesian.point_estimate_name", + "docs_anchor": "fit-result-point-estimate-name", + "docs_page": "fit_result", + "edi_name": "_fit_result.point_estimate_name", + "edi_names": ["_fit_result.point_estimate_name"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.point_estimate_name", + "_easydiffraction_fit_result.point_estimate_name" + ], + "unique_name": "fit_result.point_estimate_name" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.reduced_chi_square", + "cif_names": ["_fit_result.reduced_chi_square"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "reduced_chi_square", + "descriptor_path": "factory.FitResultFactory.bayesian.reduced_chi_square", + "docs_anchor": "fit-result-reduced-chi-square", + "docs_page": "fit_result", + "edi_name": "_fit_result.reduced_chi_square", + "edi_names": ["_fit_result.reduced_chi_square"], + "owner_class": "BayesianFitResult", + "read_names": ["_fit_result.reduced_chi_square"], + "unique_name": "fit_result.reduced_chi_square" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.resolved_random_seed", + "cif_names": ["_easydiffraction_fit_result.resolved_random_seed"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "resolved_random_seed", + "descriptor_path": "factory.FitResultFactory.bayesian.resolved_random_seed", + "docs_anchor": "fit-result-resolved-random-seed", + "docs_page": "fit_result", + "edi_name": "_fit_result.resolved_random_seed", + "edi_names": ["_fit_result.resolved_random_seed"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.resolved_random_seed", + "_easydiffraction_fit_result.resolved_random_seed" + ], + "unique_name": "fit_result.resolved_random_seed" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.result_kind", + "cif_names": ["_fit_result.result_kind"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "result_kind", + "descriptor_path": "factory.FitResultFactory.bayesian.result_kind", + "docs_anchor": "fit-result-result-kind", + "docs_page": "fit_result", + "edi_name": "_fit_result.result_kind", + "edi_names": ["_fit_result.result_kind"], + "owner_class": "BayesianFitResult", + "read_names": ["_fit_result.result_kind"], + "unique_name": "fit_result.result_kind" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_easydiffraction_fit_result.sampler_completed", + "cif_names": ["_easydiffraction_fit_result.sampler_completed"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "sampler_completed", + "descriptor_path": "factory.FitResultFactory.bayesian.sampler_completed", + "docs_anchor": "fit-result-sampler-completed", + "docs_page": "fit_result", + "edi_name": "_fit_result.sampler_completed", + "edi_names": ["_fit_result.sampler_completed"], + "owner_class": "BayesianFitResult", + "read_names": [ + "_fit_result.sampler_completed", + "_easydiffraction_fit_result.sampler_completed" + ], + "unique_name": "fit_result.sampler_completed" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.success", + "cif_names": ["_fit_result.success"], + "context": "factory.FitResultFactory.bayesian", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "success", + "descriptor_path": "factory.FitResultFactory.bayesian.success", + "docs_anchor": "fit-result-success", + "docs_page": "fit_result", + "edi_name": "_fit_result.success", + "edi_names": ["_fit_result.success"], + "owner_class": "BayesianFitResult", + "read_names": ["_fit_result.success"], + "unique_name": "fit_result.success" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.fitting_time", + "cif_names": ["_fit_result.fitting_time"], + "context": "factory.FitResultFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fitting_time", + "descriptor_path": "factory.FitResultFactory.default.fitting_time", + "docs_anchor": "fit-result-fitting-time", + "docs_page": "fit_result", + "edi_name": "_fit_result.fitting_time", + "edi_names": ["_fit_result.fitting_time"], + "owner_class": "FitResultBase", + "read_names": ["_fit_result.fitting_time"], + "unique_name": "fit_result.fitting_time" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.iterations", + "cif_names": ["_fit_result.iterations"], + "context": "factory.FitResultFactory.default", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "iterations", + "descriptor_path": "factory.FitResultFactory.default.iterations", + "docs_anchor": "fit-result-iterations", + "docs_page": "fit_result", + "edi_name": "_fit_result.iterations", + "edi_names": ["_fit_result.iterations"], + "owner_class": "FitResultBase", + "read_names": ["_fit_result.iterations"], + "unique_name": "fit_result.iterations" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.message", + "cif_names": ["_fit_result.message"], + "context": "factory.FitResultFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "message", + "descriptor_path": "factory.FitResultFactory.default.message", + "docs_anchor": "fit-result-message", + "docs_page": "fit_result", + "edi_name": "_fit_result.message", + "edi_names": ["_fit_result.message"], + "owner_class": "FitResultBase", + "read_names": ["_fit_result.message"], + "unique_name": "fit_result.message" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.reduced_chi_square", + "cif_names": ["_fit_result.reduced_chi_square"], + "context": "factory.FitResultFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "reduced_chi_square", + "descriptor_path": "factory.FitResultFactory.default.reduced_chi_square", + "docs_anchor": "fit-result-reduced-chi-square", + "docs_page": "fit_result", + "edi_name": "_fit_result.reduced_chi_square", + "edi_names": ["_fit_result.reduced_chi_square"], + "owner_class": "FitResultBase", + "read_names": ["_fit_result.reduced_chi_square"], + "unique_name": "fit_result.reduced_chi_square" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.result_kind", + "cif_names": ["_fit_result.result_kind"], + "context": "factory.FitResultFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "result_kind", + "descriptor_path": "factory.FitResultFactory.default.result_kind", + "docs_anchor": "fit-result-result-kind", + "docs_page": "fit_result", + "edi_name": "_fit_result.result_kind", + "edi_names": ["_fit_result.result_kind"], + "owner_class": "FitResultBase", + "read_names": ["_fit_result.result_kind"], + "unique_name": "fit_result.result_kind" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.success", + "cif_names": ["_fit_result.success"], + "context": "factory.FitResultFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "success", + "descriptor_path": "factory.FitResultFactory.default.success", + "docs_anchor": "fit-result-success", + "docs_page": "fit_result", + "edi_name": "_fit_result.success", + "edi_names": ["_fit_result.success"], + "owner_class": "FitResultBase", + "read_names": ["_fit_result.success"], + "unique_name": "fit_result.success" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.background_function", + "cif_names": ["_fit_result.background_function"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "StringDescriptor", + "descriptor_name": "background_function", + "descriptor_path": "factory.FitResultFactory.least_squares.background_function", + "docs_anchor": "fit-result-background-function", + "docs_page": "fit_result", + "edi_name": "_fit_result.background_function", + "edi_names": ["_fit_result.background_function"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.background_function"], + "unique_name": "fit_result.background_function" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.correlation_available", + "cif_names": ["_fit_result.correlation_available"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "correlation_available", + "descriptor_path": "factory.FitResultFactory.least_squares.correlation_available", + "docs_anchor": "fit-result-correlation-available", + "docs_page": "fit_result", + "edi_name": "_fit_result.correlation_available", + "edi_names": ["_fit_result.correlation_available"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.correlation_available"], + "unique_name": "fit_result.correlation_available" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.covariance_available", + "cif_names": ["_fit_result.covariance_available"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "covariance_available", + "descriptor_path": "factory.FitResultFactory.least_squares.covariance_available", + "docs_anchor": "fit-result-covariance-available", + "docs_page": "fit_result", + "edi_name": "_fit_result.covariance_available", + "edi_names": ["_fit_result.covariance_available"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.covariance_available"], + "unique_name": "fit_result.covariance_available" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.degrees_of_freedom", + "cif_names": ["_fit_result.degrees_of_freedom"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "degrees_of_freedom", + "descriptor_path": "factory.FitResultFactory.least_squares.degrees_of_freedom", + "docs_anchor": "fit-result-degrees-of-freedom", + "docs_page": "fit_result", + "edi_name": "_fit_result.degrees_of_freedom", + "edi_names": ["_fit_result.degrees_of_freedom"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.degrees_of_freedom"], + "unique_name": "fit_result.degrees_of_freedom" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.exit_reason", + "cif_names": ["_fit_result.exit_reason"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "StringDescriptor", + "descriptor_name": "exit_reason", + "descriptor_path": "factory.FitResultFactory.least_squares.exit_reason", + "docs_anchor": "fit-result-exit-reason", + "docs_page": "fit_result", + "edi_name": "_fit_result.exit_reason", + "edi_names": ["_fit_result.exit_reason"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.exit_reason"], + "unique_name": "fit_result.exit_reason" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.fitting_time", + "cif_names": ["_fit_result.fitting_time"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "fitting_time", + "descriptor_path": "factory.FitResultFactory.least_squares.fitting_time", + "docs_anchor": "fit-result-fitting-time", + "docs_page": "fit_result", + "edi_name": "_fit_result.fitting_time", + "edi_names": ["_fit_result.fitting_time"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.fitting_time"], + "unique_name": "fit_result.fitting_time" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.iterations", + "cif_names": ["_fit_result.iterations"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "iterations", + "descriptor_path": "factory.FitResultFactory.least_squares.iterations", + "docs_anchor": "fit-result-iterations", + "docs_page": "fit_result", + "edi_name": "_fit_result.iterations", + "edi_names": ["_fit_result.iterations"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.iterations"], + "unique_name": "fit_result.iterations" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.message", + "cif_names": ["_fit_result.message"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "StringDescriptor", + "descriptor_name": "message", + "descriptor_path": "factory.FitResultFactory.least_squares.message", + "docs_anchor": "fit-result-message", + "docs_page": "fit_result", + "edi_name": "_fit_result.message", + "edi_names": ["_fit_result.message"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.message"], + "unique_name": "fit_result.message" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.n_data_points", + "cif_names": ["_fit_result.n_data_points"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "n_data_points", + "descriptor_path": "factory.FitResultFactory.least_squares.n_data_points", + "docs_anchor": "fit-result-n-data-points", + "docs_page": "fit_result", + "edi_name": "_fit_result.n_data_points", + "edi_names": ["_fit_result.n_data_points"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.n_data_points"], + "unique_name": "fit_result.n_data_points" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.n_free_parameters", + "cif_names": ["_fit_result.n_free_parameters"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "n_free_parameters", + "descriptor_path": "factory.FitResultFactory.least_squares.n_free_parameters", + "docs_anchor": "fit-result-n-free-parameters", + "docs_page": "fit_result", + "edi_name": "_fit_result.n_free_parameters", + "edi_names": ["_fit_result.n_free_parameters"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.n_free_parameters"], + "unique_name": "fit_result.n_free_parameters" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.n_parameters", + "cif_names": ["_fit_result.n_parameters"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "n_parameters", + "descriptor_path": "factory.FitResultFactory.least_squares.n_parameters", + "docs_anchor": "fit-result-n-parameters", + "docs_page": "fit_result", + "edi_name": "_fit_result.n_parameters", + "edi_names": ["_fit_result.n_parameters"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.n_parameters"], + "unique_name": "fit_result.n_parameters" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_constraints", + "cif_names": ["_fit_result.number_constraints"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_constraints", + "descriptor_path": "factory.FitResultFactory.least_squares.number_constraints", + "docs_anchor": "fit-result-number-constraints", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_constraints", + "edi_names": ["_fit_result.number_constraints"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_constraints"], + "unique_name": "fit_result.number_constraints" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_reflns_gt", + "cif_names": ["_fit_result.number_reflns_gt"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_reflns_gt", + "descriptor_path": "factory.FitResultFactory.least_squares.number_reflns_gt", + "docs_anchor": "fit-result-number-reflns-gt", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_reflns_gt", + "edi_names": ["_fit_result.number_reflns_gt"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_reflns_gt"], + "unique_name": "fit_result.number_reflns_gt" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_reflns_total", + "cif_names": ["_fit_result.number_reflns_total"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_reflns_total", + "descriptor_path": "factory.FitResultFactory.least_squares.number_reflns_total", + "docs_anchor": "fit-result-number-reflns-total", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_reflns_total", + "edi_names": ["_fit_result.number_reflns_total"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_reflns_total"], + "unique_name": "fit_result.number_reflns_total" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.number_restraints", + "cif_names": ["_fit_result.number_restraints"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "number_restraints", + "descriptor_path": "factory.FitResultFactory.least_squares.number_restraints", + "docs_anchor": "fit-result-number-restraints", + "docs_page": "fit_result", + "edi_name": "_fit_result.number_restraints", + "edi_names": ["_fit_result.number_restraints"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.number_restraints"], + "unique_name": "fit_result.number_restraints" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.objective_name", + "cif_names": ["_fit_result.objective_name"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "StringDescriptor", + "descriptor_name": "objective_name", + "descriptor_path": "factory.FitResultFactory.least_squares.objective_name", + "docs_anchor": "fit-result-objective-name", + "docs_page": "fit_result", + "edi_name": "_fit_result.objective_name", + "edi_names": ["_fit_result.objective_name"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.objective_name"], + "unique_name": "fit_result.objective_name" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.objective_value", + "cif_names": ["_fit_result.objective_value"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "objective_value", + "descriptor_path": "factory.FitResultFactory.least_squares.objective_value", + "docs_anchor": "fit-result-objective-value", + "docs_page": "fit_result", + "edi_name": "_fit_result.objective_value", + "edi_names": ["_fit_result.objective_value"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.objective_value"], + "unique_name": "fit_result.objective_value" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.prof_R_factor", + "cif_names": ["_fit_result.prof_R_factor"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "prof_r_factor", + "descriptor_path": "factory.FitResultFactory.least_squares.prof_r_factor", + "docs_anchor": "fit-result-prof-r-factor", + "docs_page": "fit_result", + "edi_name": "_fit_result.prof_r_factor", + "edi_names": ["_fit_result.prof_r_factor"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.prof_r_factor", "_fit_result.prof_R_factor"], + "unique_name": "fit_result.prof_r_factor" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.prof_wR_expected", + "cif_names": ["_fit_result.prof_wR_expected"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "prof_wr_expected", + "descriptor_path": "factory.FitResultFactory.least_squares.prof_wr_expected", + "docs_anchor": "fit-result-prof-wr-expected", + "docs_page": "fit_result", + "edi_name": "_fit_result.prof_wr_expected", + "edi_names": ["_fit_result.prof_wr_expected"], + "owner_class": "LeastSquaresFitResult", + "read_names": [ + "_fit_result.prof_wr_expected", + "_fit_result.prof_wR_expected" + ], + "unique_name": "fit_result.prof_wr_expected" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.prof_wR_factor", + "cif_names": ["_fit_result.prof_wR_factor"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "prof_wr_factor", + "descriptor_path": "factory.FitResultFactory.least_squares.prof_wr_factor", + "docs_anchor": "fit-result-prof-wr-factor", + "docs_page": "fit_result", + "edi_name": "_fit_result.prof_wr_factor", + "edi_names": ["_fit_result.prof_wr_factor"], + "owner_class": "LeastSquaresFitResult", + "read_names": [ + "_fit_result.prof_wr_factor", + "_fit_result.prof_wR_factor" + ], + "unique_name": "fit_result.prof_wr_factor" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.profile_function", + "cif_names": ["_fit_result.profile_function"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "StringDescriptor", + "descriptor_name": "profile_function", + "descriptor_path": "factory.FitResultFactory.least_squares.profile_function", + "docs_anchor": "fit-result-profile-function", + "docs_page": "fit_result", + "edi_name": "_fit_result.profile_function", + "edi_names": ["_fit_result.profile_function"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.profile_function"], + "unique_name": "fit_result.profile_function" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.R_factor_all", + "cif_names": ["_fit_result.R_factor_all"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "r_factor_all", + "descriptor_path": "factory.FitResultFactory.least_squares.r_factor_all", + "docs_anchor": "fit-result-r-factor-all", + "docs_page": "fit_result", + "edi_name": "_fit_result.r_factor_all", + "edi_names": ["_fit_result.r_factor_all"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.r_factor_all", "_fit_result.R_factor_all"], + "unique_name": "fit_result.r_factor_all" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.R_factor_gt", + "cif_names": ["_fit_result.R_factor_gt"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "r_factor_gt", + "descriptor_path": "factory.FitResultFactory.least_squares.r_factor_gt", + "docs_anchor": "fit-result-r-factor-gt", + "docs_page": "fit_result", + "edi_name": "_fit_result.r_factor_gt", + "edi_names": ["_fit_result.r_factor_gt"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.r_factor_gt", "_fit_result.R_factor_gt"], + "unique_name": "fit_result.r_factor_gt" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.reduced_chi_square", + "cif_names": ["_fit_result.reduced_chi_square"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "reduced_chi_square", + "descriptor_path": "factory.FitResultFactory.least_squares.reduced_chi_square", + "docs_anchor": "fit-result-reduced-chi-square", + "docs_page": "fit_result", + "edi_name": "_fit_result.reduced_chi_square", + "edi_names": ["_fit_result.reduced_chi_square"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.reduced_chi_square"], + "unique_name": "fit_result.reduced_chi_square" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.result_kind", + "cif_names": ["_fit_result.result_kind"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "result_kind", + "descriptor_path": "factory.FitResultFactory.least_squares.result_kind", + "docs_anchor": "fit-result-result-kind", + "docs_page": "fit_result", + "edi_name": "_fit_result.result_kind", + "edi_names": ["_fit_result.result_kind"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.result_kind"], + "unique_name": "fit_result.result_kind" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.shift_over_su_max", + "cif_names": ["_fit_result.shift_over_su_max"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "shift_over_su_max", + "descriptor_path": "factory.FitResultFactory.least_squares.shift_over_su_max", + "docs_anchor": "fit-result-shift-over-su-max", + "docs_page": "fit_result", + "edi_name": "_fit_result.shift_over_su_max", + "edi_names": ["_fit_result.shift_over_su_max"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.shift_over_su_max"], + "unique_name": "fit_result.shift_over_su_max" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.shift_over_su_mean", + "cif_names": ["_fit_result.shift_over_su_mean"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "shift_over_su_mean", + "descriptor_path": "factory.FitResultFactory.least_squares.shift_over_su_mean", + "docs_anchor": "fit-result-shift-over-su-mean", + "docs_page": "fit_result", + "edi_name": "_fit_result.shift_over_su_mean", + "edi_names": ["_fit_result.shift_over_su_mean"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.shift_over_su_mean"], + "unique_name": "fit_result.shift_over_su_mean" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.success", + "cif_names": ["_fit_result.success"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "success", + "descriptor_path": "factory.FitResultFactory.least_squares.success", + "docs_anchor": "fit-result-success", + "docs_page": "fit_result", + "edi_name": "_fit_result.success", + "edi_names": ["_fit_result.success"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.success"], + "unique_name": "fit_result.success" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.threshold_expression", + "cif_names": ["_fit_result.threshold_expression"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "StringDescriptor", + "descriptor_name": "threshold_expression", + "descriptor_path": "factory.FitResultFactory.least_squares.threshold_expression", + "docs_anchor": "fit-result-threshold-expression", + "docs_page": "fit_result", + "edi_name": "_fit_result.threshold_expression", + "edi_names": ["_fit_result.threshold_expression"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.threshold_expression"], + "unique_name": "fit_result.threshold_expression" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.wR_factor_all", + "cif_names": ["_fit_result.wR_factor_all"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "wr_factor_all", + "descriptor_path": "factory.FitResultFactory.least_squares.wr_factor_all", + "docs_anchor": "fit-result-wr-factor-all", + "docs_page": "fit_result", + "edi_name": "_fit_result.wr_factor_all", + "edi_names": ["_fit_result.wr_factor_all"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.wr_factor_all", "_fit_result.wR_factor_all"], + "unique_name": "fit_result.wr_factor_all" + }, + { + "category_code": "fit_result", + "category_entry_name": null, + "cif_name": "_fit_result.wR_factor_gt", + "cif_names": ["_fit_result.wR_factor_gt"], + "context": "factory.FitResultFactory.least_squares", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "wr_factor_gt", + "descriptor_path": "factory.FitResultFactory.least_squares.wr_factor_gt", + "docs_anchor": "fit-result-wr-factor-gt", + "docs_page": "fit_result", + "edi_name": "_fit_result.wr_factor_gt", + "edi_names": ["_fit_result.wr_factor_gt"], + "owner_class": "LeastSquaresFitResult", + "read_names": ["_fit_result.wr_factor_gt", "_fit_result.wR_factor_gt"], + "unique_name": "fit_result.wr_factor_gt" + }, + { + "category_code": "fitting_mode", + "category_entry_name": null, + "cif_name": "_easydiffraction_fitting_mode.type", + "cif_names": ["_easydiffraction_fitting_mode.type"], + "context": "factory.FittingModeFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.FittingModeFactory.default.type", + "docs_anchor": "fitting-mode-type", + "docs_page": "fitting_mode", + "edi_name": "_fitting_mode.type", + "edi_names": ["_fitting_mode.type"], + "owner_class": "FittingMode", + "read_names": [ + "_fitting_mode.type", + "_easydiffraction_fitting_mode.type" + ], + "unique_name": "fitting_mode.type" + }, + { + "category_code": "geom", + "category_entry_name": null, + "cif_name": "_geom.bond_distance_incr", + "cif_names": ["_geom.bond_distance_incr"], + "context": "factory.GeomFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "bond_distance_inc", + "descriptor_path": "factory.GeomFactory.default.bond_distance_inc", + "docs_anchor": "geom-bond-distance-inc", + "docs_page": "geom", + "edi_name": "_geom.bond_distance_inc", + "edi_names": ["_geom.bond_distance_inc"], + "owner_class": "Geom", + "read_names": ["_geom.bond_distance_inc", "_geom.bond_distance_incr"], + "unique_name": "geom.bond_distance_inc" + }, + { + "category_code": "geom", + "category_entry_name": null, + "cif_name": "_geom.min_bond_distance_cutoff", + "cif_names": ["_geom.min_bond_distance_cutoff"], + "context": "factory.GeomFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "min_bond_distance_cutoff", + "descriptor_path": "factory.GeomFactory.default.min_bond_distance_cutoff", + "docs_anchor": "geom-min-bond-distance-cutoff", + "docs_page": "geom", + "edi_name": "_geom.min_bond_distance_cutoff", + "edi_names": ["_geom.min_bond_distance_cutoff"], + "owner_class": "Geom", + "read_names": ["_geom.min_bond_distance_cutoff"], + "unique_name": "geom.min_bond_distance_cutoff" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.sample_displacement", + "cif_names": ["_instr.sample_displacement"], + "context": "factory.InstrumentFactory.cwl-pd", + "descriptor_class": "Parameter", + "descriptor_name": "sample_displacement", + "descriptor_path": "factory.InstrumentFactory.cwl-pd.calib_sample_displacement", + "docs_anchor": "instrument-calib-sample-displacement", + "docs_page": "instrument", + "edi_name": "_instrument.calib_sample_displacement", + "edi_names": ["_instrument.calib_sample_displacement"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.calib_sample_displacement", + "_instr.sample_displacement" + ], + "unique_name": "instrument.sample_displacement" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.sample_transparency", + "cif_names": ["_instr.sample_transparency"], + "context": "factory.InstrumentFactory.cwl-pd", + "descriptor_class": "Parameter", + "descriptor_name": "sample_transparency", + "descriptor_path": "factory.InstrumentFactory.cwl-pd.calib_sample_transparency", + "docs_anchor": "instrument-calib-sample-transparency", + "docs_page": "instrument", + "edi_name": "_instrument.calib_sample_transparency", + "edi_names": ["_instrument.calib_sample_transparency"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.calib_sample_transparency", + "_instr.sample_transparency" + ], + "unique_name": "instrument.sample_transparency" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_pd_calib.2theta_offset", + "cif_names": ["_pd_calib.2theta_offset", "_instr.2theta_offset"], + "context": "factory.InstrumentFactory.cwl-pd", + "descriptor_class": "Parameter", + "descriptor_name": "twotheta_offset", + "descriptor_path": "factory.InstrumentFactory.cwl-pd.calib_twotheta_offset", + "docs_anchor": "instrument-calib-twotheta-offset", + "docs_page": "instrument", + "edi_name": "_instrument.calib_twotheta_offset", + "edi_names": ["_instrument.calib_twotheta_offset"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.calib_twotheta_offset", + "_pd_calib.2theta_offset", + "_instr.2theta_offset" + ], + "unique_name": "instrument.twotheta_offset" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_diffrn_radiation_wavelength.value", + "cif_names": ["_diffrn_radiation_wavelength.value", "_instr.wavelength"], + "context": "factory.InstrumentFactory.cwl-pd", + "descriptor_class": "Parameter", + "descriptor_name": "wavelength", + "descriptor_path": "factory.InstrumentFactory.cwl-pd.setup_wavelength", + "docs_anchor": "instrument-setup-wavelength", + "docs_page": "instrument", + "edi_name": "_instrument.setup_wavelength", + "edi_names": ["_instrument.setup_wavelength"], + "owner_class": "CwlPdInstrument", + "read_names": [ + "_instrument.setup_wavelength", + "_diffrn_radiation_wavelength.value", + "_instr.wavelength" + ], + "unique_name": "instrument.wavelength" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_diffrn_radiation_wavelength.value", + "cif_names": ["_diffrn_radiation_wavelength.value", "_instr.wavelength"], + "context": "factory.InstrumentFactory.cwl-sc", + "descriptor_class": "Parameter", + "descriptor_name": "wavelength", + "descriptor_path": "factory.InstrumentFactory.cwl-sc.setup_wavelength", + "docs_anchor": "instrument-setup-wavelength", + "docs_page": "instrument", + "edi_name": "_instrument.setup_wavelength", + "edi_names": ["_instrument.setup_wavelength"], + "owner_class": "CwlScInstrument", + "read_names": [ + "_instrument.setup_wavelength", + "_diffrn_radiation_wavelength.value", + "_instr.wavelength" + ], + "unique_name": "instrument.wavelength" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_linear", + "cif_names": ["_instr.d_to_tof_linear"], + "context": "factory.InstrumentFactory.tof-pd", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_linear", + "descriptor_path": "factory.InstrumentFactory.tof-pd.calib_d_to_tof_linear", + "docs_anchor": "instrument-calib-d-to-tof-linear", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_linear", + "edi_names": ["_instrument.calib_d_to_tof_linear"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_linear", + "_instr.d_to_tof_linear" + ], + "unique_name": "instrument.d_to_tof_linear" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_offset", + "cif_names": ["_instr.d_to_tof_offset"], + "context": "factory.InstrumentFactory.tof-pd", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_offset", + "descriptor_path": "factory.InstrumentFactory.tof-pd.calib_d_to_tof_offset", + "docs_anchor": "instrument-calib-d-to-tof-offset", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_offset", + "edi_names": ["_instrument.calib_d_to_tof_offset"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_offset", + "_instr.d_to_tof_offset" + ], + "unique_name": "instrument.d_to_tof_offset" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_quad", + "cif_names": ["_instr.d_to_tof_quad"], + "context": "factory.InstrumentFactory.tof-pd", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_quadratic", + "descriptor_path": "factory.InstrumentFactory.tof-pd.calib_d_to_tof_quadratic", + "docs_anchor": "instrument-calib-d-to-tof-quadratic", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_quadratic", + "edi_names": ["_instrument.calib_d_to_tof_quadratic"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_quadratic", + "_instr.d_to_tof_quad" + ], + "unique_name": "instrument.d_to_tof_quadratic" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.d_to_tof_recip", + "cif_names": ["_instr.d_to_tof_recip"], + "context": "factory.InstrumentFactory.tof-pd", + "descriptor_class": "Parameter", + "descriptor_name": "d_to_tof_reciprocal", + "descriptor_path": "factory.InstrumentFactory.tof-pd.calib_d_to_tof_reciprocal", + "docs_anchor": "instrument-calib-d-to-tof-reciprocal", + "docs_page": "instrument", + "edi_name": "_instrument.calib_d_to_tof_reciprocal", + "edi_names": ["_instrument.calib_d_to_tof_reciprocal"], + "owner_class": "TofPdInstrument", + "read_names": [ + "_instrument.calib_d_to_tof_reciprocal", + "_instr.d_to_tof_recip" + ], + "unique_name": "instrument.d_to_tof_reciprocal" + }, + { + "category_code": "instrument", + "category_entry_name": null, + "cif_name": "_instr.2theta_bank", + "cif_names": ["_instr.2theta_bank"], + "context": "factory.InstrumentFactory.tof-pd", + "descriptor_class": "Parameter", + "descriptor_name": "twotheta_bank", + "descriptor_path": "factory.InstrumentFactory.tof-pd.setup_twotheta_bank", + "docs_anchor": "instrument-setup-twotheta-bank", + "docs_page": "instrument", + "edi_name": "_instrument.setup_twotheta_bank", + "edi_names": ["_instrument.setup_twotheta_bank"], + "owner_class": "TofPdInstrument", + "read_names": ["_instrument.setup_twotheta_bank", "_instr.2theta_bank"], + "unique_name": "instrument.twotheta_bank" + }, + { + "category_code": "joint_fit", + "category_entry_name": "_", + "cif_name": "_easydiffraction_joint_fit.experiment_id", + "cif_names": ["_easydiffraction_joint_fit.experiment_id"], + "context": "factory.JointFitFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "experiment_id", + "descriptor_path": "factory.JointFitFactory.default[].experiment_id", + "docs_anchor": "joint-fit-experiment-id", + "docs_page": "joint_fit", + "edi_name": "_joint_fit.experiment_id", + "edi_names": ["_joint_fit.experiment_id"], + "owner_class": "JointFitItem", + "read_names": [ + "_joint_fit.experiment_id", + "_easydiffraction_joint_fit.experiment_id" + ], + "unique_name": "joint_fit._.experiment_id" + }, + { + "category_code": "joint_fit", + "category_entry_name": "_", + "cif_name": "_easydiffraction_joint_fit.weight", + "cif_names": ["_easydiffraction_joint_fit.weight"], + "context": "factory.JointFitFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "weight", + "descriptor_path": "factory.JointFitFactory.default[].weight", + "docs_anchor": "joint-fit-weight", + "docs_page": "joint_fit", + "edi_name": "_joint_fit.weight", + "edi_names": ["_joint_fit.weight"], + "owner_class": "JointFitItem", + "read_names": ["_joint_fit.weight", "_easydiffraction_joint_fit.weight"], + "unique_name": "joint_fit._.weight" + }, + { + "category_code": "linked_structure", + "category_entry_name": null, + "cif_name": "_easydiffraction_sc_crystal_block.scale", + "cif_names": [ + "_easydiffraction_sc_crystal_block.scale", + "_sc_crystal_block.scale" + ], + "context": "factory.LinkedStructureFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "factory.LinkedStructureFactory.default.scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": [ + "_linked_structure.scale", + "_easydiffraction_sc_crystal_block.scale", + "_sc_crystal_block.scale" + ], + "unique_name": "linked_structure.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": null, + "cif_name": "_easydiffraction_sc_crystal_block.id", + "cif_names": [ + "_easydiffraction_sc_crystal_block.id", + "_sc_crystal_block.id" + ], + "context": "factory.LinkedStructureFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "factory.LinkedStructureFactory.default.structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": [ + "_linked_structure.structure_id", + "_easydiffraction_sc_crystal_block.id", + "_sc_crystal_block.id" + ], + "unique_name": "linked_structure.structure_id" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.scale", + "cif_names": ["_pd_phase_block.scale"], + "context": "factory.LinkedStructuresFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "scale", + "descriptor_path": "factory.LinkedStructuresFactory.default[].scale", + "docs_anchor": "linked-structure-scale", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.scale", + "edi_names": ["_linked_structure.scale"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.scale", "_pd_phase_block.scale"], + "unique_name": "linked_structure.Si.scale" + }, + { + "category_code": "linked_structure", + "category_entry_name": "Si", + "cif_name": "_pd_phase_block.id", + "cif_names": ["_pd_phase_block.id"], + "context": "factory.LinkedStructuresFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "factory.LinkedStructuresFactory.default[].structure_id", + "docs_anchor": "linked-structure-structure-id", + "docs_page": "linked_structure", + "edi_name": "_linked_structure.structure_id", + "edi_names": ["_linked_structure.structure_id"], + "owner_class": "LinkedStructure", + "read_names": ["_linked_structure.structure_id", "_pd_phase_block.id"], + "unique_name": "linked_structure.Si.structure_id" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.bumps", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps.max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "BumpsMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.bumps", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps.type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "BumpsMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.bumps (amoeba)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (amoeba).max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "BumpsAmoebaMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.bumps (amoeba)", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (amoeba).type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "BumpsAmoebaMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.bumps (de)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (de).max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "BumpsDeMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.bumps (de)", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (de).type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "BumpsDeMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.burn_in_steps", + "cif_names": ["_easydiffraction_minimizer.burn_in_steps"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "burn_in_steps", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).burn_in_steps", + "docs_anchor": "minimizer-burn-in-steps", + "docs_page": "minimizer", + "edi_name": "_minimizer.burn_in_steps", + "edi_names": ["_minimizer.burn_in_steps"], + "owner_class": "BumpsDreamMinimizer", + "read_names": [ + "_minimizer.burn_in_steps", + "_easydiffraction_minimizer.burn_in_steps" + ], + "unique_name": "minimizer.burn_in_steps" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.initialization_method", + "cif_names": ["_easydiffraction_minimizer.initialization_method"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "StringDescriptor", + "descriptor_name": "initialization_method", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).initialization_method", + "docs_anchor": "minimizer-initialization-method", + "docs_page": "minimizer", + "edi_name": "_minimizer.initialization_method", + "edi_names": ["_minimizer.initialization_method"], + "owner_class": "BumpsDreamMinimizer", + "read_names": [ + "_minimizer.initialization_method", + "_easydiffraction_minimizer.initialization_method" + ], + "unique_name": "minimizer.initialization_method" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.parallel_workers", + "cif_names": ["_easydiffraction_minimizer.parallel_workers"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "parallel_workers", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).parallel_workers", + "docs_anchor": "minimizer-parallel-workers", + "docs_page": "minimizer", + "edi_name": "_minimizer.parallel_workers", + "edi_names": ["_minimizer.parallel_workers"], + "owner_class": "BumpsDreamMinimizer", + "read_names": [ + "_minimizer.parallel_workers", + "_easydiffraction_minimizer.parallel_workers" + ], + "unique_name": "minimizer.parallel_workers" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.population_size", + "cif_names": ["_easydiffraction_minimizer.population_size"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "population_size", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).population_size", + "docs_anchor": "minimizer-population-size", + "docs_page": "minimizer", + "edi_name": "_minimizer.population_size", + "edi_names": ["_minimizer.population_size"], + "owner_class": "BumpsDreamMinimizer", + "read_names": [ + "_minimizer.population_size", + "_easydiffraction_minimizer.population_size" + ], + "unique_name": "minimizer.population_size" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.random_seed", + "cif_names": ["_easydiffraction_minimizer.random_seed"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "random_seed", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).random_seed", + "docs_anchor": "minimizer-random-seed", + "docs_page": "minimizer", + "edi_name": "_minimizer.random_seed", + "edi_names": ["_minimizer.random_seed"], + "owner_class": "BumpsDreamMinimizer", + "read_names": [ + "_minimizer.random_seed", + "_easydiffraction_minimizer.random_seed" + ], + "unique_name": "minimizer.random_seed" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.sampling_steps", + "cif_names": ["_easydiffraction_minimizer.sampling_steps"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "sampling_steps", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).sampling_steps", + "docs_anchor": "minimizer-sampling-steps", + "docs_page": "minimizer", + "edi_name": "_minimizer.sampling_steps", + "edi_names": ["_minimizer.sampling_steps"], + "owner_class": "BumpsDreamMinimizer", + "read_names": [ + "_minimizer.sampling_steps", + "_easydiffraction_minimizer.sampling_steps" + ], + "unique_name": "minimizer.sampling_steps" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.thinning_interval", + "cif_names": ["_easydiffraction_minimizer.thinning_interval"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "thinning_interval", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).thinning_interval", + "docs_anchor": "minimizer-thinning-interval", + "docs_page": "minimizer", + "edi_name": "_minimizer.thinning_interval", + "edi_names": ["_minimizer.thinning_interval"], + "owner_class": "BumpsDreamMinimizer", + "read_names": [ + "_minimizer.thinning_interval", + "_easydiffraction_minimizer.thinning_interval" + ], + "unique_name": "minimizer.thinning_interval" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.bumps (dream)", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (dream).type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "BumpsDreamMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.bumps (lm)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (lm).max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "BumpsLmMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.bumps (lm)", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.bumps (lm).type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "BumpsLmMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.dfols", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.dfols.max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "DfolsMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.dfols", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.dfols.type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "DfolsMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.burn_in_steps", + "cif_names": ["_easydiffraction_minimizer.burn_in_steps"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "burn_in_steps", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.burn_in_steps", + "docs_anchor": "minimizer-burn-in-steps", + "docs_page": "minimizer", + "edi_name": "_minimizer.burn_in_steps", + "edi_names": ["_minimizer.burn_in_steps"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.burn_in_steps", + "_easydiffraction_minimizer.burn_in_steps" + ], + "unique_name": "minimizer.burn_in_steps" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.initialization_method", + "cif_names": ["_easydiffraction_minimizer.initialization_method"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "StringDescriptor", + "descriptor_name": "initialization_method", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.initialization_method", + "docs_anchor": "minimizer-initialization-method", + "docs_page": "minimizer", + "edi_name": "_minimizer.initialization_method", + "edi_names": ["_minimizer.initialization_method"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.initialization_method", + "_easydiffraction_minimizer.initialization_method" + ], + "unique_name": "minimizer.initialization_method" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.parallel_workers", + "cif_names": ["_easydiffraction_minimizer.parallel_workers"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "parallel_workers", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.parallel_workers", + "docs_anchor": "minimizer-parallel-workers", + "docs_page": "minimizer", + "edi_name": "_minimizer.parallel_workers", + "edi_names": ["_minimizer.parallel_workers"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.parallel_workers", + "_easydiffraction_minimizer.parallel_workers" + ], + "unique_name": "minimizer.parallel_workers" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.population_size", + "cif_names": ["_easydiffraction_minimizer.population_size"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "population_size", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.population_size", + "docs_anchor": "minimizer-population-size", + "docs_page": "minimizer", + "edi_name": "_minimizer.population_size", + "edi_names": ["_minimizer.population_size"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.population_size", + "_easydiffraction_minimizer.population_size" + ], + "unique_name": "minimizer.population_size" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.proposal_moves", + "cif_names": ["_easydiffraction_minimizer.proposal_moves"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "StringDescriptor", + "descriptor_name": "proposal_moves", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.proposal_moves", + "docs_anchor": "minimizer-proposal-moves", + "docs_page": "minimizer", + "edi_name": "_minimizer.proposal_moves", + "edi_names": ["_minimizer.proposal_moves"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.proposal_moves", + "_easydiffraction_minimizer.proposal_moves" + ], + "unique_name": "minimizer.proposal_moves" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.random_seed", + "cif_names": ["_easydiffraction_minimizer.random_seed"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "random_seed", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.random_seed", + "docs_anchor": "minimizer-random-seed", + "docs_page": "minimizer", + "edi_name": "_minimizer.random_seed", + "edi_names": ["_minimizer.random_seed"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.random_seed", + "_easydiffraction_minimizer.random_seed" + ], + "unique_name": "minimizer.random_seed" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.sampling_steps", + "cif_names": ["_easydiffraction_minimizer.sampling_steps"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "sampling_steps", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.sampling_steps", + "docs_anchor": "minimizer-sampling-steps", + "docs_page": "minimizer", + "edi_name": "_minimizer.sampling_steps", + "edi_names": ["_minimizer.sampling_steps"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.sampling_steps", + "_easydiffraction_minimizer.sampling_steps" + ], + "unique_name": "minimizer.sampling_steps" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.thinning_interval", + "cif_names": ["_easydiffraction_minimizer.thinning_interval"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "thinning_interval", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.thinning_interval", + "docs_anchor": "minimizer-thinning-interval", + "docs_page": "minimizer", + "edi_name": "_minimizer.thinning_interval", + "edi_names": ["_minimizer.thinning_interval"], + "owner_class": "EmceeMinimizer", + "read_names": [ + "_minimizer.thinning_interval", + "_easydiffraction_minimizer.thinning_interval" + ], + "unique_name": "minimizer.thinning_interval" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.emcee", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.emcee.type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "EmceeMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.lmfit", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.lmfit.max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "LmfitMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.lmfit", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.lmfit.type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "LmfitMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.lmfit (least_squares)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.lmfit (least_squares).max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "LmfitLeastSquaresMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.lmfit (least_squares)", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.lmfit (least_squares).type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "LmfitLeastSquaresMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.max_iterations", + "cif_names": ["_easydiffraction_minimizer.max_iterations"], + "context": "factory.MinimizerCategoryFactory.lmfit (leastsq)", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "max_iterations", + "descriptor_path": "factory.MinimizerCategoryFactory.lmfit (leastsq).max_iterations", + "docs_anchor": "minimizer-max-iterations", + "docs_page": "minimizer", + "edi_name": "_minimizer.max_iterations", + "edi_names": ["_minimizer.max_iterations"], + "owner_class": "LmfitLeastsqMinimizer", + "read_names": [ + "_minimizer.max_iterations", + "_easydiffraction_minimizer.max_iterations" + ], + "unique_name": "minimizer.max_iterations" + }, + { + "category_code": "minimizer", + "category_entry_name": null, + "cif_name": "_easydiffraction_minimizer.type", + "cif_names": ["_easydiffraction_minimizer.type"], + "context": "factory.MinimizerCategoryFactory.lmfit (leastsq)", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.MinimizerCategoryFactory.lmfit (leastsq).type", + "docs_anchor": "minimizer-type", + "docs_page": "minimizer", + "edi_name": "_minimizer.type", + "edi_names": ["_minimizer.type"], + "owner_class": "LmfitLeastsqMinimizer", + "read_names": ["_minimizer.type", "_easydiffraction_minimizer.type"], + "unique_name": "minimizer.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_u", + "cif_names": ["_easydiffraction_peak.broad_gauss_u"], + "context": "factory.PeakFactory.cwl-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_u", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt.broad_gauss_u", + "docs_anchor": "peak-broad-gauss-u", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_u", + "edi_names": ["_peak.broad_gauss_u"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_u", + "_easydiffraction_peak.broad_gauss_u" + ], + "unique_name": "peak.broad_gauss_u" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_v", + "cif_names": ["_easydiffraction_peak.broad_gauss_v"], + "context": "factory.PeakFactory.cwl-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_v", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt.broad_gauss_v", + "docs_anchor": "peak-broad-gauss-v", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_v", + "edi_names": ["_peak.broad_gauss_v"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_v", + "_easydiffraction_peak.broad_gauss_v" + ], + "unique_name": "peak.broad_gauss_v" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_w", + "cif_names": ["_easydiffraction_peak.broad_gauss_w"], + "context": "factory.PeakFactory.cwl-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_w", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt.broad_gauss_w", + "docs_anchor": "peak-broad-gauss-w", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_w", + "edi_names": ["_peak.broad_gauss_w"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_w", + "_easydiffraction_peak.broad_gauss_w" + ], + "unique_name": "peak.broad_gauss_w" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_x", + "cif_names": ["_easydiffraction_peak.broad_lorentz_x"], + "context": "factory.PeakFactory.cwl-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_x", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt.broad_lorentz_x", + "docs_anchor": "peak-broad-lorentz-x", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_x", + "edi_names": ["_peak.broad_lorentz_x"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_lorentz_x", + "_easydiffraction_peak.broad_lorentz_x" + ], + "unique_name": "peak.broad_lorentz_x" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_y", + "cif_names": ["_easydiffraction_peak.broad_lorentz_y"], + "context": "factory.PeakFactory.cwl-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_y", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt.broad_lorentz_y", + "docs_anchor": "peak-broad-lorentz-y", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_y", + "edi_names": ["_peak.broad_lorentz_y"], + "owner_class": "CwlPseudoVoigt", + "read_names": [ + "_peak.broad_lorentz_y", + "_easydiffraction_peak.broad_lorentz_y" + ], + "unique_name": "peak.broad_lorentz_y" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.cwl-pseudo-voigt", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "CwlPseudoVoigt", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.asym_empir_1", + "cif_names": ["_easydiffraction_peak.asym_empir_1"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "asym_empir_1", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.asym_empir_1", + "docs_anchor": "peak-asym-empir-1", + "docs_page": "peak", + "edi_name": "_peak.asym_empir_1", + "edi_names": ["_peak.asym_empir_1"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.asym_empir_1", + "_easydiffraction_peak.asym_empir_1" + ], + "unique_name": "peak.asym_empir_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.asym_empir_2", + "cif_names": ["_easydiffraction_peak.asym_empir_2"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "asym_empir_2", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.asym_empir_2", + "docs_anchor": "peak-asym-empir-2", + "docs_page": "peak", + "edi_name": "_peak.asym_empir_2", + "edi_names": ["_peak.asym_empir_2"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.asym_empir_2", + "_easydiffraction_peak.asym_empir_2" + ], + "unique_name": "peak.asym_empir_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.asym_empir_3", + "cif_names": ["_easydiffraction_peak.asym_empir_3"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "asym_empir_3", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.asym_empir_3", + "docs_anchor": "peak-asym-empir-3", + "docs_page": "peak", + "edi_name": "_peak.asym_empir_3", + "edi_names": ["_peak.asym_empir_3"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.asym_empir_3", + "_easydiffraction_peak.asym_empir_3" + ], + "unique_name": "peak.asym_empir_3" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.asym_empir_4", + "cif_names": ["_easydiffraction_peak.asym_empir_4"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "asym_empir_4", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.asym_empir_4", + "docs_anchor": "peak-asym-empir-4", + "docs_page": "peak", + "edi_name": "_peak.asym_empir_4", + "edi_names": ["_peak.asym_empir_4"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.asym_empir_4", + "_easydiffraction_peak.asym_empir_4" + ], + "unique_name": "peak.asym_empir_4" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_u", + "cif_names": ["_easydiffraction_peak.broad_gauss_u"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_u", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.broad_gauss_u", + "docs_anchor": "peak-broad-gauss-u", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_u", + "edi_names": ["_peak.broad_gauss_u"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.broad_gauss_u", + "_easydiffraction_peak.broad_gauss_u" + ], + "unique_name": "peak.broad_gauss_u" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_v", + "cif_names": ["_easydiffraction_peak.broad_gauss_v"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_v", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.broad_gauss_v", + "docs_anchor": "peak-broad-gauss-v", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_v", + "edi_names": ["_peak.broad_gauss_v"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.broad_gauss_v", + "_easydiffraction_peak.broad_gauss_v" + ], + "unique_name": "peak.broad_gauss_v" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_w", + "cif_names": ["_easydiffraction_peak.broad_gauss_w"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_w", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.broad_gauss_w", + "docs_anchor": "peak-broad-gauss-w", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_w", + "edi_names": ["_peak.broad_gauss_w"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.broad_gauss_w", + "_easydiffraction_peak.broad_gauss_w" + ], + "unique_name": "peak.broad_gauss_w" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_x", + "cif_names": ["_easydiffraction_peak.broad_lorentz_x"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_x", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.broad_lorentz_x", + "docs_anchor": "peak-broad-lorentz-x", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_x", + "edi_names": ["_peak.broad_lorentz_x"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.broad_lorentz_x", + "_easydiffraction_peak.broad_lorentz_x" + ], + "unique_name": "peak.broad_lorentz_x" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_y", + "cif_names": ["_easydiffraction_peak.broad_lorentz_y"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_y", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.broad_lorentz_y", + "docs_anchor": "peak-broad-lorentz-y", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_y", + "edi_names": ["_peak.broad_lorentz_y"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": [ + "_peak.broad_lorentz_y", + "_easydiffraction_peak.broad_lorentz_y" + ], + "unique_name": "peak.broad_lorentz_y" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.cwl-pseudo-voigt-empirical-asymmetry.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "CwlPseudoVoigtEmpiricalAsymmetry", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.asym_fcj_1", + "cif_names": ["_easydiffraction_peak.asym_fcj_1"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "Parameter", + "descriptor_name": "asym_fcj_1", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.asym_fcj_1", + "docs_anchor": "peak-asym-fcj-1", + "docs_page": "peak", + "edi_name": "_peak.asym_fcj_1", + "edi_names": ["_peak.asym_fcj_1"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": ["_peak.asym_fcj_1", "_easydiffraction_peak.asym_fcj_1"], + "unique_name": "peak.asym_fcj_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.asym_fcj_2", + "cif_names": ["_easydiffraction_peak.asym_fcj_2"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "Parameter", + "descriptor_name": "asym_fcj_2", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.asym_fcj_2", + "docs_anchor": "peak-asym-fcj-2", + "docs_page": "peak", + "edi_name": "_peak.asym_fcj_2", + "edi_names": ["_peak.asym_fcj_2"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": ["_peak.asym_fcj_2", "_easydiffraction_peak.asym_fcj_2"], + "unique_name": "peak.asym_fcj_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_u", + "cif_names": ["_easydiffraction_peak.broad_gauss_u"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_u", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.broad_gauss_u", + "docs_anchor": "peak-broad-gauss-u", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_u", + "edi_names": ["_peak.broad_gauss_u"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": [ + "_peak.broad_gauss_u", + "_easydiffraction_peak.broad_gauss_u" + ], + "unique_name": "peak.broad_gauss_u" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_v", + "cif_names": ["_easydiffraction_peak.broad_gauss_v"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_v", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.broad_gauss_v", + "docs_anchor": "peak-broad-gauss-v", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_v", + "edi_names": ["_peak.broad_gauss_v"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": [ + "_peak.broad_gauss_v", + "_easydiffraction_peak.broad_gauss_v" + ], + "unique_name": "peak.broad_gauss_v" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_w", + "cif_names": ["_easydiffraction_peak.broad_gauss_w"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_w", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.broad_gauss_w", + "docs_anchor": "peak-broad-gauss-w", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_w", + "edi_names": ["_peak.broad_gauss_w"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": [ + "_peak.broad_gauss_w", + "_easydiffraction_peak.broad_gauss_w" + ], + "unique_name": "peak.broad_gauss_w" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_x", + "cif_names": ["_easydiffraction_peak.broad_lorentz_x"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_x", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.broad_lorentz_x", + "docs_anchor": "peak-broad-lorentz-x", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_x", + "edi_names": ["_peak.broad_lorentz_x"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": [ + "_peak.broad_lorentz_x", + "_easydiffraction_peak.broad_lorentz_x" + ], + "unique_name": "peak.broad_lorentz_x" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_y", + "cif_names": ["_easydiffraction_peak.broad_lorentz_y"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_y", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.broad_lorentz_y", + "docs_anchor": "peak-broad-lorentz-y", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_y", + "edi_names": ["_peak.broad_lorentz_y"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": [ + "_peak.broad_lorentz_y", + "_easydiffraction_peak.broad_lorentz_y" + ], + "unique_name": "peak.broad_lorentz_y" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.cwl-thompson-cox-hastings", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.cwl-thompson-cox-hastings.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "CwlThompsonCoxHastings", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_0", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_0"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_0", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.broad_gauss_sigma_0", + "docs_anchor": "peak-broad-gauss-sigma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_0", + "edi_names": ["_peak.broad_gauss_sigma_0"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.broad_gauss_sigma_0", + "_easydiffraction_peak.broad_gauss_sigma_0" + ], + "unique_name": "peak.broad_gauss_sigma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_1", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_1"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_1", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.broad_gauss_sigma_1", + "docs_anchor": "peak-broad-gauss-sigma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_1", + "edi_names": ["_peak.broad_gauss_sigma_1"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.broad_gauss_sigma_1", + "_easydiffraction_peak.broad_gauss_sigma_1" + ], + "unique_name": "peak.broad_gauss_sigma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_2", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_2"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_2", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.broad_gauss_sigma_2", + "docs_anchor": "peak-broad-gauss-sigma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_2", + "edi_names": ["_peak.broad_gauss_sigma_2"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.broad_gauss_sigma_2", + "_easydiffraction_peak.broad_gauss_sigma_2" + ], + "unique_name": "peak.broad_gauss_sigma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_0", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_0"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_0", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.broad_lorentz_gamma_0", + "docs_anchor": "peak-broad-lorentz-gamma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_0", + "edi_names": ["_peak.broad_lorentz_gamma_0"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.broad_lorentz_gamma_0", + "_easydiffraction_peak.broad_lorentz_gamma_0" + ], + "unique_name": "peak.broad_lorentz_gamma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_1", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_1"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_1", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.broad_lorentz_gamma_1", + "docs_anchor": "peak-broad-lorentz-gamma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_1", + "edi_names": ["_peak.broad_lorentz_gamma_1"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.broad_lorentz_gamma_1", + "_easydiffraction_peak.broad_lorentz_gamma_1" + ], + "unique_name": "peak.broad_lorentz_gamma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_2", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_2"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_2", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.broad_lorentz_gamma_2", + "docs_anchor": "peak-broad-lorentz-gamma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_2", + "edi_names": ["_peak.broad_lorentz_gamma_2"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.broad_lorentz_gamma_2", + "_easydiffraction_peak.broad_lorentz_gamma_2" + ], + "unique_name": "peak.broad_lorentz_gamma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_decay_beta_00", + "cif_names": ["_easydiffraction_peak.dexp_decay_beta_00"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_decay_beta_00", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_decay_beta_00", + "docs_anchor": "peak-dexp-decay-beta-00", + "docs_page": "peak", + "edi_name": "_peak.dexp_decay_beta_00", + "edi_names": ["_peak.dexp_decay_beta_00"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_decay_beta_00", + "_easydiffraction_peak.dexp_decay_beta_00" + ], + "unique_name": "peak.dexp_decay_beta_00" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_decay_beta_01", + "cif_names": ["_easydiffraction_peak.dexp_decay_beta_01"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_decay_beta_01", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_decay_beta_01", + "docs_anchor": "peak-dexp-decay-beta-01", + "docs_page": "peak", + "edi_name": "_peak.dexp_decay_beta_01", + "edi_names": ["_peak.dexp_decay_beta_01"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_decay_beta_01", + "_easydiffraction_peak.dexp_decay_beta_01" + ], + "unique_name": "peak.dexp_decay_beta_01" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_decay_beta_10", + "cif_names": ["_easydiffraction_peak.dexp_decay_beta_10"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_decay_beta_10", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_decay_beta_10", + "docs_anchor": "peak-dexp-decay-beta-10", + "docs_page": "peak", + "edi_name": "_peak.dexp_decay_beta_10", + "edi_names": ["_peak.dexp_decay_beta_10"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_decay_beta_10", + "_easydiffraction_peak.dexp_decay_beta_10" + ], + "unique_name": "peak.dexp_decay_beta_10" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_rise_alpha_1", + "cif_names": ["_easydiffraction_peak.dexp_rise_alpha_1"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_rise_alpha_1", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_rise_alpha_1", + "docs_anchor": "peak-dexp-rise-alpha-1", + "docs_page": "peak", + "edi_name": "_peak.dexp_rise_alpha_1", + "edi_names": ["_peak.dexp_rise_alpha_1"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_rise_alpha_1", + "_easydiffraction_peak.dexp_rise_alpha_1" + ], + "unique_name": "peak.dexp_rise_alpha_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_rise_alpha_2", + "cif_names": ["_easydiffraction_peak.dexp_rise_alpha_2"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_rise_alpha_2", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_rise_alpha_2", + "docs_anchor": "peak-dexp-rise-alpha-2", + "docs_page": "peak", + "edi_name": "_peak.dexp_rise_alpha_2", + "edi_names": ["_peak.dexp_rise_alpha_2"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_rise_alpha_2", + "_easydiffraction_peak.dexp_rise_alpha_2" + ], + "unique_name": "peak.dexp_rise_alpha_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_switch_r_01", + "cif_names": ["_easydiffraction_peak.dexp_switch_r_01"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_switch_r_01", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_switch_r_01", + "docs_anchor": "peak-dexp-switch-r-01", + "docs_page": "peak", + "edi_name": "_peak.dexp_switch_r_01", + "edi_names": ["_peak.dexp_switch_r_01"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_switch_r_01", + "_easydiffraction_peak.dexp_switch_r_01" + ], + "unique_name": "peak.dexp_switch_r_01" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_switch_r_02", + "cif_names": ["_easydiffraction_peak.dexp_switch_r_02"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_switch_r_02", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_switch_r_02", + "docs_anchor": "peak-dexp-switch-r-02", + "docs_page": "peak", + "edi_name": "_peak.dexp_switch_r_02", + "edi_names": ["_peak.dexp_switch_r_02"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_switch_r_02", + "_easydiffraction_peak.dexp_switch_r_02" + ], + "unique_name": "peak.dexp_switch_r_02" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.dexp_switch_r_03", + "cif_names": ["_easydiffraction_peak.dexp_switch_r_03"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "dexp_switch_r_03", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.dexp_switch_r_03", + "docs_anchor": "peak-dexp-switch-r-03", + "docs_page": "peak", + "edi_name": "_peak.dexp_switch_r_03", + "edi_names": ["_peak.dexp_switch_r_03"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": [ + "_peak.dexp_switch_r_03", + "_easydiffraction_peak.dexp_switch_r_03" + ], + "unique_name": "peak.dexp_switch_r_03" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.tof-double-jorgensen-von-dreele", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.tof-double-jorgensen-von-dreele.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TofDoubleJorgensenVonDreele", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_0", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_0"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_0", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.broad_gauss_sigma_0", + "docs_anchor": "peak-broad-gauss-sigma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_0", + "edi_names": ["_peak.broad_gauss_sigma_0"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.broad_gauss_sigma_0", + "_easydiffraction_peak.broad_gauss_sigma_0" + ], + "unique_name": "peak.broad_gauss_sigma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_1", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_1"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_1", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.broad_gauss_sigma_1", + "docs_anchor": "peak-broad-gauss-sigma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_1", + "edi_names": ["_peak.broad_gauss_sigma_1"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.broad_gauss_sigma_1", + "_easydiffraction_peak.broad_gauss_sigma_1" + ], + "unique_name": "peak.broad_gauss_sigma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_2", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_2"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_2", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.broad_gauss_sigma_2", + "docs_anchor": "peak-broad-gauss-sigma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_2", + "edi_names": ["_peak.broad_gauss_sigma_2"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.broad_gauss_sigma_2", + "_easydiffraction_peak.broad_gauss_sigma_2" + ], + "unique_name": "peak.broad_gauss_sigma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.decay_beta_0", + "cif_names": ["_easydiffraction_peak.decay_beta_0"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "Parameter", + "descriptor_name": "decay_beta_0", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.decay_beta_0", + "docs_anchor": "peak-decay-beta-0", + "docs_page": "peak", + "edi_name": "_peak.decay_beta_0", + "edi_names": ["_peak.decay_beta_0"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.decay_beta_0", + "_easydiffraction_peak.decay_beta_0" + ], + "unique_name": "peak.decay_beta_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.decay_beta_1", + "cif_names": ["_easydiffraction_peak.decay_beta_1"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "Parameter", + "descriptor_name": "decay_beta_1", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.decay_beta_1", + "docs_anchor": "peak-decay-beta-1", + "docs_page": "peak", + "edi_name": "_peak.decay_beta_1", + "edi_names": ["_peak.decay_beta_1"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.decay_beta_1", + "_easydiffraction_peak.decay_beta_1" + ], + "unique_name": "peak.decay_beta_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.rise_alpha_0", + "cif_names": ["_easydiffraction_peak.rise_alpha_0"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "Parameter", + "descriptor_name": "rise_alpha_0", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.rise_alpha_0", + "docs_anchor": "peak-rise-alpha-0", + "docs_page": "peak", + "edi_name": "_peak.rise_alpha_0", + "edi_names": ["_peak.rise_alpha_0"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.rise_alpha_0", + "_easydiffraction_peak.rise_alpha_0" + ], + "unique_name": "peak.rise_alpha_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.rise_alpha_1", + "cif_names": ["_easydiffraction_peak.rise_alpha_1"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "Parameter", + "descriptor_name": "rise_alpha_1", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.rise_alpha_1", + "docs_anchor": "peak-rise-alpha-1", + "docs_page": "peak", + "edi_name": "_peak.rise_alpha_1", + "edi_names": ["_peak.rise_alpha_1"], + "owner_class": "TofJorgensen", + "read_names": [ + "_peak.rise_alpha_1", + "_easydiffraction_peak.rise_alpha_1" + ], + "unique_name": "peak.rise_alpha_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.tof-jorgensen", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.tof-jorgensen.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TofJorgensen", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_0", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_0"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_0", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.broad_gauss_sigma_0", + "docs_anchor": "peak-broad-gauss-sigma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_0", + "edi_names": ["_peak.broad_gauss_sigma_0"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.broad_gauss_sigma_0", + "_easydiffraction_peak.broad_gauss_sigma_0" + ], + "unique_name": "peak.broad_gauss_sigma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_1", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_1"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_1", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.broad_gauss_sigma_1", + "docs_anchor": "peak-broad-gauss-sigma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_1", + "edi_names": ["_peak.broad_gauss_sigma_1"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.broad_gauss_sigma_1", + "_easydiffraction_peak.broad_gauss_sigma_1" + ], + "unique_name": "peak.broad_gauss_sigma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_2", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_2"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_2", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.broad_gauss_sigma_2", + "docs_anchor": "peak-broad-gauss-sigma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_2", + "edi_names": ["_peak.broad_gauss_sigma_2"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.broad_gauss_sigma_2", + "_easydiffraction_peak.broad_gauss_sigma_2" + ], + "unique_name": "peak.broad_gauss_sigma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_0", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_0"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_0", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.broad_lorentz_gamma_0", + "docs_anchor": "peak-broad-lorentz-gamma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_0", + "edi_names": ["_peak.broad_lorentz_gamma_0"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.broad_lorentz_gamma_0", + "_easydiffraction_peak.broad_lorentz_gamma_0" + ], + "unique_name": "peak.broad_lorentz_gamma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_1", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_1"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_1", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.broad_lorentz_gamma_1", + "docs_anchor": "peak-broad-lorentz-gamma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_1", + "edi_names": ["_peak.broad_lorentz_gamma_1"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.broad_lorentz_gamma_1", + "_easydiffraction_peak.broad_lorentz_gamma_1" + ], + "unique_name": "peak.broad_lorentz_gamma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_2", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_2"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_2", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.broad_lorentz_gamma_2", + "docs_anchor": "peak-broad-lorentz-gamma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_2", + "edi_names": ["_peak.broad_lorentz_gamma_2"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.broad_lorentz_gamma_2", + "_easydiffraction_peak.broad_lorentz_gamma_2" + ], + "unique_name": "peak.broad_lorentz_gamma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.decay_beta_0", + "cif_names": ["_easydiffraction_peak.decay_beta_0"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "decay_beta_0", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.decay_beta_0", + "docs_anchor": "peak-decay-beta-0", + "docs_page": "peak", + "edi_name": "_peak.decay_beta_0", + "edi_names": ["_peak.decay_beta_0"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.decay_beta_0", + "_easydiffraction_peak.decay_beta_0" + ], + "unique_name": "peak.decay_beta_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.decay_beta_1", + "cif_names": ["_easydiffraction_peak.decay_beta_1"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "decay_beta_1", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.decay_beta_1", + "docs_anchor": "peak-decay-beta-1", + "docs_page": "peak", + "edi_name": "_peak.decay_beta_1", + "edi_names": ["_peak.decay_beta_1"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.decay_beta_1", + "_easydiffraction_peak.decay_beta_1" + ], + "unique_name": "peak.decay_beta_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.rise_alpha_0", + "cif_names": ["_easydiffraction_peak.rise_alpha_0"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "rise_alpha_0", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.rise_alpha_0", + "docs_anchor": "peak-rise-alpha-0", + "docs_page": "peak", + "edi_name": "_peak.rise_alpha_0", + "edi_names": ["_peak.rise_alpha_0"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.rise_alpha_0", + "_easydiffraction_peak.rise_alpha_0" + ], + "unique_name": "peak.rise_alpha_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.rise_alpha_1", + "cif_names": ["_easydiffraction_peak.rise_alpha_1"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "Parameter", + "descriptor_name": "rise_alpha_1", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.rise_alpha_1", + "docs_anchor": "peak-rise-alpha-1", + "docs_page": "peak", + "edi_name": "_peak.rise_alpha_1", + "edi_names": ["_peak.rise_alpha_1"], + "owner_class": "TofJorgensenVonDreele", + "read_names": [ + "_peak.rise_alpha_1", + "_easydiffraction_peak.rise_alpha_1" + ], + "unique_name": "peak.rise_alpha_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.tof-jorgensen-von-dreele", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.tof-jorgensen-von-dreele.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TofJorgensenVonDreele", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_0", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_0"], + "context": "factory.PeakFactory.tof-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_0", + "descriptor_path": "factory.PeakFactory.tof-pseudo-voigt.broad_gauss_sigma_0", + "docs_anchor": "peak-broad-gauss-sigma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_0", + "edi_names": ["_peak.broad_gauss_sigma_0"], + "owner_class": "TofPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_sigma_0", + "_easydiffraction_peak.broad_gauss_sigma_0" + ], + "unique_name": "peak.broad_gauss_sigma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_1", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_1"], + "context": "factory.PeakFactory.tof-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_1", + "descriptor_path": "factory.PeakFactory.tof-pseudo-voigt.broad_gauss_sigma_1", + "docs_anchor": "peak-broad-gauss-sigma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_1", + "edi_names": ["_peak.broad_gauss_sigma_1"], + "owner_class": "TofPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_sigma_1", + "_easydiffraction_peak.broad_gauss_sigma_1" + ], + "unique_name": "peak.broad_gauss_sigma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_gauss_sigma_2", + "cif_names": ["_easydiffraction_peak.broad_gauss_sigma_2"], + "context": "factory.PeakFactory.tof-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_gauss_sigma_2", + "descriptor_path": "factory.PeakFactory.tof-pseudo-voigt.broad_gauss_sigma_2", + "docs_anchor": "peak-broad-gauss-sigma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_gauss_sigma_2", + "edi_names": ["_peak.broad_gauss_sigma_2"], + "owner_class": "TofPseudoVoigt", + "read_names": [ + "_peak.broad_gauss_sigma_2", + "_easydiffraction_peak.broad_gauss_sigma_2" + ], + "unique_name": "peak.broad_gauss_sigma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_0", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_0"], + "context": "factory.PeakFactory.tof-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_0", + "descriptor_path": "factory.PeakFactory.tof-pseudo-voigt.broad_lorentz_gamma_0", + "docs_anchor": "peak-broad-lorentz-gamma-0", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_0", + "edi_names": ["_peak.broad_lorentz_gamma_0"], + "owner_class": "TofPseudoVoigt", + "read_names": [ + "_peak.broad_lorentz_gamma_0", + "_easydiffraction_peak.broad_lorentz_gamma_0" + ], + "unique_name": "peak.broad_lorentz_gamma_0" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_1", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_1"], + "context": "factory.PeakFactory.tof-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_1", + "descriptor_path": "factory.PeakFactory.tof-pseudo-voigt.broad_lorentz_gamma_1", + "docs_anchor": "peak-broad-lorentz-gamma-1", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_1", + "edi_names": ["_peak.broad_lorentz_gamma_1"], + "owner_class": "TofPseudoVoigt", + "read_names": [ + "_peak.broad_lorentz_gamma_1", + "_easydiffraction_peak.broad_lorentz_gamma_1" + ], + "unique_name": "peak.broad_lorentz_gamma_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_lorentz_gamma_2", + "cif_names": ["_easydiffraction_peak.broad_lorentz_gamma_2"], + "context": "factory.PeakFactory.tof-pseudo-voigt", + "descriptor_class": "Parameter", + "descriptor_name": "broad_lorentz_gamma_2", + "descriptor_path": "factory.PeakFactory.tof-pseudo-voigt.broad_lorentz_gamma_2", + "docs_anchor": "peak-broad-lorentz-gamma-2", + "docs_page": "peak", + "edi_name": "_peak.broad_lorentz_gamma_2", + "edi_names": ["_peak.broad_lorentz_gamma_2"], + "owner_class": "TofPseudoVoigt", + "read_names": [ + "_peak.broad_lorentz_gamma_2", + "_easydiffraction_peak.broad_lorentz_gamma_2" + ], + "unique_name": "peak.broad_lorentz_gamma_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.tof-pseudo-voigt", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.tof-pseudo-voigt.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TofPseudoVoigt", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.broad_q", + "cif_names": ["_easydiffraction_peak.broad_q"], + "context": "factory.PeakFactory.total-gaussian-damped-sinc", + "descriptor_class": "Parameter", + "descriptor_name": "broad_q", + "descriptor_path": "factory.PeakFactory.total-gaussian-damped-sinc.broad_q", + "docs_anchor": "peak-broad-q", + "docs_page": "peak", + "edi_name": "_peak.broad_q", + "edi_names": ["_peak.broad_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.broad_q", "_easydiffraction_peak.broad_q"], + "unique_name": "peak.broad_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.cutoff_q", + "cif_names": ["_easydiffraction_peak.cutoff_q"], + "context": "factory.PeakFactory.total-gaussian-damped-sinc", + "descriptor_class": "Parameter", + "descriptor_name": "cutoff_q", + "descriptor_path": "factory.PeakFactory.total-gaussian-damped-sinc.cutoff_q", + "docs_anchor": "peak-cutoff-q", + "docs_page": "peak", + "edi_name": "_peak.cutoff_q", + "edi_names": ["_peak.cutoff_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.cutoff_q", "_easydiffraction_peak.cutoff_q"], + "unique_name": "peak.cutoff_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.damp_particle_diameter", + "cif_names": ["_easydiffraction_peak.damp_particle_diameter"], + "context": "factory.PeakFactory.total-gaussian-damped-sinc", + "descriptor_class": "Parameter", + "descriptor_name": "damp_particle_diameter", + "descriptor_path": "factory.PeakFactory.total-gaussian-damped-sinc.damp_particle_diameter", + "docs_anchor": "peak-damp-particle-diameter", + "docs_page": "peak", + "edi_name": "_peak.damp_particle_diameter", + "edi_names": ["_peak.damp_particle_diameter"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.damp_particle_diameter", + "_easydiffraction_peak.damp_particle_diameter" + ], + "unique_name": "peak.damp_particle_diameter" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.damp_q", + "cif_names": ["_easydiffraction_peak.damp_q"], + "context": "factory.PeakFactory.total-gaussian-damped-sinc", + "descriptor_class": "Parameter", + "descriptor_name": "damp_q", + "descriptor_path": "factory.PeakFactory.total-gaussian-damped-sinc.damp_q", + "docs_anchor": "peak-damp-q", + "docs_page": "peak", + "edi_name": "_peak.damp_q", + "edi_names": ["_peak.damp_q"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.damp_q", "_easydiffraction_peak.damp_q"], + "unique_name": "peak.damp_q" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.sharp_delta_1", + "cif_names": ["_easydiffraction_peak.sharp_delta_1"], + "context": "factory.PeakFactory.total-gaussian-damped-sinc", + "descriptor_class": "Parameter", + "descriptor_name": "sharp_delta_1", + "descriptor_path": "factory.PeakFactory.total-gaussian-damped-sinc.sharp_delta_1", + "docs_anchor": "peak-sharp-delta-1", + "docs_page": "peak", + "edi_name": "_peak.sharp_delta_1", + "edi_names": ["_peak.sharp_delta_1"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.sharp_delta_1", + "_easydiffraction_peak.sharp_delta_1" + ], + "unique_name": "peak.sharp_delta_1" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.sharp_delta_2", + "cif_names": ["_easydiffraction_peak.sharp_delta_2"], + "context": "factory.PeakFactory.total-gaussian-damped-sinc", + "descriptor_class": "Parameter", + "descriptor_name": "sharp_delta_2", + "descriptor_path": "factory.PeakFactory.total-gaussian-damped-sinc.sharp_delta_2", + "docs_anchor": "peak-sharp-delta-2", + "docs_page": "peak", + "edi_name": "_peak.sharp_delta_2", + "edi_names": ["_peak.sharp_delta_2"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": [ + "_peak.sharp_delta_2", + "_easydiffraction_peak.sharp_delta_2" + ], + "unique_name": "peak.sharp_delta_2" + }, + { + "category_code": "peak", + "category_entry_name": null, + "cif_name": "_easydiffraction_peak.type", + "cif_names": ["_easydiffraction_peak.type"], + "context": "factory.PeakFactory.total-gaussian-damped-sinc", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.PeakFactory.total-gaussian-damped-sinc.type", + "docs_anchor": "peak-type", + "docs_page": "peak", + "edi_name": "_peak.type", + "edi_names": ["_peak.type"], + "owner_class": "TotalGaussianDampedSinc", + "read_names": ["_peak.type", "_easydiffraction_peak.type"], + "unique_name": "peak.type" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_h", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_h", + "_pref_orient.index_h" + ], + "context": "factory.PrefOrientFactory.default", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "factory.PrefOrientFactory.default[].index_h", + "docs_anchor": "preferred-orientation-index-h", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_h", + "edi_names": ["_preferred_orientation.index_h"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_h", + "_pd_pref_orient_March_Dollase.index_h", + "_pref_orient.index_h" + ], + "unique_name": "preferred_orientation.Si.index_h" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_k", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_k", + "_pref_orient.index_k" + ], + "context": "factory.PrefOrientFactory.default", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "factory.PrefOrientFactory.default[].index_k", + "docs_anchor": "preferred-orientation-index-k", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_k", + "edi_names": ["_preferred_orientation.index_k"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_k", + "_pd_pref_orient_March_Dollase.index_k", + "_pref_orient.index_k" + ], + "unique_name": "preferred_orientation.Si.index_k" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.index_l", + "cif_names": [ + "_pd_pref_orient_March_Dollase.index_l", + "_pref_orient.index_l" + ], + "context": "factory.PrefOrientFactory.default", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "factory.PrefOrientFactory.default[].index_l", + "docs_anchor": "preferred-orientation-index-l", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.index_l", + "edi_names": ["_preferred_orientation.index_l"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.index_l", + "_pd_pref_orient_March_Dollase.index_l", + "_pref_orient.index_l" + ], + "unique_name": "preferred_orientation.Si.index_l" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.r", + "cif_names": ["_pd_pref_orient_March_Dollase.r", "_pref_orient.march_r"], + "context": "factory.PrefOrientFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "march_r", + "descriptor_path": "factory.PrefOrientFactory.default[].march_r", + "docs_anchor": "preferred-orientation-march-r", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.march_r", + "edi_names": ["_preferred_orientation.march_r"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.march_r", + "_pd_pref_orient_March_Dollase.r", + "_pref_orient.march_r" + ], + "unique_name": "preferred_orientation.Si.march_r" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_easydiffraction_pref_orient.march_random_fract", + "cif_names": [ + "_easydiffraction_pref_orient.march_random_fract", + "_pref_orient.march_random_fract" + ], + "context": "factory.PrefOrientFactory.default", + "descriptor_class": "Parameter", + "descriptor_name": "march_random_fract", + "descriptor_path": "factory.PrefOrientFactory.default[].march_random_fract", + "docs_anchor": "preferred-orientation-march-random-fract", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.march_random_fract", + "edi_names": ["_preferred_orientation.march_random_fract"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.march_random_fract", + "_easydiffraction_pref_orient.march_random_fract", + "_pref_orient.march_random_fract" + ], + "unique_name": "preferred_orientation.Si.march_random_fract" + }, + { + "category_code": "preferred_orientation", + "category_entry_name": "Si", + "cif_name": "_pd_pref_orient_March_Dollase.phase_id", + "cif_names": [ + "_pd_pref_orient_March_Dollase.phase_id", + "_pref_orient.phase_id" + ], + "context": "factory.PrefOrientFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "factory.PrefOrientFactory.default[].structure_id", + "docs_anchor": "preferred-orientation-structure-id", + "docs_page": "preferred_orientation", + "edi_name": "_preferred_orientation.structure_id", + "edi_names": ["_preferred_orientation.structure_id"], + "owner_class": "PrefOrient", + "read_names": [ + "_preferred_orientation.structure_id", + "_pd_pref_orient_March_Dollase.phase_id", + "_pref_orient.phase_id" + ], + "unique_name": "preferred_orientation.Si.structure_id" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.created", + "cif_names": ["_project.created"], + "context": "factory.ProjectMetadataFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "created", + "descriptor_path": "factory.ProjectMetadataFactory.default.created_descriptor", + "docs_anchor": "metadata-created", + "docs_page": "metadata", + "edi_name": "_metadata.created", + "edi_names": ["_metadata.created"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.created", "_project.created"], + "unique_name": "metadata.created" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.description", + "cif_names": ["_project.description"], + "context": "factory.ProjectMetadataFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "description", + "descriptor_path": "factory.ProjectMetadataFactory.default.description_descriptor", + "docs_anchor": "metadata-description", + "docs_page": "metadata", + "edi_name": "_metadata.description", + "edi_names": ["_metadata.description"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.description", "_project.description"], + "unique_name": "metadata.description" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.last_modified", + "cif_names": ["_project.last_modified"], + "context": "factory.ProjectMetadataFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "last_modified", + "descriptor_path": "factory.ProjectMetadataFactory.default.last_modified_descriptor", + "docs_anchor": "metadata-last-modified", + "docs_page": "metadata", + "edi_name": "_metadata.last_modified", + "edi_names": ["_metadata.last_modified"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.last_modified", "_project.last_modified"], + "unique_name": "metadata.last_modified" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.id", + "cif_names": ["_project.id"], + "context": "factory.ProjectMetadataFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "name", + "descriptor_path": "factory.ProjectMetadataFactory.default.project_id", + "docs_anchor": "metadata-name", + "docs_page": "metadata", + "edi_name": "_metadata.name", + "edi_names": ["_metadata.name"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.name", "_project.id"], + "unique_name": "metadata.name" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_easydiffraction_project.timestamp", + "cif_names": [ + "_easydiffraction_project.timestamp", + "_software.timestamp" + ], + "context": "factory.ProjectMetadataFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "timestamp", + "descriptor_path": "factory.ProjectMetadataFactory.default.timestamp_descriptor", + "docs_anchor": "metadata-timestamp", + "docs_page": "metadata", + "edi_name": "_metadata.timestamp", + "edi_names": ["_metadata.timestamp"], + "owner_class": "ProjectMetadata", + "read_names": [ + "_metadata.timestamp", + "_easydiffraction_project.timestamp", + "_software.timestamp" + ], + "unique_name": "metadata.timestamp" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.title", + "cif_names": ["_project.title"], + "context": "factory.ProjectMetadataFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "title", + "descriptor_path": "factory.ProjectMetadataFactory.default.title_descriptor", + "docs_anchor": "metadata-title", + "docs_page": "metadata", + "edi_name": "_metadata.title", + "edi_names": ["_metadata.title"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.title", "_project.title"], + "unique_name": "metadata.title" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_calc", + "cif_names": ["_refln.f_calc"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_calc", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].f_calc", + "docs_anchor": "refln-f-calc", + "docs_page": "refln", + "edi_name": "_refln.f_calc", + "edi_names": ["_refln.f_calc"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.f_calc"], + "unique_name": "refln.0.f_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_squared_calc", + "cif_names": ["_refln.f_squared_calc"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_squared_calc", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].f_squared_calc", + "docs_anchor": "refln-f-squared-calc", + "docs_page": "refln", + "edi_name": "_refln.f_squared_calc", + "edi_names": ["_refln.f_squared_calc"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.f_squared_calc"], + "unique_name": "refln.0.f_squared_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_pd_refln.phase_id", + "cif_names": ["_pd_refln.phase_id", "_refln.phase_id"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].structure_id", + "docs_anchor": "refln-structure-id", + "docs_page": "refln", + "edi_name": "_refln.structure_id", + "edi_names": ["_refln.structure_id"], + "owner_class": "PowderCwlRefln", + "read_names": [ + "_refln.structure_id", + "_pd_refln.phase_id", + "_refln.phase_id" + ], + "unique_name": "refln.0.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.two_theta", + "cif_names": ["_refln.two_theta"], + "context": "factory.ReflnFactory.bragg-pd-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "two_theta", + "descriptor_path": "factory.ReflnFactory.bragg-pd-refln[].two_theta", + "docs_anchor": "refln-two-theta", + "docs_page": "refln", + "edi_name": "_refln.two_theta", + "edi_names": ["_refln.two_theta"], + "owner_class": "PowderCwlRefln", + "read_names": ["_refln.two_theta"], + "unique_name": "refln.0.two_theta" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_calc", + "cif_names": ["_refln.f_calc"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_calc", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].f_calc", + "docs_anchor": "refln-f-calc", + "docs_page": "refln", + "edi_name": "_refln.f_calc", + "edi_names": ["_refln.f_calc"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.f_calc"], + "unique_name": "refln.0.f_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.f_squared_calc", + "cif_names": ["_refln.f_squared_calc"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "f_squared_calc", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].f_squared_calc", + "docs_anchor": "refln-f-squared-calc", + "docs_page": "refln", + "edi_name": "_refln.f_squared_calc", + "edi_names": ["_refln.f_squared_calc"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.f_squared_calc"], + "unique_name": "refln.0.f_squared_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_pd_refln.phase_id", + "cif_names": ["_pd_refln.phase_id", "_refln.phase_id"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "StringDescriptor", + "descriptor_name": "structure_id", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].structure_id", + "docs_anchor": "refln-structure-id", + "docs_page": "refln", + "edi_name": "_refln.structure_id", + "edi_names": ["_refln.structure_id"], + "owner_class": "PowderTofRefln", + "read_names": [ + "_refln.structure_id", + "_pd_refln.phase_id", + "_refln.phase_id" + ], + "unique_name": "refln.0.structure_id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.time_of_flight", + "cif_names": ["_refln.time_of_flight"], + "context": "factory.ReflnFactory.bragg-pd-tof-refln", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "time_of_flight", + "descriptor_path": "factory.ReflnFactory.bragg-pd-tof-refln[].time_of_flight", + "docs_anchor": "refln-time-of-flight", + "docs_page": "refln", + "edi_name": "_refln.time_of_flight", + "edi_names": ["_refln.time_of_flight"], + "owner_class": "PowderTofRefln", + "read_names": ["_refln.time_of_flight"], + "unique_name": "refln.0.time_of_flight" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "Refln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "Refln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "Refln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "Refln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "Refln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "Refln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "Refln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "Refln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "factory.ReflnFactory.bragg-sc-cwl", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "factory.ReflnFactory.bragg-sc-cwl[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "Refln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.d_spacing", + "cif_names": ["_refln.d_spacing"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "d_spacing", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].d_spacing", + "docs_anchor": "refln-d-spacing", + "docs_page": "refln", + "edi_name": "_refln.d_spacing", + "edi_names": ["_refln.d_spacing"], + "owner_class": "TofRefln", + "read_names": ["_refln.d_spacing"], + "unique_name": "refln.0.d_spacing" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.id", + "cif_names": ["_refln.id"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].id", + "docs_anchor": "refln-id", + "docs_page": "refln", + "edi_name": "_refln.id", + "edi_names": ["_refln.id"], + "owner_class": "TofRefln", + "read_names": ["_refln.id"], + "unique_name": "refln.0.id" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_h", + "cif_names": ["_refln.index_h"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_h", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].index_h", + "docs_anchor": "refln-index-h", + "docs_page": "refln", + "edi_name": "_refln.index_h", + "edi_names": ["_refln.index_h"], + "owner_class": "TofRefln", + "read_names": ["_refln.index_h"], + "unique_name": "refln.0.index_h" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_k", + "cif_names": ["_refln.index_k"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_k", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].index_k", + "docs_anchor": "refln-index-k", + "docs_page": "refln", + "edi_name": "_refln.index_k", + "edi_names": ["_refln.index_k"], + "owner_class": "TofRefln", + "read_names": ["_refln.index_k"], + "unique_name": "refln.0.index_k" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.index_l", + "cif_names": ["_refln.index_l"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "index_l", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].index_l", + "docs_anchor": "refln-index-l", + "docs_page": "refln", + "edi_name": "_refln.index_l", + "edi_names": ["_refln.index_l"], + "owner_class": "TofRefln", + "read_names": ["_refln.index_l"], + "unique_name": "refln.0.index_l" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_calc", + "cif_names": ["_refln.intensity_calc"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_calc", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].intensity_calc", + "docs_anchor": "refln-intensity-calc", + "docs_page": "refln", + "edi_name": "_refln.intensity_calc", + "edi_names": ["_refln.intensity_calc"], + "owner_class": "TofRefln", + "read_names": ["_refln.intensity_calc"], + "unique_name": "refln.0.intensity_calc" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas", + "cif_names": ["_refln.intensity_meas"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].intensity_meas", + "docs_anchor": "refln-intensity-meas", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas", + "edi_names": ["_refln.intensity_meas"], + "owner_class": "TofRefln", + "read_names": ["_refln.intensity_meas"], + "unique_name": "refln.0.intensity_meas" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.intensity_meas_su", + "cif_names": ["_refln.intensity_meas_su"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "intensity_meas_su", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].intensity_meas_su", + "docs_anchor": "refln-intensity-meas-su", + "docs_page": "refln", + "edi_name": "_refln.intensity_meas_su", + "edi_names": ["_refln.intensity_meas_su"], + "owner_class": "TofRefln", + "read_names": ["_refln.intensity_meas_su"], + "unique_name": "refln.0.intensity_meas_su" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.sin_theta_over_lambda", + "cif_names": ["_refln.sin_theta_over_lambda"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "sin_theta_over_lambda", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].sin_theta_over_lambda", + "docs_anchor": "refln-sin-theta-over-lambda", + "docs_page": "refln", + "edi_name": "_refln.sin_theta_over_lambda", + "edi_names": ["_refln.sin_theta_over_lambda"], + "owner_class": "TofRefln", + "read_names": ["_refln.sin_theta_over_lambda"], + "unique_name": "refln.0.sin_theta_over_lambda" + }, + { + "category_code": "refln", + "category_entry_name": "0", + "cif_name": "_refln.wavelength", + "cif_names": ["_refln.wavelength"], + "context": "factory.ReflnFactory.bragg-sc-tof", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "wavelength", + "descriptor_path": "factory.ReflnFactory.bragg-sc-tof[].wavelength", + "docs_anchor": "refln-wavelength", + "docs_page": "refln", + "edi_name": "_refln.wavelength", + "edi_names": ["_refln.wavelength"], + "owner_class": "TofRefln", + "read_names": ["_refln.wavelength"], + "unique_name": "refln.0.wavelength" + }, + { + "category_code": "rendering_plot", + "category_entry_name": null, + "cif_name": "_rendering_plot.type", + "cif_names": ["_rendering_plot.type"], + "context": "factory.RenderingPlotFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.RenderingPlotFactory.default.type", + "docs_anchor": "rendering-plot-type", + "docs_page": "rendering_plot", + "edi_name": "_rendering_plot.type", + "edi_names": ["_rendering_plot.type"], + "owner_class": "RenderingPlot", + "read_names": ["_rendering_plot.type"], + "unique_name": "rendering_plot.type" + }, + { + "category_code": "rendering_structure", + "category_entry_name": null, + "cif_name": "_rendering_structure.type", + "cif_names": ["_rendering_structure.type"], + "context": "factory.RenderingStructureFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.RenderingStructureFactory.default.type", + "docs_anchor": "rendering-structure-type", + "docs_page": "rendering_structure", + "edi_name": "_rendering_structure.type", + "edi_names": ["_rendering_structure.type"], + "owner_class": "RenderingStructure", + "read_names": ["_rendering_structure.type"], + "unique_name": "rendering_structure.type" + }, + { + "category_code": "rendering_table", + "category_entry_name": null, + "cif_name": "_rendering_table.type", + "cif_names": ["_rendering_table.type"], + "context": "factory.RenderingTableFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "factory.RenderingTableFactory.default.type", + "docs_anchor": "rendering-table-type", + "docs_page": "rendering_table", + "edi_name": "_rendering_table.type", + "edi_names": ["_rendering_table.type"], + "owner_class": "RenderingTable", + "read_names": ["_rendering_table.type"], + "unique_name": "rendering_table.type" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.cif", + "cif_names": ["_report.cif"], + "context": "factory.ReportFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "cif", + "descriptor_path": "factory.ReportFactory.default.cif", + "docs_anchor": "report-cif", + "docs_page": "report", + "edi_name": "_report.cif", + "edi_names": ["_report.cif"], + "owner_class": "Report", + "read_names": ["_report.cif"], + "unique_name": "report.cif" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.html", + "cif_names": ["_report.html"], + "context": "factory.ReportFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "html", + "descriptor_path": "factory.ReportFactory.default.html", + "docs_anchor": "report-html", + "docs_page": "report", + "edi_name": "_report.html", + "edi_names": ["_report.html"], + "owner_class": "Report", + "read_names": ["_report.html"], + "unique_name": "report.html" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.html_offline", + "cif_names": ["_report.html_offline"], + "context": "factory.ReportFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "html_offline", + "descriptor_path": "factory.ReportFactory.default.html_offline", + "docs_anchor": "report-html-offline", + "docs_page": "report", + "edi_name": "_report.html_offline", + "edi_names": ["_report.html_offline"], + "owner_class": "Report", + "read_names": ["_report.html_offline"], + "unique_name": "report.html_offline" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.pdf", + "cif_names": ["_report.pdf"], + "context": "factory.ReportFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "pdf", + "descriptor_path": "factory.ReportFactory.default.pdf", + "docs_anchor": "report-pdf", + "docs_page": "report", + "edi_name": "_report.pdf", + "edi_names": ["_report.pdf"], + "owner_class": "Report", + "read_names": ["_report.pdf"], + "unique_name": "report.pdf" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.tex", + "cif_names": ["_report.tex"], + "context": "factory.ReportFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "tex", + "descriptor_path": "factory.ReportFactory.default.tex", + "docs_anchor": "report-tex", + "docs_page": "report", + "edi_name": "_report.tex", + "edi_names": ["_report.tex"], + "owner_class": "Report", + "read_names": ["_report.tex"], + "unique_name": "report.tex" + }, + { + "category_code": "sequential_fit_extract", + "category_entry_name": "_", + "cif_name": "_easydiffraction_sequential_fit_extract.id", + "cif_names": ["_easydiffraction_sequential_fit_extract.id"], + "context": "factory.SequentialFitExtractFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.SequentialFitExtractFactory.default[].id", + "docs_anchor": "sequential-fit-extract-id", + "docs_page": "sequential_fit_extract", + "edi_name": "_sequential_fit_extract.id", + "edi_names": ["_sequential_fit_extract.id"], + "owner_class": "SequentialFitExtractItem", + "read_names": [ + "_sequential_fit_extract.id", + "_easydiffraction_sequential_fit_extract.id" + ], + "unique_name": "sequential_fit_extract._.id" + }, + { + "category_code": "sequential_fit_extract", + "category_entry_name": "_", + "cif_name": "_easydiffraction_sequential_fit_extract.pattern", + "cif_names": ["_easydiffraction_sequential_fit_extract.pattern"], + "context": "factory.SequentialFitExtractFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "pattern", + "descriptor_path": "factory.SequentialFitExtractFactory.default[].pattern", + "docs_anchor": "sequential-fit-extract-pattern", + "docs_page": "sequential_fit_extract", + "edi_name": "_sequential_fit_extract.pattern", + "edi_names": ["_sequential_fit_extract.pattern"], + "owner_class": "SequentialFitExtractItem", + "read_names": [ + "_sequential_fit_extract.pattern", + "_easydiffraction_sequential_fit_extract.pattern" + ], + "unique_name": "sequential_fit_extract._.pattern" + }, + { + "category_code": "sequential_fit_extract", + "category_entry_name": "_", + "cif_name": "_easydiffraction_sequential_fit_extract.required", + "cif_names": ["_easydiffraction_sequential_fit_extract.required"], + "context": "factory.SequentialFitExtractFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "required", + "descriptor_path": "factory.SequentialFitExtractFactory.default[].required", + "docs_anchor": "sequential-fit-extract-required", + "docs_page": "sequential_fit_extract", + "edi_name": "_sequential_fit_extract.required", + "edi_names": ["_sequential_fit_extract.required"], + "owner_class": "SequentialFitExtractItem", + "read_names": [ + "_sequential_fit_extract.required", + "_easydiffraction_sequential_fit_extract.required" + ], + "unique_name": "sequential_fit_extract._.required" + }, + { + "category_code": "sequential_fit_extract", + "category_entry_name": "_", + "cif_name": "_easydiffraction_sequential_fit_extract.target", + "cif_names": ["_easydiffraction_sequential_fit_extract.target"], + "context": "factory.SequentialFitExtractFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "target", + "descriptor_path": "factory.SequentialFitExtractFactory.default[].target", + "docs_anchor": "sequential-fit-extract-target", + "docs_page": "sequential_fit_extract", + "edi_name": "_sequential_fit_extract.target", + "edi_names": ["_sequential_fit_extract.target"], + "owner_class": "SequentialFitExtractItem", + "read_names": [ + "_sequential_fit_extract.target", + "_easydiffraction_sequential_fit_extract.target" + ], + "unique_name": "sequential_fit_extract._.target" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.chunk_size", + "cif_names": ["_easydiffraction_sequential_fit.chunk_size"], + "context": "factory.SequentialFitFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "chunk_size", + "descriptor_path": "factory.SequentialFitFactory.default.chunk_size", + "docs_anchor": "sequential-fit-chunk-size", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.chunk_size", + "edi_names": ["_sequential_fit.chunk_size"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.chunk_size", + "_easydiffraction_sequential_fit.chunk_size" + ], + "unique_name": "sequential_fit.chunk_size" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.data_dir", + "cif_names": ["_easydiffraction_sequential_fit.data_dir"], + "context": "factory.SequentialFitFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "data_dir", + "descriptor_path": "factory.SequentialFitFactory.default.data_dir", + "docs_anchor": "sequential-fit-data-dir", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.data_dir", + "edi_names": ["_sequential_fit.data_dir"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.data_dir", + "_easydiffraction_sequential_fit.data_dir" + ], + "unique_name": "sequential_fit.data_dir" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.file_pattern", + "cif_names": ["_easydiffraction_sequential_fit.file_pattern"], + "context": "factory.SequentialFitFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "file_pattern", + "descriptor_path": "factory.SequentialFitFactory.default.file_pattern", + "docs_anchor": "sequential-fit-file-pattern", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.file_pattern", + "edi_names": ["_sequential_fit.file_pattern"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.file_pattern", + "_easydiffraction_sequential_fit.file_pattern" + ], + "unique_name": "sequential_fit.file_pattern" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.max_workers", + "cif_names": ["_easydiffraction_sequential_fit.max_workers"], + "context": "factory.SequentialFitFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "max_workers", + "descriptor_path": "factory.SequentialFitFactory.default.max_workers", + "docs_anchor": "sequential-fit-max-workers", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.max_workers", + "edi_names": ["_sequential_fit.max_workers"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.max_workers", + "_easydiffraction_sequential_fit.max_workers" + ], + "unique_name": "sequential_fit.max_workers" + }, + { + "category_code": "sequential_fit", + "category_entry_name": null, + "cif_name": "_easydiffraction_sequential_fit.reverse", + "cif_names": ["_easydiffraction_sequential_fit.reverse"], + "context": "factory.SequentialFitFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "reverse", + "descriptor_path": "factory.SequentialFitFactory.default.reverse", + "docs_anchor": "sequential-fit-reverse", + "docs_page": "sequential_fit", + "edi_name": "_sequential_fit.reverse", + "edi_names": ["_sequential_fit.reverse"], + "owner_class": "SequentialFit", + "read_names": [ + "_sequential_fit.reverse", + "_easydiffraction_sequential_fit.reverse" + ], + "unique_name": "sequential_fit.reverse" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.id", + "cif_names": ["_software.id"], + "context": "factory.SoftwareFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.SoftwareFactory.default[].id", + "docs_anchor": "software-id", + "docs_page": "software", + "edi_name": "_software.id", + "edi_names": ["_software.id"], + "owner_class": "SoftwareRole", + "read_names": ["_software.id"], + "unique_name": "software.framework.id" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.name", + "cif_names": ["_software.name"], + "context": "factory.SoftwareFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "name", + "descriptor_path": "factory.SoftwareFactory.default[].name", + "docs_anchor": "software-name", + "docs_page": "software", + "edi_name": "_software.name", + "edi_names": ["_software.name"], + "owner_class": "SoftwareRole", + "read_names": ["_software.name"], + "unique_name": "software.framework.name" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.url", + "cif_names": ["_software.url"], + "context": "factory.SoftwareFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "url", + "descriptor_path": "factory.SoftwareFactory.default[].url", + "docs_anchor": "software-url", + "docs_page": "software", + "edi_name": "_software.url", + "edi_names": ["_software.url"], + "owner_class": "SoftwareRole", + "read_names": ["_software.url"], + "unique_name": "software.framework.url" + }, + { + "category_code": "software", + "category_entry_name": "framework", + "cif_name": "_software.version", + "cif_names": ["_software.version"], + "context": "factory.SoftwareFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "version", + "descriptor_path": "factory.SoftwareFactory.default[].version", + "docs_anchor": "software-version", + "docs_page": "software", + "edi_name": "_software.version", + "edi_names": ["_software.version"], + "owner_class": "SoftwareRole", + "read_names": ["_software.version"], + "unique_name": "software.framework.version" + }, + { + "category_code": "space_group", + "category_entry_name": null, + "cif_name": "_space_group.IT_coordinate_system_code", + "cif_names": [ + "_space_group.IT_coordinate_system_code", + "_space_group_IT_coordinate_system_code", + "_symmetry.IT_coordinate_system_code", + "_symmetry_IT_coordinate_system_code" + ], + "context": "factory.SpaceGroupFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "coord_system_code", + "descriptor_path": "factory.SpaceGroupFactory.default.coord_system_code", + "docs_anchor": "space-group-coord-system-code", + "docs_page": "space_group", + "edi_name": "_space_group.coord_system_code", + "edi_names": ["_space_group.coord_system_code"], + "owner_class": "SpaceGroup", + "read_names": [ + "_space_group.coord_system_code", + "_space_group.IT_coordinate_system_code", + "_space_group_IT_coordinate_system_code", + "_symmetry.IT_coordinate_system_code", + "_symmetry_IT_coordinate_system_code" + ], + "unique_name": "space_group.coord_system_code" + }, + { + "category_code": "space_group", + "category_entry_name": null, + "cif_name": "_space_group.name_H-M_alt", + "cif_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" + ], + "context": "factory.SpaceGroupFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "name_h_m", + "descriptor_path": "factory.SpaceGroupFactory.default.name_h_m", + "docs_anchor": "space-group-name-h-m", + "docs_page": "space_group", + "edi_name": "_space_group.name_h_m", + "edi_names": ["_space_group.name_h_m"], + "owner_class": "SpaceGroup", + "read_names": [ + "_space_group.name_h_m", + "_space_group.name_H-M_alt", + "_space_group_name_H-M_alt", + "_symmetry.space_group_name_H-M", + "_symmetry_space_group_name_H-M" + ], + "unique_name": "space_group.name_h_m" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.coords_xyz", + "cif_names": ["_space_group_Wyckoff.coords_xyz"], + "context": "factory.SpaceGroupWyckoffFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "coords_xyz", + "descriptor_path": "factory.SpaceGroupWyckoffFactory.default[].coords_xyz", + "docs_anchor": "space-group-wyckoff-coords-xyz", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.coords_xyz", + "edi_names": ["_space_group_Wyckoff.coords_xyz"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.coords_xyz"], + "unique_name": "space_group_Wyckoff.coords_xyz" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.id", + "cif_names": ["_space_group_Wyckoff.id"], + "context": "factory.SpaceGroupWyckoffFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "factory.SpaceGroupWyckoffFactory.default[].id", + "docs_anchor": "space-group-wyckoff-id", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.id", + "edi_names": ["_space_group_Wyckoff.id"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.id"], + "unique_name": "space_group_Wyckoff.id" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.letter", + "cif_names": ["_space_group_Wyckoff.letter"], + "context": "factory.SpaceGroupWyckoffFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "letter", + "descriptor_path": "factory.SpaceGroupWyckoffFactory.default[].letter", + "docs_anchor": "space-group-wyckoff-letter", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.letter", + "edi_names": ["_space_group_Wyckoff.letter"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.letter"], + "unique_name": "space_group_Wyckoff.letter" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.multiplicity", + "cif_names": ["_space_group_Wyckoff.multiplicity"], + "context": "factory.SpaceGroupWyckoffFactory.default", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "multiplicity", + "descriptor_path": "factory.SpaceGroupWyckoffFactory.default[].multiplicity", + "docs_anchor": "space-group-wyckoff-multiplicity", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.multiplicity", + "edi_names": ["_space_group_Wyckoff.multiplicity"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.multiplicity"], + "unique_name": "space_group_Wyckoff.multiplicity" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.site_symmetry", + "cif_names": ["_space_group_Wyckoff.site_symmetry"], + "context": "factory.SpaceGroupWyckoffFactory.default", + "descriptor_class": "StringDescriptor", + "descriptor_name": "site_symmetry", + "descriptor_path": "factory.SpaceGroupWyckoffFactory.default[].site_symmetry", + "docs_anchor": "space-group-wyckoff-site-symmetry", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.site_symmetry", + "edi_names": ["_space_group_Wyckoff.site_symmetry"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.site_symmetry"], + "unique_name": "space_group_Wyckoff.site_symmetry" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.adp_probability", + "cif_names": ["_structure_style.adp_probability"], + "context": "factory.StructureStyleFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "adp_probability", + "descriptor_path": "factory.StructureStyleFactory.default.adp_probability", + "docs_anchor": "structure-style-adp-probability", + "docs_page": "structure_style", + "edi_name": "_structure_style.adp_probability", + "edi_names": ["_structure_style.adp_probability"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.adp_probability"], + "unique_name": "structure_style.adp_probability" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.atom_scale", + "cif_names": ["_structure_style.atom_scale"], + "context": "factory.StructureStyleFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "atom_scale", + "descriptor_path": "factory.StructureStyleFactory.default.atom_scale", + "docs_anchor": "structure-style-atom-scale", + "docs_page": "structure_style", + "edi_name": "_structure_style.atom_scale", + "edi_names": ["_structure_style.atom_scale"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.atom_scale"], + "unique_name": "structure_style.atom_scale" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.atom_view", + "cif_names": ["_structure_style.atom_view"], + "context": "factory.StructureStyleFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "atom_view", + "descriptor_path": "factory.StructureStyleFactory.default.atom_view", + "docs_anchor": "structure-style-atom-view", + "docs_page": "structure_style", + "edi_name": "_structure_style.atom_view", + "edi_names": ["_structure_style.atom_view"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.atom_view"], + "unique_name": "structure_style.atom_view" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.color_scheme", + "cif_names": ["_structure_style.color_scheme"], + "context": "factory.StructureStyleFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "color_scheme", + "descriptor_path": "factory.StructureStyleFactory.default.color_scheme", + "docs_anchor": "structure-style-color-scheme", + "docs_page": "structure_style", + "edi_name": "_structure_style.color_scheme", + "edi_names": ["_structure_style.color_scheme"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.color_scheme"], + "unique_name": "structure_style.color_scheme" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_a_max", + "cif_names": ["_structure_view.range_a_max"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_a_max", + "descriptor_path": "factory.StructureViewFactory.default.range_a_max", + "docs_anchor": "structure-view-range-a-max", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_a_max", + "edi_names": ["_structure_view.range_a_max"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_a_max"], + "unique_name": "structure_view.range_a_max" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_a_min", + "cif_names": ["_structure_view.range_a_min"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_a_min", + "descriptor_path": "factory.StructureViewFactory.default.range_a_min", + "docs_anchor": "structure-view-range-a-min", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_a_min", + "edi_names": ["_structure_view.range_a_min"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_a_min"], + "unique_name": "structure_view.range_a_min" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_b_max", + "cif_names": ["_structure_view.range_b_max"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_b_max", + "descriptor_path": "factory.StructureViewFactory.default.range_b_max", + "docs_anchor": "structure-view-range-b-max", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_b_max", + "edi_names": ["_structure_view.range_b_max"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_b_max"], + "unique_name": "structure_view.range_b_max" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_b_min", + "cif_names": ["_structure_view.range_b_min"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_b_min", + "descriptor_path": "factory.StructureViewFactory.default.range_b_min", + "docs_anchor": "structure-view-range-b-min", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_b_min", + "edi_names": ["_structure_view.range_b_min"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_b_min"], + "unique_name": "structure_view.range_b_min" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_c_max", + "cif_names": ["_structure_view.range_c_max"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_c_max", + "descriptor_path": "factory.StructureViewFactory.default.range_c_max", + "docs_anchor": "structure-view-range-c-max", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_c_max", + "edi_names": ["_structure_view.range_c_max"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_c_max"], + "unique_name": "structure_view.range_c_max" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_c_min", + "cif_names": ["_structure_view.range_c_min"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_c_min", + "descriptor_path": "factory.StructureViewFactory.default.range_c_min", + "docs_anchor": "structure-view-range-c-min", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_c_min", + "edi_names": ["_structure_view.range_c_min"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_c_min"], + "unique_name": "structure_view.range_c_min" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.show_labels", + "cif_names": ["_structure_view.show_labels"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "show_labels", + "descriptor_path": "factory.StructureViewFactory.default.show_labels", + "docs_anchor": "structure-view-show-labels", + "docs_page": "structure_view", + "edi_name": "_structure_view.show_labels", + "edi_names": ["_structure_view.show_labels"], + "owner_class": "StructureView", + "read_names": ["_structure_view.show_labels"], + "unique_name": "structure_view.show_labels" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.show_moments", + "cif_names": ["_structure_view.show_moments"], + "context": "factory.StructureViewFactory.default", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "show_moments", + "descriptor_path": "factory.StructureViewFactory.default.show_moments", + "docs_anchor": "structure-view-show-moments", + "docs_page": "structure_view", + "edi_name": "_structure_view.show_moments", + "edi_names": ["_structure_view.show_moments"], + "owner_class": "StructureView", + "read_names": ["_structure_view.show_moments"], + "unique_name": "structure_view.show_moments" + }, + { + "category_code": "verbosity", + "category_entry_name": null, + "cif_name": "_verbosity.fit", + "cif_names": ["_verbosity.fit"], + "context": "factory.VerbosityFactory.default", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "fit", + "descriptor_path": "factory.VerbosityFactory.default.fit", + "docs_anchor": "verbosity-fit", + "docs_page": "verbosity", + "edi_name": "_verbosity.fit", + "edi_names": ["_verbosity.fit"], + "owner_class": "Verbosity", + "read_names": ["_verbosity.fit"], + "unique_name": "verbosity.fit" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.created", + "cif_names": ["_project.created"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "created", + "descriptor_path": "project.metadata.created_descriptor", + "docs_anchor": "metadata-created", + "docs_page": "metadata", + "edi_name": "_metadata.created", + "edi_names": ["_metadata.created"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.created", "_project.created"], + "unique_name": "metadata.created" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.description", + "cif_names": ["_project.description"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "description", + "descriptor_path": "project.metadata.description_descriptor", + "docs_anchor": "metadata-description", + "docs_page": "metadata", + "edi_name": "_metadata.description", + "edi_names": ["_metadata.description"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.description", "_project.description"], + "unique_name": "metadata.description" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.last_modified", + "cif_names": ["_project.last_modified"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "last_modified", + "descriptor_path": "project.metadata.last_modified_descriptor", + "docs_anchor": "metadata-last-modified", + "docs_page": "metadata", + "edi_name": "_metadata.last_modified", + "edi_names": ["_metadata.last_modified"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.last_modified", "_project.last_modified"], + "unique_name": "metadata.last_modified" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.id", + "cif_names": ["_project.id"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "name", + "descriptor_path": "project.metadata.project_id", + "docs_anchor": "metadata-name", + "docs_page": "metadata", + "edi_name": "_metadata.name", + "edi_names": ["_metadata.name"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.name", "_project.id"], + "unique_name": "metadata.name" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_easydiffraction_project.timestamp", + "cif_names": [ + "_easydiffraction_project.timestamp", + "_software.timestamp" + ], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "timestamp", + "descriptor_path": "project.metadata.timestamp_descriptor", + "docs_anchor": "metadata-timestamp", + "docs_page": "metadata", + "edi_name": "_metadata.timestamp", + "edi_names": ["_metadata.timestamp"], + "owner_class": "ProjectMetadata", + "read_names": [ + "_metadata.timestamp", + "_easydiffraction_project.timestamp", + "_software.timestamp" + ], + "unique_name": "metadata.timestamp" + }, + { + "category_code": "metadata", + "category_entry_name": null, + "cif_name": "_project.title", + "cif_names": ["_project.title"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "title", + "descriptor_path": "project.metadata.title_descriptor", + "docs_anchor": "metadata-title", + "docs_page": "metadata", + "edi_name": "_metadata.title", + "edi_names": ["_metadata.title"], + "owner_class": "ProjectMetadata", + "read_names": ["_metadata.title", "_project.title"], + "unique_name": "metadata.title" + }, + { + "category_code": "rendering_plot", + "category_entry_name": null, + "cif_name": "_rendering_plot.type", + "cif_names": ["_rendering_plot.type"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "project.rendering_plot.type", + "docs_anchor": "rendering-plot-type", + "docs_page": "rendering_plot", + "edi_name": "_rendering_plot.type", + "edi_names": ["_rendering_plot.type"], + "owner_class": "RenderingPlot", + "read_names": ["_rendering_plot.type"], + "unique_name": "rendering_plot.type" + }, + { + "category_code": "rendering_structure", + "category_entry_name": null, + "cif_name": "_rendering_structure.type", + "cif_names": ["_rendering_structure.type"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "project.rendering_structure.type", + "docs_anchor": "rendering-structure-type", + "docs_page": "rendering_structure", + "edi_name": "_rendering_structure.type", + "edi_names": ["_rendering_structure.type"], + "owner_class": "RenderingStructure", + "read_names": ["_rendering_structure.type"], + "unique_name": "rendering_structure.type" + }, + { + "category_code": "rendering_table", + "category_entry_name": null, + "cif_name": "_rendering_table.type", + "cif_names": ["_rendering_table.type"], + "context": "project", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type", + "descriptor_path": "project.rendering_table.type", + "docs_anchor": "rendering-table-type", + "docs_page": "rendering_table", + "edi_name": "_rendering_table.type", + "edi_names": ["_rendering_table.type"], + "owner_class": "RenderingTable", + "read_names": ["_rendering_table.type"], + "unique_name": "rendering_table.type" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.cif", + "cif_names": ["_report.cif"], + "context": "project", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "cif", + "descriptor_path": "project.report.cif", + "docs_anchor": "report-cif", + "docs_page": "report", + "edi_name": "_report.cif", + "edi_names": ["_report.cif"], + "owner_class": "Report", + "read_names": ["_report.cif"], + "unique_name": "report.cif" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.html", + "cif_names": ["_report.html"], + "context": "project", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "html", + "descriptor_path": "project.report.html", + "docs_anchor": "report-html", + "docs_page": "report", + "edi_name": "_report.html", + "edi_names": ["_report.html"], + "owner_class": "Report", + "read_names": ["_report.html"], + "unique_name": "report.html" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.html_offline", + "cif_names": ["_report.html_offline"], + "context": "project", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "html_offline", + "descriptor_path": "project.report.html_offline", + "docs_anchor": "report-html-offline", + "docs_page": "report", + "edi_name": "_report.html_offline", + "edi_names": ["_report.html_offline"], + "owner_class": "Report", + "read_names": ["_report.html_offline"], + "unique_name": "report.html_offline" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.pdf", + "cif_names": ["_report.pdf"], + "context": "project", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "pdf", + "descriptor_path": "project.report.pdf", + "docs_anchor": "report-pdf", + "docs_page": "report", + "edi_name": "_report.pdf", + "edi_names": ["_report.pdf"], + "owner_class": "Report", + "read_names": ["_report.pdf"], + "unique_name": "report.pdf" + }, + { + "category_code": "report", + "category_entry_name": null, + "cif_name": "_report.tex", + "cif_names": ["_report.tex"], + "context": "project", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "tex", + "descriptor_path": "project.report.tex", + "docs_anchor": "report-tex", + "docs_page": "report", + "edi_name": "_report.tex", + "edi_names": ["_report.tex"], + "owner_class": "Report", + "read_names": ["_report.tex"], + "unique_name": "report.tex" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.adp_probability", + "cif_names": ["_structure_style.adp_probability"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "adp_probability", + "descriptor_path": "project.structure_style.adp_probability", + "docs_anchor": "structure-style-adp-probability", + "docs_page": "structure_style", + "edi_name": "_structure_style.adp_probability", + "edi_names": ["_structure_style.adp_probability"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.adp_probability"], + "unique_name": "structure_style.adp_probability" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.atom_scale", + "cif_names": ["_structure_style.atom_scale"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "atom_scale", + "descriptor_path": "project.structure_style.atom_scale", + "docs_anchor": "structure-style-atom-scale", + "docs_page": "structure_style", + "edi_name": "_structure_style.atom_scale", + "edi_names": ["_structure_style.atom_scale"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.atom_scale"], + "unique_name": "structure_style.atom_scale" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.atom_view", + "cif_names": ["_structure_style.atom_view"], + "context": "project", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "atom_view", + "descriptor_path": "project.structure_style.atom_view", + "docs_anchor": "structure-style-atom-view", + "docs_page": "structure_style", + "edi_name": "_structure_style.atom_view", + "edi_names": ["_structure_style.atom_view"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.atom_view"], + "unique_name": "structure_style.atom_view" + }, + { + "category_code": "structure_style", + "category_entry_name": null, + "cif_name": "_structure_style.color_scheme", + "cif_names": ["_structure_style.color_scheme"], + "context": "project", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "color_scheme", + "descriptor_path": "project.structure_style.color_scheme", + "docs_anchor": "structure-style-color-scheme", + "docs_page": "structure_style", + "edi_name": "_structure_style.color_scheme", + "edi_names": ["_structure_style.color_scheme"], + "owner_class": "StructureStyle", + "read_names": ["_structure_style.color_scheme"], + "unique_name": "structure_style.color_scheme" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_a_max", + "cif_names": ["_structure_view.range_a_max"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_a_max", + "descriptor_path": "project.structure_view.range_a_max", + "docs_anchor": "structure-view-range-a-max", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_a_max", + "edi_names": ["_structure_view.range_a_max"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_a_max"], + "unique_name": "structure_view.range_a_max" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_a_min", + "cif_names": ["_structure_view.range_a_min"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_a_min", + "descriptor_path": "project.structure_view.range_a_min", + "docs_anchor": "structure-view-range-a-min", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_a_min", + "edi_names": ["_structure_view.range_a_min"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_a_min"], + "unique_name": "structure_view.range_a_min" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_b_max", + "cif_names": ["_structure_view.range_b_max"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_b_max", + "descriptor_path": "project.structure_view.range_b_max", + "docs_anchor": "structure-view-range-b-max", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_b_max", + "edi_names": ["_structure_view.range_b_max"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_b_max"], + "unique_name": "structure_view.range_b_max" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_b_min", + "cif_names": ["_structure_view.range_b_min"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_b_min", + "descriptor_path": "project.structure_view.range_b_min", + "docs_anchor": "structure-view-range-b-min", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_b_min", + "edi_names": ["_structure_view.range_b_min"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_b_min"], + "unique_name": "structure_view.range_b_min" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_c_max", + "cif_names": ["_structure_view.range_c_max"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_c_max", + "descriptor_path": "project.structure_view.range_c_max", + "docs_anchor": "structure-view-range-c-max", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_c_max", + "edi_names": ["_structure_view.range_c_max"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_c_max"], + "unique_name": "structure_view.range_c_max" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.range_c_min", + "cif_names": ["_structure_view.range_c_min"], + "context": "project", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "range_c_min", + "descriptor_path": "project.structure_view.range_c_min", + "docs_anchor": "structure-view-range-c-min", + "docs_page": "structure_view", + "edi_name": "_structure_view.range_c_min", + "edi_names": ["_structure_view.range_c_min"], + "owner_class": "StructureView", + "read_names": ["_structure_view.range_c_min"], + "unique_name": "structure_view.range_c_min" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.show_labels", + "cif_names": ["_structure_view.show_labels"], + "context": "project", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "show_labels", + "descriptor_path": "project.structure_view.show_labels", + "docs_anchor": "structure-view-show-labels", + "docs_page": "structure_view", + "edi_name": "_structure_view.show_labels", + "edi_names": ["_structure_view.show_labels"], + "owner_class": "StructureView", + "read_names": ["_structure_view.show_labels"], + "unique_name": "structure_view.show_labels" + }, + { + "category_code": "structure_view", + "category_entry_name": null, + "cif_name": "_structure_view.show_moments", + "cif_names": ["_structure_view.show_moments"], + "context": "project", + "descriptor_class": "BoolDescriptor", + "descriptor_name": "show_moments", + "descriptor_path": "project.structure_view.show_moments", + "docs_anchor": "structure-view-show-moments", + "docs_page": "structure_view", + "edi_name": "_structure_view.show_moments", + "edi_names": ["_structure_view.show_moments"], + "owner_class": "StructureView", + "read_names": ["_structure_view.show_moments"], + "unique_name": "structure_view.show_moments" + }, + { + "category_code": "verbosity", + "category_entry_name": null, + "cif_name": "_verbosity.fit", + "cif_names": ["_verbosity.fit"], + "context": "project", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "fit", + "descriptor_path": "project.verbosity.fit", + "docs_anchor": "verbosity-fit", + "docs_page": "verbosity", + "edi_name": "_verbosity.fit", + "edi_names": ["_verbosity.fit"], + "owner_class": "Verbosity", + "read_names": ["_verbosity.fit"], + "unique_name": "verbosity.fit" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_11", + "cif_names": [ + "_atom_site_aniso.B_11", + "_atom_site_aniso.U_11", + "_atom_site_aniso.beta_11" + ], + "context": "structure", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_11", + "descriptor_path": "structure.atom_site_aniso[].adp_11", + "docs_anchor": "atom-site-aniso-adp-11", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_11", + "edi_names": ["_atom_site_aniso.adp_11"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_11", + "_atom_site_aniso.B_11", + "_atom_site_aniso.U_11", + "_atom_site_aniso.beta_11" + ], + "unique_name": "atom_site_aniso.adp_11" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_12", + "cif_names": [ + "_atom_site_aniso.B_12", + "_atom_site_aniso.U_12", + "_atom_site_aniso.beta_12" + ], + "context": "structure", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_12", + "descriptor_path": "structure.atom_site_aniso[].adp_12", + "docs_anchor": "atom-site-aniso-adp-12", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_12", + "edi_names": ["_atom_site_aniso.adp_12"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_12", + "_atom_site_aniso.B_12", + "_atom_site_aniso.U_12", + "_atom_site_aniso.beta_12" + ], + "unique_name": "atom_site_aniso.adp_12" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_13", + "cif_names": [ + "_atom_site_aniso.B_13", + "_atom_site_aniso.U_13", + "_atom_site_aniso.beta_13" + ], + "context": "structure", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_13", + "descriptor_path": "structure.atom_site_aniso[].adp_13", + "docs_anchor": "atom-site-aniso-adp-13", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_13", + "edi_names": ["_atom_site_aniso.adp_13"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_13", + "_atom_site_aniso.B_13", + "_atom_site_aniso.U_13", + "_atom_site_aniso.beta_13" + ], + "unique_name": "atom_site_aniso.adp_13" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_22", + "cif_names": [ + "_atom_site_aniso.B_22", + "_atom_site_aniso.U_22", + "_atom_site_aniso.beta_22" + ], + "context": "structure", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_22", + "descriptor_path": "structure.atom_site_aniso[].adp_22", + "docs_anchor": "atom-site-aniso-adp-22", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_22", + "edi_names": ["_atom_site_aniso.adp_22"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_22", + "_atom_site_aniso.B_22", + "_atom_site_aniso.U_22", + "_atom_site_aniso.beta_22" + ], + "unique_name": "atom_site_aniso.adp_22" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_23", + "cif_names": [ + "_atom_site_aniso.B_23", + "_atom_site_aniso.U_23", + "_atom_site_aniso.beta_23" + ], + "context": "structure", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_23", + "descriptor_path": "structure.atom_site_aniso[].adp_23", + "docs_anchor": "atom-site-aniso-adp-23", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_23", + "edi_names": ["_atom_site_aniso.adp_23"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_23", + "_atom_site_aniso.B_23", + "_atom_site_aniso.U_23", + "_atom_site_aniso.beta_23" + ], + "unique_name": "atom_site_aniso.adp_23" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.B_33", + "cif_names": [ + "_atom_site_aniso.B_33", + "_atom_site_aniso.U_33", + "_atom_site_aniso.beta_33" + ], + "context": "structure", + "descriptor_class": "_AnisoAdpParameter", + "descriptor_name": "adp_33", + "descriptor_path": "structure.atom_site_aniso[].adp_33", + "docs_anchor": "atom-site-aniso-adp-33", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.adp_33", + "edi_names": ["_atom_site_aniso.adp_33"], + "owner_class": "AtomSiteAniso", + "read_names": [ + "_atom_site_aniso.adp_33", + "_atom_site_aniso.B_33", + "_atom_site_aniso.U_33", + "_atom_site_aniso.beta_33" + ], + "unique_name": "atom_site_aniso.adp_33" + }, + { + "category_code": "atom_site_aniso", + "category_entry_name": "", + "cif_name": "_atom_site_aniso.label", + "cif_names": ["_atom_site_aniso.label"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "structure.atom_site_aniso[].id", + "docs_anchor": "atom-site-aniso-id", + "docs_page": "atom_site_aniso", + "edi_name": "_atom_site_aniso.id", + "edi_names": ["_atom_site_aniso.id"], + "owner_class": "AtomSiteAniso", + "read_names": ["_atom_site_aniso.id", "_atom_site_aniso.label"], + "unique_name": "atom_site_aniso.id" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.B_iso_or_equiv", + "cif_names": ["_atom_site.B_iso_or_equiv", "_atom_site.U_iso_or_equiv"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "adp_iso", + "descriptor_path": "structure.atom_sites[].adp_iso", + "docs_anchor": "atom-site-adp-iso", + "docs_page": "atom_site", + "edi_name": "_atom_site.adp_iso", + "edi_names": ["_atom_site.adp_iso"], + "owner_class": "AtomSite", + "read_names": [ + "_atom_site.adp_iso", + "_atom_site.B_iso_or_equiv", + "_atom_site.U_iso_or_equiv" + ], + "unique_name": "atom_site.Si.adp_iso" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.ADP_type", + "cif_names": ["_atom_site.ADP_type"], + "context": "structure", + "descriptor_class": "EnumDescriptor", + "descriptor_name": "adp_type", + "descriptor_path": "structure.atom_sites[].adp_type", + "docs_anchor": "atom-site-adp-type", + "docs_page": "atom_site", + "edi_name": "_atom_site.adp_type", + "edi_names": ["_atom_site.adp_type"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.adp_type", "_atom_site.ADP_type"], + "unique_name": "atom_site.Si.adp_type" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.fract_x", + "cif_names": ["_atom_site.fract_x"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "fract_x", + "descriptor_path": "structure.atom_sites[].fract_x", + "docs_anchor": "atom-site-fract-x", + "docs_page": "atom_site", + "edi_name": "_atom_site.fract_x", + "edi_names": ["_atom_site.fract_x"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.fract_x"], + "unique_name": "atom_site.Si.fract_x" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.fract_y", + "cif_names": ["_atom_site.fract_y"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "fract_y", + "descriptor_path": "structure.atom_sites[].fract_y", + "docs_anchor": "atom-site-fract-y", + "docs_page": "atom_site", + "edi_name": "_atom_site.fract_y", + "edi_names": ["_atom_site.fract_y"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.fract_y"], + "unique_name": "atom_site.Si.fract_y" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.fract_z", + "cif_names": ["_atom_site.fract_z"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "fract_z", + "descriptor_path": "structure.atom_sites[].fract_z", + "docs_anchor": "atom-site-fract-z", + "docs_page": "atom_site", + "edi_name": "_atom_site.fract_z", + "edi_names": ["_atom_site.fract_z"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.fract_z"], + "unique_name": "atom_site.Si.fract_z" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.label", + "cif_names": ["_atom_site.label"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "structure.atom_sites[].id", + "docs_anchor": "atom-site-id", + "docs_page": "atom_site", + "edi_name": "_atom_site.id", + "edi_names": ["_atom_site.id"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.id", "_atom_site.label"], + "unique_name": "atom_site.Si.id" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.site_symmetry_multiplicity", + "cif_names": ["_atom_site.site_symmetry_multiplicity"], + "context": "structure", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "multiplicity", + "descriptor_path": "structure.atom_sites[].multiplicity", + "docs_anchor": "atom-site-multiplicity", + "docs_page": "atom_site", + "edi_name": "_atom_site.multiplicity", + "edi_names": ["_atom_site.multiplicity"], + "owner_class": "AtomSite", + "read_names": [ + "_atom_site.multiplicity", + "_atom_site.site_symmetry_multiplicity" + ], + "unique_name": "atom_site.Si.multiplicity" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.occupancy", + "cif_names": ["_atom_site.occupancy"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "occupancy", + "descriptor_path": "structure.atom_sites[].occupancy", + "docs_anchor": "atom-site-occupancy", + "docs_page": "atom_site", + "edi_name": "_atom_site.occupancy", + "edi_names": ["_atom_site.occupancy"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.occupancy"], + "unique_name": "atom_site.Si.occupancy" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.type_symbol", + "cif_names": ["_atom_site.type_symbol"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "type_symbol", + "descriptor_path": "structure.atom_sites[].type_symbol", + "docs_anchor": "atom-site-type-symbol", + "docs_page": "atom_site", + "edi_name": "_atom_site.type_symbol", + "edi_names": ["_atom_site.type_symbol"], + "owner_class": "AtomSite", + "read_names": ["_atom_site.type_symbol"], + "unique_name": "atom_site.Si.type_symbol" + }, + { + "category_code": "atom_site", + "category_entry_name": "Si", + "cif_name": "_atom_site.Wyckoff_symbol", + "cif_names": ["_atom_site.Wyckoff_symbol", "_atom_site.Wyckoff_letter"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "wyckoff_letter", + "descriptor_path": "structure.atom_sites[].wyckoff_letter", + "docs_anchor": "atom-site-wyckoff-letter", + "docs_page": "atom_site", + "edi_name": "_atom_site.wyckoff_letter", + "edi_names": ["_atom_site.wyckoff_letter"], + "owner_class": "AtomSite", + "read_names": [ + "_atom_site.wyckoff_letter", + "_atom_site.Wyckoff_symbol", + "_atom_site.Wyckoff_letter" + ], + "unique_name": "atom_site.Si.wyckoff_letter" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.angle_alpha", + "cif_names": ["_cell.angle_alpha"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "angle_alpha", + "descriptor_path": "structure.cell.angle_alpha", + "docs_anchor": "cell-angle-alpha", + "docs_page": "cell", + "edi_name": "_cell.angle_alpha", + "edi_names": ["_cell.angle_alpha"], + "owner_class": "Cell", + "read_names": ["_cell.angle_alpha"], + "unique_name": "inventory_structure.cell.angle_alpha" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.angle_beta", + "cif_names": ["_cell.angle_beta"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "angle_beta", + "descriptor_path": "structure.cell.angle_beta", + "docs_anchor": "cell-angle-beta", + "docs_page": "cell", + "edi_name": "_cell.angle_beta", + "edi_names": ["_cell.angle_beta"], + "owner_class": "Cell", + "read_names": ["_cell.angle_beta"], + "unique_name": "inventory_structure.cell.angle_beta" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.angle_gamma", + "cif_names": ["_cell.angle_gamma"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "angle_gamma", + "descriptor_path": "structure.cell.angle_gamma", + "docs_anchor": "cell-angle-gamma", + "docs_page": "cell", + "edi_name": "_cell.angle_gamma", + "edi_names": ["_cell.angle_gamma"], + "owner_class": "Cell", + "read_names": ["_cell.angle_gamma"], + "unique_name": "inventory_structure.cell.angle_gamma" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.length_a", + "cif_names": ["_cell.length_a"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "length_a", + "descriptor_path": "structure.cell.length_a", + "docs_anchor": "cell-length-a", + "docs_page": "cell", + "edi_name": "_cell.length_a", + "edi_names": ["_cell.length_a"], + "owner_class": "Cell", + "read_names": ["_cell.length_a"], + "unique_name": "inventory_structure.cell.length_a" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.length_b", + "cif_names": ["_cell.length_b"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "length_b", + "descriptor_path": "structure.cell.length_b", + "docs_anchor": "cell-length-b", + "docs_page": "cell", + "edi_name": "_cell.length_b", + "edi_names": ["_cell.length_b"], + "owner_class": "Cell", + "read_names": ["_cell.length_b"], + "unique_name": "inventory_structure.cell.length_b" + }, + { + "category_code": "cell", + "category_entry_name": null, + "cif_name": "_cell.length_c", + "cif_names": ["_cell.length_c"], + "context": "structure", + "descriptor_class": "Parameter", + "descriptor_name": "length_c", + "descriptor_path": "structure.cell.length_c", + "docs_anchor": "cell-length-c", + "docs_page": "cell", + "edi_name": "_cell.length_c", + "edi_names": ["_cell.length_c"], + "owner_class": "Cell", + "read_names": ["_cell.length_c"], + "unique_name": "inventory_structure.cell.length_c" + }, + { + "category_code": "geom", + "category_entry_name": null, + "cif_name": "_geom.bond_distance_incr", + "cif_names": ["_geom.bond_distance_incr"], + "context": "structure", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "bond_distance_inc", + "descriptor_path": "structure.geom.bond_distance_inc", + "docs_anchor": "geom-bond-distance-inc", + "docs_page": "geom", + "edi_name": "_geom.bond_distance_inc", + "edi_names": ["_geom.bond_distance_inc"], + "owner_class": "Geom", + "read_names": ["_geom.bond_distance_inc", "_geom.bond_distance_incr"], + "unique_name": "inventory_structure.geom.bond_distance_inc" + }, + { + "category_code": "geom", + "category_entry_name": null, + "cif_name": "_geom.min_bond_distance_cutoff", + "cif_names": ["_geom.min_bond_distance_cutoff"], + "context": "structure", + "descriptor_class": "NumericDescriptor", + "descriptor_name": "min_bond_distance_cutoff", + "descriptor_path": "structure.geom.min_bond_distance_cutoff", + "docs_anchor": "geom-min-bond-distance-cutoff", + "docs_page": "geom", + "edi_name": "_geom.min_bond_distance_cutoff", + "edi_names": ["_geom.min_bond_distance_cutoff"], + "owner_class": "Geom", + "read_names": ["_geom.min_bond_distance_cutoff"], + "unique_name": "inventory_structure.geom.min_bond_distance_cutoff" + }, + { + "category_code": "space_group", + "category_entry_name": null, + "cif_name": "_space_group.IT_coordinate_system_code", + "cif_names": [ + "_space_group.IT_coordinate_system_code", + "_space_group_IT_coordinate_system_code", + "_symmetry.IT_coordinate_system_code", + "_symmetry_IT_coordinate_system_code" + ], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "coord_system_code", + "descriptor_path": "structure.space_group.coord_system_code", + "docs_anchor": "space-group-coord-system-code", + "docs_page": "space_group", + "edi_name": "_space_group.coord_system_code", + "edi_names": ["_space_group.coord_system_code"], + "owner_class": "SpaceGroup", + "read_names": [ + "_space_group.coord_system_code", + "_space_group.IT_coordinate_system_code", + "_space_group_IT_coordinate_system_code", + "_symmetry.IT_coordinate_system_code", + "_symmetry_IT_coordinate_system_code" + ], + "unique_name": "inventory_structure.space_group.coord_system_code" + }, + { + "category_code": "space_group", + "category_entry_name": null, + "cif_name": "_space_group.name_H-M_alt", + "cif_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" + ], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "name_h_m", + "descriptor_path": "structure.space_group.name_h_m", + "docs_anchor": "space-group-name-h-m", + "docs_page": "space_group", + "edi_name": "_space_group.name_h_m", + "edi_names": ["_space_group.name_h_m"], + "owner_class": "SpaceGroup", + "read_names": [ + "_space_group.name_h_m", + "_space_group.name_H-M_alt", + "_space_group_name_H-M_alt", + "_symmetry.space_group_name_H-M", + "_symmetry_space_group_name_H-M" + ], + "unique_name": "inventory_structure.space_group.name_h_m" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.coords_xyz", + "cif_names": ["_space_group_Wyckoff.coords_xyz"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "coords_xyz", + "descriptor_path": "structure.space_group_wyckoff[].coords_xyz", + "docs_anchor": "space-group-wyckoff-coords-xyz", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.coords_xyz", + "edi_names": ["_space_group_Wyckoff.coords_xyz"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.coords_xyz"], + "unique_name": "space_group_Wyckoff.coords_xyz" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.id", + "cif_names": ["_space_group_Wyckoff.id"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "id", + "descriptor_path": "structure.space_group_wyckoff[].id", + "docs_anchor": "space-group-wyckoff-id", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.id", + "edi_names": ["_space_group_Wyckoff.id"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.id"], + "unique_name": "space_group_Wyckoff.id" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.letter", + "cif_names": ["_space_group_Wyckoff.letter"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "letter", + "descriptor_path": "structure.space_group_wyckoff[].letter", + "docs_anchor": "space-group-wyckoff-letter", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.letter", + "edi_names": ["_space_group_Wyckoff.letter"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.letter"], + "unique_name": "space_group_Wyckoff.letter" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.multiplicity", + "cif_names": ["_space_group_Wyckoff.multiplicity"], + "context": "structure", + "descriptor_class": "IntegerDescriptor", + "descriptor_name": "multiplicity", + "descriptor_path": "structure.space_group_wyckoff[].multiplicity", + "docs_anchor": "space-group-wyckoff-multiplicity", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.multiplicity", + "edi_names": ["_space_group_Wyckoff.multiplicity"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.multiplicity"], + "unique_name": "space_group_Wyckoff.multiplicity" + }, + { + "category_code": "space_group_Wyckoff", + "category_entry_name": "", + "cif_name": "_space_group_Wyckoff.site_symmetry", + "cif_names": ["_space_group_Wyckoff.site_symmetry"], + "context": "structure", + "descriptor_class": "StringDescriptor", + "descriptor_name": "site_symmetry", + "descriptor_path": "structure.space_group_wyckoff[].site_symmetry", + "docs_anchor": "space-group-wyckoff-site-symmetry", + "docs_page": "space_group_Wyckoff", + "edi_name": "_space_group_Wyckoff.site_symmetry", + "edi_names": ["_space_group_Wyckoff.site_symmetry"], + "owner_class": "SpaceGroupWyckoff", + "read_names": ["_space_group_Wyckoff.site_symmetry"], + "unique_name": "space_group_Wyckoff.site_symmetry" + } + ], + "generated_by": "tools/edi_handler_inventory.py", + "schema": "edi-handler-inventory-v2" +} diff --git a/docs/dev/adrs/accepted/iucr-cif-tag-alignment.md b/docs/dev/adrs/accepted/iucr-cif-tag-alignment.md index d36193346..255a4b410 100644 --- a/docs/dev/adrs/accepted/iucr-cif-tag-alignment.md +++ b/docs/dev/adrs/accepted/iucr-cif-tag-alignment.md @@ -13,6 +13,11 @@ runs alongside the [`python-cif-category-correspondence.md`](python-cif-category-correspondence.md) suggestion (Python-side correspondence). +[`edstar-project-persistence.md`](edstar-project-persistence.md) +supersedes this ADR's default-save file-extension and project-tag naming +policy. This ADR remains authoritative for strict IUCr/pdCIF report +export under `reports/.cif`. + Grounded in: - COMCIFS @@ -169,7 +174,7 @@ dotted DDLm tag emitted by the IUCr CIF report writer. | `_atom_site.B_iso_or_equiv` / `U_iso_or_equiv` | core | Structure — single-tag emit | `_atom_site.B_iso_or_equiv` xor `_atom_site.U_iso_or_equiv` per row, based on `_atom_site.ADP_type`. | | `_atom_site_aniso.B_*` / `U_*` | core | Structure — single-tag emit | `_atom_site_aniso.B_*` xor `_atom_site_aniso.U_*` per row. | | `_space_group.name_h_m` | core (`_space_group.name_H-M_alt`) | Structure — casing fix | `_space_group.name_H-M_alt`. | -| `_space_group.it_coordinate_system_code` | core (`_space_group.IT_coordinate_system_code`) | Structure — casing fix | `_space_group.IT_coordinate_system_code`. | +| `_space_group.coord_system_code` | core (`_space_group.IT_coordinate_system_code`) | Structure — casing fix | `_space_group.IT_coordinate_system_code`. | | symmetry operations | core (`_space_group_symop.*`) | (not emitted today) | `_space_group_symop.id` + `_space_group_symop.operation_xyz` loop alongside the H-M name. | | `_diffrn.ambient_temperature`, `ambient_pressure` | core | Experiment — unchanged | `_diffrn.ambient_temperature`, `_diffrn.ambient_pressure`. | | `_diffrn.ambient_magnetic_field`, `ambient_electric_field` | none | Experiment — unchanged | `_easydiffraction_diffrn.ambient_magnetic_field`, `…electric_field` (project extension). | @@ -214,7 +219,7 @@ In `structures/.cif`: a different category (`_space_group_Wyckoff.letter`). - Rename `_space_group.name_h_m` → `_space_group.name_H-M_alt` (uppercase hyphenated H-M, with `_alt` suffix per dictionary). -- Rename `_space_group.it_coordinate_system_code` → +- Rename `_space_group.coord_system_code` → `_space_group.IT_coordinate_system_code` (uppercase IT). - ADP single-tag emission per row (see §4). - All other `_cell.*`, `_atom_site.*`, `_atom_site_aniso.*`, @@ -222,7 +227,7 @@ In `structures/.cif`: Python attribute names stay lowercase (`atom_site.adp_type`, `atom_site.wyckoff_letter`, `space_group.name_h_m`, -`space_group.it_coordinate_system_code`). Only emitted CIF tags change. +`space_group.coord_system_code`). Only emitted CIF tags change. #### 1.2 Analysis tier — topology-neutral `_fit_result.*`, IUCr renaming on export only diff --git a/docs/dev/adrs/accepted/loop-category-key-identity.md b/docs/dev/adrs/accepted/loop-category-key-identity.md index 4334b4e13..fc9450848 100644 --- a/docs/dev/adrs/accepted/loop-category-key-identity.md +++ b/docs/dev/adrs/accepted/loop-category-key-identity.md @@ -102,10 +102,10 @@ next section. These fields are serialized in loop rows and look identity-like, but they do not define the collection key. -| Python field | Area | Collection class | Category code | CIF tag | Role | -| ------------------- | ---------- | ------------------------------------------- | ------------- | -------------------------- | ------------------------------------------------------------------------------------- | -| `phase_id` | Experiment | `PowderCwlReflnData` / `PowderTofReflnData` | `refln` | `_refln.phase_id` | References the linked phase for a calculated reflection. Row key remains `_refln.id`. | -| `param_unique_name` | Analysis | `Aliases` | `alias` | `_alias.param_unique_name` | References the target parameter. Row key remains `_alias.label`. | +| Python field | Area | Collection class | Category code | CIF tag | Role | +| ----------------------- | ---------- | ------------------------------------------- | ------------- | ------------------------------ | ------------------------------------------------------------------------------------- | +| `phase_id` | Experiment | `PowderCwlReflnData` / `PowderTofReflnData` | `refln` | `_refln.phase_id` | References the linked phase for a calculated reflection. Row key remains `_refln.id`. | +| `parameter_unique_name` | Analysis | `Aliases` | `alias` | `_alias.parameter_unique_name` | References the target parameter. Row key remains `_alias.label`. | ## Naming Guidance diff --git a/docs/dev/adrs/accepted/model-sample-absorption.md b/docs/dev/adrs/accepted/model-sample-absorption.md index 64afc5251..73c2e8de6 100644 --- a/docs/dev/adrs/accepted/model-sample-absorption.md +++ b/docs/dev/adrs/accepted/model-sample-absorption.md @@ -29,7 +29,7 @@ This is not hypothetical. The verification reference the unmodelled correction is the _entire_ intensity residual on the companion `pd-neut-cwl_tch-fcj_abs_lab6` page (≈5 % profile difference), while the μR = 0 page passes to corr 0.9999. See -[issue #119](../../issues/open.md). +[issue #119](../../issues/open/highest_model-sample-absorption-debye-scherrer-r.md). ### What the three reference sources provide diff --git a/docs/dev/adrs/accepted/parameter-correlation-persistence.md b/docs/dev/adrs/accepted/parameter-correlation-persistence.md index 9b51c0362..692e55618 100644 --- a/docs/dev/adrs/accepted/parameter-correlation-persistence.md +++ b/docs/dev/adrs/accepted/parameter-correlation-persistence.md @@ -39,8 +39,8 @@ Store one row per unique parameter pair with these fields: - `id` - `source_kind` -- `param_unique_name_i` -- `param_unique_name_j` +- `parameter_unique_name_i` +- `parameter_unique_name_j` - `correlation` Example: @@ -49,16 +49,16 @@ Example: loop_ _fit_parameter_correlation.id _fit_parameter_correlation.source_kind -_fit_parameter_correlation.param_unique_name_i -_fit_parameter_correlation.param_unique_name_j +_fit_parameter_correlation.parameter_unique_name_i +_fit_parameter_correlation.parameter_unique_name_j _fit_parameter_correlation.correlation 1 posterior cosio.atom_site.Co1.adp_iso cosio.atom_site.Co2.adp_iso 0.87 ``` Normalize each row to the upper triangle excluding the diagonal. -`param_unique_name_i` and `param_unique_name_j` use a stable ordering so -only one unordered pair is stored. The diagonal is omitted because it is -always `1.0` and can be rebuilt on load. +`parameter_unique_name_i` and `parameter_unique_name_j` use a stable +ordering so only one unordered pair is stored. The diagonal is omitted +because it is always `1.0` and can be rebuilt on load. Use the same loop for deterministic and Bayesian projections. The source is carried by `source_kind`, currently `deterministic` or `posterior`. diff --git a/docs/dev/adrs/accepted/preferred-orientation-category.md b/docs/dev/adrs/accepted/preferred-orientation-category.md index cf07c156d..2ed7309c6 100644 --- a/docs/dev/adrs/accepted/preferred-orientation-category.md +++ b/docs/dev/adrs/accepted/preferred-orientation-category.md @@ -202,9 +202,9 @@ backward-compatible change. ### 3. User-facing API (Jupyter) ```python -import easydiffraction as ed +import easydiffraction as edi -project = ed.Project() +project = edi.Project() project.experiments.add(name='hrpt', ...) # CW powder, neutron expt = project.experiments['hrpt'] diff --git a/docs/dev/adrs/accepted/project-facade-and-persistence.md b/docs/dev/adrs/accepted/project-facade-and-persistence.md index 7a3fcf66c..e2093a417 100644 --- a/docs/dev/adrs/accepted/project-facade-and-persistence.md +++ b/docs/dev/adrs/accepted/project-facade-and-persistence.md @@ -12,6 +12,13 @@ Accepted current design. Persistence. +## Amendment + +[`edstar-project-persistence.md`](edstar-project-persistence.md) +replaces the default project-save file layout with `.edi` files. This +ADR remains historical context for the `Project` facade, singleton +sections versus real datablocks, and report artifact placement. + ## Context `Project` is the top-level user facade. It owns project metadata, diff --git a/docs/dev/adrs/accepted/project-summary-rendering.md b/docs/dev/adrs/accepted/project-summary-rendering.md index 85e9e72b0..a9162d5b5 100644 --- a/docs/dev/adrs/accepted/project-summary-rendering.md +++ b/docs/dev/adrs/accepted/project-summary-rendering.md @@ -253,9 +253,9 @@ configuration categories: each persisted scalar descriptor is set directly. ```python -import easydiffraction as ed +import easydiffraction as edi -project = ed.Project() +project = edi.Project() # … set up structures, experiments, run fit … # Configure once — persisted in project.cif (see §1.3 below). diff --git a/docs/dev/adrs/accepted/python-cif-category-correspondence.md b/docs/dev/adrs/accepted/python-cif-category-correspondence.md index a9963270e..076332d4b 100644 --- a/docs/dev/adrs/accepted/python-cif-category-correspondence.md +++ b/docs/dev/adrs/accepted/python-cif-category-correspondence.md @@ -5,6 +5,12 @@ ## Context +[`edstar-project-persistence.md`](edstar-project-persistence.md) +replaces this ADR's scoped Python-to-`project.cif` correspondence with +Python-to-Edi correspondence for regular project persistence. This ADR +remains historical context for the old CIF layout and for the reasoning +behind previous Python/CIF naming exceptions. + EasyDiffraction exposes a Python object graph and persists state in CIF files. The public Python API should be easy for scientists to predict, while CIF output should remain readable and semantically useful. @@ -124,7 +130,7 @@ to objects reached from the current `Project` root, for example | `analysis.sequential_fit_extract[id].pattern` | `_sequential_fit_extract.pattern` | Yes | Direct collection mapping. | | `analysis.sequential_fit_extract[id].required` | `_sequential_fit_extract.required` | Yes | Direct collection mapping. | | `analysis.aliases[label].label` | `_alias.label` | Partly | Python collection is plural; CIF row category is singular. | -| `analysis.aliases[label].param_unique_name` | `_alias.param_unique_name` | Partly | Python collection is plural; CIF row category is singular. | +| `analysis.aliases[label].parameter_unique_name` | `_alias.parameter_unique_name` | Partly | Python collection is plural; CIF row category is singular. | | `analysis.constraints[id].id` | `_constraint.id` | Yes | Direct explicit row-key mapping; older CIFs may backfill the id from the expression left-hand side. | | `analysis.constraints[id].expression` | `_constraint.expression` | Yes | Direct row-field mapping; `lhs_alias` and `rhs_expr` are derived Python helpers. | @@ -158,8 +164,8 @@ to objects reached from the current `Project` root, for example | `experiment.peak.asym_fcj_1..2` | `_peak.asym_fcj_1..2` | Yes | CWL peak field group. | | `experiment.peak.broad_gauss_sigma_0..2` | `_peak.gauss_sigma_0..2` | Partly | Python prefixes the family with `broad_`; CIF tags omit that grouping prefix. | | `experiment.peak.broad_lorentz_gamma_0..2` | `_peak.lorentz_gamma_0..2` | Partly | Python prefixes the family with `broad_`; CIF tags omit that grouping prefix. | -| `experiment.peak.exp_rise_alpha_0..1` | `_peak.rise_alpha_0..1` | Partly | Python prefixes the family with `exp_`; CIF tags omit that grouping prefix. | -| `experiment.peak.exp_decay_beta_0..1` | `_peak.decay_beta_0..1` | Partly | Python prefixes the family with `exp_`; CIF tags omit that grouping prefix. | +| `experiment.peak.rise_alpha_0..1` | `_peak.rise_alpha_0..1` | Partly | Python prefixes the family with `exp_`; CIF tags omit that grouping prefix. | +| `experiment.peak.decay_beta_0..1` | `_peak.decay_beta_0..1` | Partly | Python prefixes the family with `exp_`; CIF tags omit that grouping prefix. | | `experiment.peak.dexp_*` | `_peak.dexp_*` | Yes | TOF double-exponential peak field group. | | `experiment.peak.damp_q` | `_peak.damp_q` | Yes | Total-scattering peak field. | | `experiment.peak.broad_q` | `_peak.broad_q` | Yes | Total-scattering peak field. | @@ -209,32 +215,32 @@ to objects reached from the current `Project` root, for example ### Structure Configuration -| Current Python path | Current CIF path | Match? | Notes | -| ------------------------------------------------- | ---------------------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------- | -| `structure.cell.length_a` | `_cell.length_a` | Yes | Direct category mapping. | -| `structure.cell.length_b` | `_cell.length_b` | Yes | Direct category mapping. | -| `structure.cell.length_c` | `_cell.length_c` | Yes | Direct category mapping. | -| `structure.cell.angle_alpha` | `_cell.angle_alpha` | Yes | Direct category mapping. | -| `structure.cell.angle_beta` | `_cell.angle_beta` | Yes | Direct category mapping. | -| `structure.cell.angle_gamma` | `_cell.angle_gamma` | Yes | Direct category mapping. | -| `structure.space_group.name_h_m` | `_space_group.name_H-M_alt` | Partly | Default write uses dictionary-canonical casing; legacy `_space_group_name_H-M_alt` and `_symmetry*` alternatives are accepted on read. | -| `structure.space_group.it_coordinate_system_code` | `_space_group.IT_coordinate_system_code` | Partly | Default write uses dictionary-canonical casing; legacy underscore-form and `_symmetry*` alternatives are accepted on read. | -| `structure.atom_sites[label].label` | `_atom_site.label` | Yes | Direct row-field mapping. | -| `structure.atom_sites[label].type_symbol` | `_atom_site.type_symbol` | Yes | Direct row-field mapping. | -| `structure.atom_sites[label].fract_x` | `_atom_site.fract_x` | Yes | Direct row-field mapping. | -| `structure.atom_sites[label].fract_y` | `_atom_site.fract_y` | Yes | Direct row-field mapping. | -| `structure.atom_sites[label].fract_z` | `_atom_site.fract_z` | Yes | Direct row-field mapping. | -| `structure.atom_sites[label].wyckoff_letter` | `_atom_site.Wyckoff_symbol` | Partly | Default write uses dictionary-canonical tag; legacy `_atom_site.Wyckoff_letter` is accepted on read. | -| `structure.atom_sites[label].occupancy` | `_atom_site.occupancy` | Yes | Direct row-field mapping. | -| `structure.atom_sites[label].adp_iso` | `_atom_site.B_iso_or_equiv` or `_atom_site.U_iso_or_equiv` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | -| `structure.atom_sites[label].adp_type` | `_atom_site.ADP_type` | Partly | Default write uses dictionary-canonical capitalization; legacy `_atom_site.adp_type` is accepted on read. | -| `structure.atom_site_aniso[label].label` | `_atom_site_aniso.label` | Yes | Direct row-field mapping. | -| `structure.atom_site_aniso[label].adp_11` | `_atom_site_aniso.B_11` or `_atom_site_aniso.U_11` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | -| `structure.atom_site_aniso[label].adp_22` | `_atom_site_aniso.B_22` or `_atom_site_aniso.U_22` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | -| `structure.atom_site_aniso[label].adp_33` | `_atom_site_aniso.B_33` or `_atom_site_aniso.U_33` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | -| `structure.atom_site_aniso[label].adp_12` | `_atom_site_aniso.B_12` or `_atom_site_aniso.U_12` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | -| `structure.atom_site_aniso[label].adp_13` | `_atom_site_aniso.B_13` or `_atom_site_aniso.U_13` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | -| `structure.atom_site_aniso[label].adp_23` | `_atom_site_aniso.B_23` or `_atom_site_aniso.U_23` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | +| Current Python path | Current CIF path | Match? | Notes | +| -------------------------------------------- | ---------------------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `structure.cell.length_a` | `_cell.length_a` | Yes | Direct category mapping. | +| `structure.cell.length_b` | `_cell.length_b` | Yes | Direct category mapping. | +| `structure.cell.length_c` | `_cell.length_c` | Yes | Direct category mapping. | +| `structure.cell.angle_alpha` | `_cell.angle_alpha` | Yes | Direct category mapping. | +| `structure.cell.angle_beta` | `_cell.angle_beta` | Yes | Direct category mapping. | +| `structure.cell.angle_gamma` | `_cell.angle_gamma` | Yes | Direct category mapping. | +| `structure.space_group.name_h_m` | `_space_group.name_H-M_alt` | Partly | Default write uses dictionary-canonical casing; legacy `_space_group_name_H-M_alt` and `_symmetry*` alternatives are accepted on read. | +| `structure.space_group.coord_system_code` | `_space_group.IT_coordinate_system_code` | Partly | Default write uses dictionary-canonical casing; legacy underscore-form and `_symmetry*` alternatives are accepted on read. | +| `structure.atom_sites[label].label` | `_atom_site.label` | Yes | Direct row-field mapping. | +| `structure.atom_sites[label].type_symbol` | `_atom_site.type_symbol` | Yes | Direct row-field mapping. | +| `structure.atom_sites[label].fract_x` | `_atom_site.fract_x` | Yes | Direct row-field mapping. | +| `structure.atom_sites[label].fract_y` | `_atom_site.fract_y` | Yes | Direct row-field mapping. | +| `structure.atom_sites[label].fract_z` | `_atom_site.fract_z` | Yes | Direct row-field mapping. | +| `structure.atom_sites[label].wyckoff_letter` | `_atom_site.Wyckoff_symbol` | Partly | Default write uses dictionary-canonical tag; legacy `_atom_site.Wyckoff_letter` is accepted on read. | +| `structure.atom_sites[label].occupancy` | `_atom_site.occupancy` | Yes | Direct row-field mapping. | +| `structure.atom_sites[label].adp_iso` | `_atom_site.B_iso_or_equiv` or `_atom_site.U_iso_or_equiv` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | +| `structure.atom_sites[label].adp_type` | `_atom_site.ADP_type` | Partly | Default write uses dictionary-canonical capitalization; legacy `_atom_site.adp_type` is accepted on read. | +| `structure.atom_site_aniso[label].label` | `_atom_site_aniso.label` | Yes | Direct row-field mapping. | +| `structure.atom_site_aniso[label].adp_11` | `_atom_site_aniso.B_11` or `_atom_site_aniso.U_11` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | +| `structure.atom_site_aniso[label].adp_22` | `_atom_site_aniso.B_22` or `_atom_site_aniso.U_22` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | +| `structure.atom_site_aniso[label].adp_33` | `_atom_site_aniso.B_33` or `_atom_site_aniso.U_33` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | +| `structure.atom_site_aniso[label].adp_12` | `_atom_site_aniso.B_12` or `_atom_site_aniso.U_12` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | +| `structure.atom_site_aniso[label].adp_13` | `_atom_site_aniso.B_13` or `_atom_site_aniso.U_13` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | +| `structure.atom_site_aniso[label].adp_23` | `_atom_site_aniso.B_23` or `_atom_site_aniso.U_23` | No | Python uses type-neutral ADP name; CIF uses B/U-specific tags. | ### Not Represented In V1 diff --git a/docs/dev/adrs/accepted/resource-naming.md b/docs/dev/adrs/accepted/resource-naming.md new file mode 100644 index 000000000..e400243bd --- /dev/null +++ b/docs/dev/adrs/accepted/resource-naming.md @@ -0,0 +1,305 @@ +# ADR: Downloadable Resource Naming + +**Status:** Accepted **Date:** 2026-06-14 + +> This ADR follows [`AGENTS.md`](../../../../AGENTS.md). No deliberate +> exception to those instructions is taken. + +## Group + +Naming. + +> Sibling of [`data-source-pinning.md`](data-source-pinning.md): that +> ADR pins _which snapshot_ of the data repository to fetch and decides +> replace-in-place under stable identifiers; this ADR decides what those +> identifiers are, for both downloadable datasets and tutorials. + +## Context + +Downloadable resources — example/tutorial datasets fetched by +`download_data()` and tutorial notebooks fetched by +`download_tutorial()` — are currently identified by integer ids +(`download_data(id=44)`), with files named `ed-.` and an +`index.json` keyed by those integers. + +This is fragile: + +- **Integers churn.** Replacing a dataset has been done by minting a new + id with a `replaces` pointer, producing chains like + `28 -> 30 -> 36 -> 40 -> 44` for one LBCO/HRPT project. Tutorials, in + turn, are numbered by _creation_ order (`ed-1 … ed-29`); inserting a + beginner tutorial later, or removing/replacing one, would renumber the + rest. So the number is never a stable handle. +- **Integers are opaque.** `download_data(44)` says nothing; a reader + cannot tell a structure from a project, or LBCO from Co2SiO4. +- **It fights replace-in-place.** `data-source-pinning.md` decides data + is updated by overwriting files under stable identifiers; integer ids + with `replaces` chains contradict that. + +The companion `data-source-pinning` ADR establishes "stable identifiers, +replace-in-place" (its Decision 5) but leaves the identifier _form_ +undecided. This ADR fixes that form, for datasets and tutorials alike. + +## Decision + +1. **Descriptive dash-prefixed names, not integers.** Every downloadable + resource is identified by a stable, lowercase-dash name. Datasets + carry their kind as a short **category prefix** joined by a dash — + `-` — so the whole id reads as one name, not a path: + - `struct-` — crystal-structure import CIFs, + - `expt-` — EasyDiffraction experiment-definition files, + - `meas-` — raw measured or simulated data files, + - `proj-` — saved EasyDiffraction project archives. + + So `struct-lbco`, `meas-cosio-d20`, `proj-lbco-hrpt-dream`. A slash + form (`measured/cosio-d20`) was rejected because it reads as a + filesystem path ("put `cosio-d20` into folder `measured`"); the + dash-prefix keeps the category visible while staying a single, + copy-pasteable token. Tutorials use a bare descriptive slug (e.g. + `refine-lbco-hrpt-from-cif`, `pdf-si-nomad`, + `bayesian-emcee-resume-lbco-hrpt`); their leading verb already + conveys the activity, and they have no cross-category name collision + to disambiguate, so they take no category prefix. The name encodes + the sample, technique, and instrument/qualifier needed to keep it + unique and self-explanatory, and carries no volatile facts that would + force a rename on unrelated changes. + +2. **The slug is the stable identity.** Updating a resource overwrites + the file under the same slug (per `data-source-pinning`); a genuinely + different resource gets a genuinely different slug. The integer + `replaces` chains collapse — each chain becomes one slug. A removed + slug is absent from the catalog and fails with the same clear + unknown-resource error as any other well-formed but missing id + (Decision 7). + +3. **Drop integer ids as persistent keys.** Integer ids are not used to + identify resources in `index.json`, tutorials, documentation, tests, + or any saved code. The project is in beta, so no integer-id + compatibility shim is kept. + +4. **Order is separate metadata, never the id.** Presentation/learning + order (which mainly matters for tutorials) lives in the MkDocs + navigation and/or an explicit `order` field in the catalog — never in + the slug. Inserting, removing, or reordering resources touches that + metadata only, with no renames. + +5. **Files stay in category folders; the index maps name to path.** The + repository keeps full-word category folders for tidiness — + `data/structures/`, `data/experiments/`, `data/measured/`, + `data/projects/` — so the repository path is + `data//.`, and `index.json` maps each + dash-prefixed name to that path. The locally **downloaded** file is + named after the id (`.`, e.g. `meas-lbco-hrpt.xye`) so the + saved file matches the name the user typed. The extension follows the + format: generic IUCr structures `.cif`; EasyDiffraction + experiment-definition files `.edi`; raw measured data in its native + extension (`.xye`/`.gr`/`.dat`/`.xys`); multi-file scans and project + archives `.zip`. + +6. **Listings are compact; the name is the only persisted handle.** + `list_data()` shows `name`, `format` (the bare extension), and + `description`; `list_tutorials()` shows `name` and the tutorial + title/description. The table renderer already prepends a row number, + so no separate `#` column is added, and no redundant `file` column is + shown (it only repeats the name plus extension). The name is the + canonical argument everywhere: + - **The name is the canonical argument.** `download_data(name)` and + `download_tutorial(name)` take the name string as their first + positional argument, named `name`; there is no `id=` keyword. + - **The integer shorthand is an interactive convenience on both + surfaces.** Both the Python functions and the CLI download command + **may** additionally accept a positional integer that selects a row + by the renderer's number. The shorthand is stateless: the integer + is resolved against the same deterministic catalog order that + `list_*` prints, recomputed fresh at call time. This number is a + transient row index tied to that order, never an identity, and + there is no `--id` flag. + - **Saved artifacts are name-only.** Tutorials, documentation, tests, + and any other saved code use names exclusively; the positional + integer is for live exploration only and must never be written into + a persisted artifact, or it reintroduces the order-dependent churn + this ADR removes. + +7. **Name grammar and boundary validation.** Because `download_data()`, + `download_tutorial()`, and the CLI accept these names from public + users, the name is validated at that boundary _before_ any catalog + lookup, path, or URL is built: + - A **slug** matches `[a-z0-9]+(-[a-z0-9]+)*` — lowercase ASCII + letters and digits in dash-separated groups, with no leading, + trailing, or doubled dashes, no slash, and no other characters. + - A **tutorial name** is one slug. + - A **dataset name** is one slug whose first dash-separated segment + is one of the four fixed category prefixes (`struct`, `expt`, + `meas`, `proj`) — a closed set, so it is an enum per `AGENTS.md` — + followed by at least one more segment (i.e. `-`). + - A name never contains a file extension, an empty segment, a `.` or + `..` segment, or any slash or other path/URL separator. + - A value that violates this grammar raises a clear validation error + naming the offending input. A well-formed name that is simply + absent from the index is a distinct "unknown resource" error, not a + validation error. + +## Consequences + +- Resource references become self-documenting and stable across + insertion, removal, and replacement; the `replaces` churn disappears. +- One consistent identifier scheme spans datasets and tutorials and the + `download_*`/`list_*` API. +- The category prefix names the kind and matches its on-disk folder, so + the kind is visible in the name without a separate column. +- A one-time migration is required: rename every dataset id and file, + rewrite `index.json`, rename the tutorial sources and their generated + notebooks, update the MkDocs nav and the `download_*`/`list_*` calls + in all tutorials and docs, and move order into metadata. The full old + → new name tables are recorded in the §Name map below; only the + step-by-step migration mechanics belong in the implementation plan. +- The optional positional-integer shorthand is a convenience only; + because it is order-dependent, it must never appear in saved code, or + it reintroduces exactly the churn this ADR removes. + +## Alternatives Considered + +- **Keep integer ids.** Rejected: opaque, and they churn on + replace/insert/remove as shown above. +- **Stable, never-reused integer ids.** Rejected: a permanent number is + just the current `ed-N` scheme, which still renumbers nothing but also + conveys nothing and already accumulated `replaces` chains. +- **Version/`data-vN` tags or versioned directories.** Out of scope here + and rejected in `data-source-pinning` (multi-purpose repo; prefer + replace-in-place). +- **Numeric ordering for tutorials.** Rejected: tutorials are authored + in feature-implementation order, not learning order, so a numeric id + is not a meaningful or stable sequence; order belongs in nav metadata. +- **Slug-only with no positional shortcut.** Viable and strictest; the + positional `#` is offered as an interactive convenience under the + guardrail in Decision 6. + +## Name map + +This is the authoritative old → new **name** map for every resource that +exists today. The step-by-step migration _mechanics_ (rewriting +`index.json`, moving files, regenerating notebooks, wiring nav order, +and updating the `download_*`/`list_*` calls) remain a plan concern; the +names themselves are fixed here. + +### Datasets + +The superseded integer ids from the `replaces` chains +(`28, 30, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43`) disappear — each +chain collapses to one stable slug overwritten in place. + +**`struct-` — crystal-structure import CIFs** + +| Old id / file | New id | +| ----------------------------- | ---------------- | +| 1 `ed-1.cif` (La0.5Ba0.5CoO3) | `struct-lbco` | +| 20 `ed-20.cif` (Tb2Ti2O7) | `struct-tbti` | +| 21 `ed-21.cif` (Taurine) | `struct-taurine` | + +**`expt-` — EasyDiffraction experiment-definition files** + +| Old id / file | New id | +| ----------------------------------------------- | ---------------- | +| 2 `ed-2.cif` (LBCO HRPT, experiment definition) | `expt-lbco-hrpt` | + +> Id 2 is a full experiment-definition file: its id is the +> extension-free `expt-lbco-hrpt` (per Decisions 1 and 5), and the +> stored file migrates from `.cif` to the new `.edi` format, so its path +> is `data/experiments/lbco-hrpt.edi`. Id 3, which previously shared the +> "LBCO HRPT" description, is the _raw measured pattern_ and moves to +> `meas-` below. + +**`meas-` — raw measured or simulated data** + +| Old id / file | New id | +| ---------------------------------------------------------- | ------------------------------ | +| 3 `ed-3.xye` (LBCO HRPT, 300 K pattern) | `meas-lbco-hrpt` | +| 4 `ed-4.gr` (NaCl) | `meas-nacl-pdf` | +| 5 `ed-5.gr` (Si, NOMAD) | `meas-si-pdf-nomad` | +| 6 `ed-6.gr` (Ni) | `meas-ni-pdf` | +| 7 `ed-7.xye` (Si, SEPD) | `meas-si-sepd` | +| 8 `ed-8.xye` (LBCO+Si, McStas) | `meas-lbco-si-mcstas` | +| 9 `ed-9.xys` (NCAF, WISH banks 5&6) | `meas-ncaf-wish-b56` | +| 10 `ed-10.xys` (NCAF, WISH banks 4&7) | `meas-ncaf-wish-b47` | +| 11 `ed-11.xye` (HS, HRPT) | `meas-hs-hrpt` | +| 12 `ed-12.xye` (Co2SiO4, D20) | `meas-cosio-d20` | +| 13 `ed-13.dat` (PbSO4, D1A) | `meas-pbso4-d1a` | +| 14 `ed-14.dat` (PbSO4, D1A 1st half) | `meas-pbso4-d1a-part1` | +| 15 `ed-15.dat` (PbSO4, D1A 2nd half) | `meas-pbso4-d1a-part2` | +| 16 `ed-16.dat` (PbSO4, lab X-ray) | `meas-pbso4-xray` | +| 17 `ed-17.xye` (Si, McStas DMSC2025) | `meas-si-mcstas-dmsc2025` | +| 18 `ed-18.xye` (LBCO+Si, McStas DMSC2025) | `meas-lbco-si-mcstas-dmsc2025` | +| 19 `ed-19.xye` (Tb2Ti2O7, HEiDi) | `meas-tbti-heidi` | +| 22 `ed-22.xye` (Taurine, SENJU) | `meas-taurine-senju` | +| 23 `ed-23.zip` (Co2SiO4 D20 T-scan, 20 files) | `meas-cosio-d20-scan-20f` | +| 24 `ed-24.zip` (... 156 files) | `meas-cosio-d20-scan-156f` | +| 25 `ed-25.zip` (... 3 files) | `meas-cosio-d20-scan-3f` | +| 26 `ed-26.zip` (... 46 files) | `meas-cosio-d20-scan-46f` | +| 27 `ed-27.zip` (... 23 files) | `meas-cosio-d20-scan-23f` | +| 29 `ed-29.zip` (... 213 files) | `meas-cosio-d20-scan-213f` | +| 31 `ed-31.dat` (La 7-cation perovskite, synchrotron X-ray) | `meas-hep7c-xray-synchrotron` | +| 32 `ed-32.dat` (La 7-cation perovskite, lab Cu Ka) | `meas-hep7c-xray-cuka` | +| 33 `ed-33.zip` (ferrite+austenite, BEER) | `meas-ferrite-austenite-beer` | + +> Ids 31 and 32 previously shared the description "LaM7O3, X-ray". Their +> `.pcr` sources show both are the same La/7-cation high-entropy +> perovskite (GdFeO3-type, Pnma) measured on different instruments: 31 +> is synchrotron (lambda = 0.2071 A), 32 is lab Cu Ka1 (Bruker D8). The +> sample's own `hep7c` code disambiguates them, and their index +> descriptions are made distinct to match. + +**`proj-` — saved EasyDiffraction project archives (chains collapse)** + +| Old ids / files | New id | +| ------------------------------------- | ---------------------- | +| 28, 30, 36, 40, 44 (LBCO HRPT, 300 K) | `proj-lbco-hrpt` | +| 34, 37, 41, 45 (Co2SiO4 D20 T-scan) | `proj-cosio-d20-scan` | +| 35, 38, 42, 46 (emcee, LBCO HRPT) | `proj-lbco-hrpt-emcee` | +| 39, 43, 47 (bumps-dream, LBCO HRPT) | `proj-lbco-hrpt-dream` | + +### Tutorials + +Presentation order moves to the MkDocs nav (Decision 4), so these slugs +carry no sequence and can be inserted, removed, or reordered freely. +There is no `ed-19` tutorial — id 19 is a dataset only. + +| Old id | Title | New id | +| ------ | -------------------------------------------- | ---------------------------------- | +| ed-1 | Structure Refinement: LBCO, HRPT (from CIF) | `refine-lbco-hrpt-from-cif` | +| ed-2 | Structure Refinement: LBCO, HRPT (from data) | `refine-lbco-hrpt-from-data` | +| ed-3 | Structure Refinement: LBCO, HRPT (report) | `refine-lbco-hrpt-report` | +| ed-4 | Refinement: PbSO4, NPD+XRD | `refine-pbso4-joint` | +| ed-5 | Refinement: Co2SiO4, D20 | `refine-cosio-d20` | +| ed-6 | Refinement: HS, HRPT | `refine-hs-hrpt` | +| ed-7 | Refinement: Si, SEPD | `refine-si-sepd` | +| ed-8 | Refinement: NCAF, WISH | `refine-ncaf-wish` | +| ed-9 | Refinement: LBCO+Si, McStas | `refine-lbco-si-mcstas` | +| ed-10 | PDF: Ni, NPD | `pdf-ni-npd` | +| ed-11 | PDF: Si, NOMAD (SNS) | `pdf-si-nomad` | +| ed-12 | PDF: NaCl, XRD | `pdf-nacl-xrd` | +| ed-13 | Fitting exercise: Si, LBCO | `fitting-exercise-si-lbco` | +| ed-14 | Refinement: Tb2Ti2O7, HEiDi | `refine-tbti-heidi` | +| ed-15 | Refinement: Taurine, SENJU | `refine-taurine-senju` | +| ed-16 | Joint: Si, Bragg+PDF | `joint-si-bragg-pdf` | +| ed-17 | Refinement: Co2SiO4, D20 (T-scan) | `refine-cosio-d20-tscan` | +| ed-18 | Load Project and Fit: LBCO, HRPT | `load-and-fit-lbco-hrpt` | +| ed-20 | Instrument calibration: BEER, ESS | `calibrate-beer-ess` | +| ed-21 | Bayesian (bumps-dream): LBCO, HRPT | `bayesian-dream-lbco-hrpt` | +| ed-22 | Bayesian (emcee): Tb2Ti2O7, HEiDi | `bayesian-emcee-tbti-heidi` | +| ed-23 | Refinement: Co2SiO4 D20 (T-scan, resumed) | `refine-cosio-d20-tscan-resumed` | +| ed-24 | Bayesian Display (bumps-dream): LBCO, HRPT | `bayesian-dream-display-lbco-hrpt` | +| ed-25 | Bayesian (emcee): LBCO, HRPT | `bayesian-emcee-lbco-hrpt` | +| ed-26 | Bayesian Resume (emcee): LBCO, HRPT | `bayesian-emcee-resume-lbco-hrpt` | +| ed-27 | Calculation Without Data: LBCO, CWL | `simulate-lbco-cwl` | +| ed-28 | Calculation Without Data: Si, TOF | `simulate-si-tof` | +| ed-29 | Calculation Without Data: NaCl, X-ray | `simulate-nacl-xray` | + +## Deferred Work + +- The step-by-step migration _mechanics_ — rewriting `index.json`, + moving/renaming files, regenerating notebooks, wiring nav order, and + updating the `download_*`/`list_*` calls in tutorials and docs — are + an implementation concern for the plan, not this ADR. The old → new + **name** map itself is fixed above in §Name map. +- Whether to implement the positional-integer shorthand at all, or ship + slug-only first, is left to the plan. diff --git a/docs/dev/adrs/accepted/switchable-category-owned-selectors.md b/docs/dev/adrs/accepted/switchable-category-owned-selectors.md index 2d3ca926a..ee12e0ac0 100644 --- a/docs/dev/adrs/accepted/switchable-category-owned-selectors.md +++ b/docs/dev/adrs/accepted/switchable-category-owned-selectors.md @@ -56,7 +56,8 @@ Three problems have accumulated since that ADR landed: notes the inconsistency under §"Owner-level switchable selectors" and tags it for a future ADR. -3. **Cross-cutting inconsistency.** Issue [#76](../../issues/open.md) +3. **Cross-cutting inconsistency.** Issue + [#76](../../issues/closed/consistent-type-suffix-in-switchable-category-api-names.md) ("Consistent `_type` suffix in switchable-category API names") tracked the inconsistency in the _method names_ on the owner, but assumed the owner-level model stayed. @@ -860,10 +861,10 @@ full grep results.) ### Issues that this ADR closes -- [#72 "Warn on All Switchable-Category Type Changes"](../../issues/open.md) +- [#72 "Warn on All Switchable-Category Type Changes"](../../issues/closed/warn-on-all-switchable-category-type-changes.md) — the warning logic moves into each owner's `_swap_` method; uniform by construction. -- [#76 "Consistent `_type` suffix in switchable-category API names"](../../issues/open.md) +- [#76 "Consistent `_type` suffix in switchable-category API names"](../../issues/closed/consistent-type-suffix-in-switchable-category-api-names.md) — superseded; the new convention drops the suffix entirely. ## Alternatives Considered diff --git a/docs/dev/adrs/accepted/undo-fit.md b/docs/dev/adrs/accepted/undo-fit.md index 78816d0d8..c58e8d588 100644 --- a/docs/dev/adrs/accepted/undo-fit.md +++ b/docs/dev/adrs/accepted/undo-fit.md @@ -57,12 +57,12 @@ After `undo_fit()`: - `_fit_parameter_correlations` is cleared — purely fit-derived - `_fit_parameter` rows are **preserved**. The collection carries both user-owned fit controls (`fit_min`, `fit_max`, - `fit_bounds_uncertainty_multiplier`) and the rollback anchors - themselves (`start_value`, `start_uncertainty`). Clearing the whole - collection — which is what `Analysis._clear_persisted_fit_state()` - does at the start of a new fit — would silently drop the user's bounds - and erase the anchors needed for idempotence (§6). Undo therefore - leaves these rows in place; the next fit rewrites them via + `bounds_uncertainty_multiplier`) and the rollback anchors themselves + (`start_value`, `start_uncertainty`). Clearing the whole collection — + which is what `Analysis._clear_persisted_fit_state()` does at the + start of a new fit — would silently drop the user's bounds and erase + the anchors needed for idempotence (§6). Undo therefore leaves these + rows in place; the next fit rewrites them via `_capture_fit_parameter_state()`. - `analysis/results.h5` is cleared in memory only: the `Analysis._persisted_fit_state_sidecar` dict is reset to empty. All @@ -225,9 +225,9 @@ always either a rollback or a no-op, never an error. ### Python API ```python -import easydiffraction as ed +import easydiffraction as edi -project = ed.Project.load('projects/lbco_hrpt') +project = edi.Project.load('projects/lbco_hrpt') # After a fit has been committed, the project carries refined state: project.analysis.fit_results # FitResults(success=True, ...) diff --git a/docs/dev/adrs/accepted/value-selector-discovery.md b/docs/dev/adrs/accepted/value-selector-discovery.md index 93f982e52..86651c2f4 100644 --- a/docs/dev/adrs/accepted/value-selector-discovery.md +++ b/docs/dev/adrs/accepted/value-selector-discovery.md @@ -66,10 +66,10 @@ against **dynamic or external** sets rather than a project-owned closed enum: `atom_sites.type_symbol` (CrySPY isotope symbols from `DATABASE['Isotopes']`), `atom_sites.wyckoff_letter` (space-group dependent), `space_group.name_h_m` (CrySPY H-M symbols), and -`space_group.it_coordinate_system_code` (derived from the current H-M -symbol). These are boundary-facing CIF/science values, not `(str, Enum)` -closed sets with a static `.default()`/`.description()`; they are out of -scope here (see Decision and Deferred Work). +`space_group.coord_system_code` (derived from the current H-M symbol). +These are boundary-facing CIF/science values, not `(str, Enum)` closed +sets with a static `.default()`/`.description()`; they are out of scope +here (see Decision and Deferred Work). ## Decision @@ -127,13 +127,12 @@ a discovery surface symmetric with the three category-level families. they keep their category-level `show_supported()`. - **Dynamic / external / context-dependent** membership validators — `atom_sites.type_symbol`, `atom_sites.wyckoff_letter`, - `space_group.name_h_m`, `space_group.it_coordinate_system_code`, - and any field whose allowed values come from a database, another - field, or runtime context. These keep their existing - `MembershipValidator` and current validation behavior; they do - **not** become `EnumDescriptor`s and do **not** gain - `show_supported()` under this ADR. A separate dynamic-choice - discovery surface is deferred. + `space_group.name_h_m`, `space_group.coord_system_code`, and any + field whose allowed values come from a database, another field, or + runtime context. These keep their existing `MembershipValidator` + and current validation behavior; they do **not** become + `EnumDescriptor`s and do **not** gain `show_supported()` under this + ADR. A separate dynamic-choice discovery surface is deferred. An implementation audit classifies each `MembershipValidator` field as value selector, category-level selector, or dynamic/external @@ -213,9 +212,9 @@ a discovery surface symmetric with the three category-level families. `show_supported()`-style listing) to fields whose allowed set is dynamic, external, or context-dependent — `atom_sites.type_symbol`, `atom_sites.wyckoff_letter`, `space_group.name_h_m`, - `space_group.it_coordinate_system_code`, and similar. Out of scope - here; these keep their current `MembershipValidator` until such a - descriptor exists. + `space_group.coord_system_code`, and similar. Out of scope here; these + keep their current `MembershipValidator` until such a descriptor + exists. - An optional, clearly-named grouped overview for tightly-related axis bundles (notably `experiment.experiment_type`'s four axes shown together). Not part of the initial rollout. diff --git a/docs/dev/adrs/accepted/wyckoff-letter-detection.md b/docs/dev/adrs/accepted/wyckoff-letter-detection.md index c8b4340bb..b921c76e4 100644 --- a/docs/dev/adrs/accepted/wyckoff-letter-detection.md +++ b/docs/dev/adrs/accepted/wyckoff-letter-detection.md @@ -44,8 +44,9 @@ on that letter: - The IUCr writer emits `_atom_site.Wyckoff_symbol` ([`iucr_writer.py:876`](../../../../src/easydiffraction/io/cif/iucr_writer.py)). -Two gaps remain, both recorded as open issue **#51** -([`open.md:999`](../../../../docs/dev/issues/open.md)): +Two gaps remain, both recorded under issue **#51** (now closed — verify +these gaps before relying on this paragraph) +([`#51`](../../../../docs/dev/issues/closed/access-space-group-from-atomsites-for-wyckoff-letters.md)): 1. The set of letters a site may take is a hardcoded placeholder, `['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']`, with a TODO to read @@ -160,11 +161,11 @@ fallback option if numeric edge cases appear — see _Alternatives_. **Key normalisation.** `SpaceGroup` represents a group with no coordinate-system code as the empty string `''` (its -`_it_coordinate_system_code_allowed_values` returns `codes or ['']`), -but the bundled table stores those groups under a `None` code — the -triclinic groups are `(1, None)` (P1, Wyckoff `a`, multiplicity 1) and -`(2, None)` (P-1), and there are no empty-string keys at all. Detection -and allowed-letter discovery therefore normalise `''` to `None` before +`_coord_system_code_allowed_values` returns `codes or ['']`), but the +bundled table stores those groups under a `None` code — the triclinic +groups are `(1, None)` (P1, Wyckoff `a`, multiplicity 1) and `(2, None)` +(P-1), and there are no empty-string keys at all. Detection and +allowed-letter discovery therefore normalise `''` to `None` before indexing `SPACE_GROUPS`, so P1 and P-1 resolve to their real Wyckoff positions instead of being mistaken for unsupported groups. A shared `_normalize_coord_code()` helper owns this mapping; the existing @@ -200,8 +201,8 @@ group the letter changes in three ways: edit is covered. - **User edits the space group or setting.** A change to `structure.space_group.name_h_m` or - `structure.space_group.it_coordinate_system_code` invalidates every - atom site's Wyckoff record even when coordinates are unchanged. For a + `structure.space_group.coord_system_code` invalidates every atom + site's Wyckoff record even when coordinates are unchanged. For a supported new key, all sites re-detect from their current coordinates and refresh letter, multiplicity, and selected representative. For an unsupported new key, auto-detection is a no-op: existing letters are diff --git a/docs/dev/adrs/index.md b/docs/dev/adrs/index.md index f409a19cf..33f1f9d76 100644 --- a/docs/dev/adrs/index.md +++ b/docs/dev/adrs/index.md @@ -13,53 +13,56 @@ folders. ## ADR Index -| Group | Status | Title | Short description | Link | -| -------------------- | ---------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| Analysis and fitting | Accepted | Fit Mode Categories and Fit Execution API | Splits fitting configuration from execution and defines active sibling fit-mode categories. | [`fit-mode-categories.md`](accepted/fit-mode-categories.md) | -| Analysis and fitting | Accepted | Runtime Fit Results | Keeps full fit outputs runtime-only in the current design unless a narrower persistence ADR is accepted. | [`runtime-fit-results.md`](accepted/runtime-fit-results.md) | -| Analysis and fitting | Accepted | Analysis CIF Fit State | Defines the persisted fit-state projection in `analysis/analysis.cif` and `analysis/results.h5`. | [`analysis-cif-fit-state.md`](accepted/analysis-cif-fit-state.md) | -| Analysis and fitting | Accepted | Parameter Correlation Persistence | Persists deterministic and posterior correlation summaries in `_fit_parameter_correlation` | [`parameter-correlation-persistence.md`](accepted/parameter-correlation-persistence.md) | -| Analysis and fitting | Suggestion | Fit Output Files and Data Exports | Narrows remaining archive/export questions after adopting `results.csv` and `results.h5`. | [`fit-output-files-and-data-exports.md`](suggestions/fit-output-files-and-data-exports.md) | -| Analysis and fitting | Accepted | Minimizer Category Consolidation | Collapses the seven Bayesian categories into one owner-level switchable `minimizer` category with HDF5 sidecar. | [`minimizer-category-consolidation.md`](accepted/minimizer-category-consolidation.md) | -| Analysis and fitting | Accepted | Minimizer Input/Output Split | Keeps `analysis.minimizer` input-only and moves scalar fit outputs to paired `analysis.fit_result` classes. | [`minimizer-input-output-split.md`](accepted/minimizer-input-output-split.md) | -| Analysis and fitting | Superseded | Parameter-Level Posterior Projection | Superseded by minimizer-category consolidation; kept as historical context for `parameter.posterior`. | [`parameter-posterior-summary.md`](suggestions/parameter-posterior-summary.md) | -| Analysis and fitting | Accepted | Undo Fit | Builds rollback semantics and CLI behavior on already-persisted pre-fit scalar snapshots. | [`undo-fit.md`](accepted/undo-fit.md) | -| Core model | Accepted | Category Owners and Real Datablocks | Introduces `CategoryOwner` so singleton sections do not pretend to be real CIF datablocks. | [`category-owner-sections.md`](accepted/category-owner-sections.md) | -| Core model | Accepted | Enum-Backed Closed Value Sets | Requires finite option sets to use `(str, Enum)` classes for validation and dispatch. | [`enum-backed-closed-values.md`](accepted/enum-backed-closed-values.md) | -| Core model | Accepted | Guarded Public Properties | Uses property setters as the public writability contract for guarded objects. | [`guarded-public-properties.md`](accepted/guarded-public-properties.md) | -| Core model | Accepted | Two-Level Category Parameter Access | Keeps parameter access to `datablock.category.parameter` or `datablock.collection[id].parameter`. | [`category-parameter-access.md`](accepted/category-parameter-access.md) | -| Documentation | Accepted | Descriptor Property Docstring Template | Makes descriptor metadata the source of truth for public property docstrings and annotations. | [`property-docstring-template.md`](accepted/property-docstring-template.md) | -| Documentation | Accepted | Development Documentation Structure | Defines the `docs/dev` layout for ADRs, issues, plans, package structure, and roadmap. | [`development-docs-structure.md`](accepted/development-docs-structure.md) | -| Documentation | Accepted | Help Method Discoverability | Requires primary public objects and facades to expose consistent `help()` output. | [`help-discoverability.md`](accepted/help-discoverability.md) | -| Documentation | Accepted | Notebook Generation Source of Truth | Treats tutorial `.py` files as editable sources and notebooks as generated artifacts. | [`notebook-generation.md`](accepted/notebook-generation.md) | -| Documentation | Accepted | Plotting & Docs Performance for Interactive Figures | Self-hosts a lazy, shared figure runtime so docs pages load fast and progressively while staying interactive. | [`plotting-docs-performance.md`](accepted/plotting-docs-performance.md) | -| Documentation | Accepted | Documentation CI and Build Verification | Strict MkDocs builds, API-derived docs, snippet smoke tests, link checks, and prose/spelling checks. | [`documentation-ci-build.md`](accepted/documentation-ci-build.md) | -| Experiment model | Accepted | Immutable Experiment Type | Makes experiment type axes creation-time state rather than mutable runtime state. | [`immutable-experiment-type.md`](accepted/immutable-experiment-type.md) | -| Experiment model | Accepted | Automatic Line-Segment Background Estimation | Detects line-segment background control points from the measured pattern, peak-insensitive and editable. | [`background-auto-estimate.md`](accepted/background-auto-estimate.md) | -| Experiment model | Accepted | Calculation Without Measured Data | Adds a writable `data_range` category so a structure-only experiment is calculable and plottable without loaded data. | [`calculation-without-measured-data.md`](accepted/calculation-without-measured-data.md) | -| Experiment model | Accepted | Preferred-Orientation Category | Adds a per-phase March–Dollase preferred-orientation category for textured powder refinement on the CrysPy backend. | [`preferred-orientation-category.md`](accepted/preferred-orientation-category.md) | -| Experiment model | Accepted | Model Sample Absorption (Debye–Scherrer, μR) | Switchable `absorption` category applying a calculator-independent cylindrical Hewat A(θ) envelope for powder samples. | [`model-sample-absorption.md`](accepted/model-sample-absorption.md) | -| Factories | Accepted | Factory Contracts and Metadata | Standardizes factory construction, metadata, compatibility, and registration behavior. | [`factory-contracts.md`](accepted/factory-contracts.md) | -| Naming | Accepted | Factory Tag Naming | Defines canonical factory tag style and standard abbreviations. | [`factory-tag-naming.md`](accepted/factory-tag-naming.md) | -| Persistence | Accepted | Free-Flag CIF Encoding | Encodes fit free/fixed state through CIF uncertainty syntax instead of a separate free list. | [`free-flag-cif-encoding.md`](accepted/free-flag-cif-encoding.md) | -| Persistence | Accepted | Loop Category Keys and Identity Naming | Documents loop collection keys and naming rules aligned with CIF category keys. | [`loop-category-key-identity.md`](accepted/loop-category-key-identity.md) | -| Persistence | Accepted | Project Facade and Persistence Layout | Documents the current `Project` facade and saved directory layout. | [`project-facade-and-persistence.md`](accepted/project-facade-and-persistence.md) | -| Persistence | Accepted | IUCr CIF Tag Alignment | Aligns default CIF tags with IUCr dictionaries and adds a clean IUCr-aligned report export. | [`iucr-cif-tag-alignment.md`](accepted/iucr-cif-tag-alignment.md) | -| Persistence | Accepted | Python and CIF Category Correspondence | Compares current Python paths and CIF tags, then records scoped one-to-one mapping for project-level categories. | [`python-cif-category-correspondence.md`](accepted/python-cif-category-correspondence.md) | -| Quality | Accepted | Lint Complexity Thresholds | Treats ruff PLR complexity limits as design guardrails that should not be bypassed. | [`lint-complexity-thresholds.md`](accepted/lint-complexity-thresholds.md) | -| Quality | Accepted | Lint Rule Scope and Test-File Exceptions | Records the standing tests/\*\* PLR/N812 ignores and CIF-aligned `id`/`type` builtin exception from the lint audit. | [`lint-rule-exceptions.md`](accepted/lint-rule-exceptions.md) | -| Quality | Accepted | Test Strategy | Defines layered unit, functional, integration, script, and notebook testing. | [`test-strategy.md`](accepted/test-strategy.md) | -| Quality | Accepted | Test Suite and Validation Strategy | Strict test layers, cost tiers, coverage/codecov policy, cross-engine verification docs, and a nightly validation harness. | [`test-suite-and-validation.md`](accepted/test-suite-and-validation.md) | -| Structure model | Accepted | Type-Neutral ADP Parameters | Keeps ADP parameter object identities stable across B/U and iso/ani switches. | [`type-neutral-adp-parameters.md`](accepted/type-neutral-adp-parameters.md) | -| Structure model | Accepted | Automatic Wyckoff Position Detection | Detects Wyckoff letter, multiplicity, and site symmetry from space group and coordinates; calculators consume them. | [`wyckoff-letter-detection.md`](accepted/wyckoff-letter-detection.md) | -| Structure model | Accepted | Complete Space-Group Reference Database | One-time build of a complete space_groups.json.gz (all 230 groups) from cctbx, verified against multiple sources. | [`space-group-database.md`](accepted/space-group-database.md) | -| User-facing API | Accepted | Crystal Structure 3D Visualization | Adds a renderer-neutral scene model drawn by ASCII and interactive Three.js engines for viewing crystal structures. | [`crysview-structure-visualization.md`](accepted/crysview-structure-visualization.md) | -| User-facing API | Accepted | Display UX Facade | Defines `project.display` and `project.rendering` responsibilities and display method names. | [`display-ux.md`](accepted/display-ux.md) | -| User-facing API | Accepted | Fit Results Display Naming | Short, IUCr/GUM-aligned column headers (`s.u.`, `value`, `95% CI`) with a footnote glossary on every fit table. | [`fit-results-display-naming.md`](accepted/fit-results-display-naming.md) | -| User-facing API | Accepted | Project Summary Rendering | Defines project report configuration plus terminal, HTML, TeX, PDF, and clean report-CIF metadata policy. | [`project-summary-rendering.md`](accepted/project-summary-rendering.md) | -| User-facing API | Accepted | Selector Families | Distinguishes backend selectors, switchable-category selectors, and active-sibling selectors. | [`selector-families.md`](accepted/selector-families.md) | -| User-facing API | Accepted | String Paths and Live Descriptors | Separates persisted field selectors from references to live model parameters. | [`string-paths-and-live-descriptors.md`](accepted/string-paths-and-live-descriptors.md) | -| User-facing API | Accepted | Switchable Category API | Places multi-type category selectors on the owner and omits public selectors for fixed or single-type categories. | [`switchable-category-api.md`](accepted/switchable-category-api.md) | -| User-facing API | Accepted | Switchable Category Owned Selectors | Moves the writable `type` selector and `show_supported()` onto the category itself; collapses the CIF duplication. | [`switchable-category-owned-selectors.md`](accepted/switchable-category-owned-selectors.md) | -| User-facing API | Accepted | Unified Pattern View | `pattern()` always renders available data, drops `include`, and unifies single- and three-panel figure sizing. | [`pattern-display-unification.md`](accepted/pattern-display-unification.md) | -| User-facing API | Accepted | Value-Selector Discovery | Gives enumerated value fields a per-descriptor `show_supported()`, beside the three category-level selector families. | [`value-selector-discovery.md`](accepted/value-selector-discovery.md) | +| Group | Status | Title | Short description | Link | +| -------------------- | ---------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | +| Analysis and fitting | Accepted | Fit Mode Categories and Fit Execution API | Splits fitting configuration from execution and defines active sibling fit-mode categories. | [`fit-mode-categories.md`](accepted/fit-mode-categories.md) | +| Analysis and fitting | Accepted | Runtime Fit Results | Keeps full fit outputs runtime-only in the current design unless a narrower persistence ADR is accepted. | [`runtime-fit-results.md`](accepted/runtime-fit-results.md) | +| Analysis and fitting | Accepted | Analysis CIF Fit State | Defines the persisted fit-state projection in `analysis/analysis.cif` and `analysis/results.h5`. | [`analysis-cif-fit-state.md`](accepted/analysis-cif-fit-state.md) | +| Analysis and fitting | Accepted | Parameter Correlation Persistence | Persists deterministic and posterior correlation summaries in `_fit_parameter_correlation` | [`parameter-correlation-persistence.md`](accepted/parameter-correlation-persistence.md) | +| Analysis and fitting | Suggestion | Fit Output Files and Data Exports | Narrows remaining archive/export questions after adopting `results.csv` and `results.h5`. | [`fit-output-files-and-data-exports.md`](suggestions/fit-output-files-and-data-exports.md) | +| Analysis and fitting | Accepted | Minimizer Category Consolidation | Collapses the seven Bayesian categories into one owner-level switchable `minimizer` category with HDF5 sidecar. | [`minimizer-category-consolidation.md`](accepted/minimizer-category-consolidation.md) | +| Analysis and fitting | Accepted | Minimizer Input/Output Split | Keeps `analysis.minimizer` input-only and moves scalar fit outputs to paired `analysis.fit_result` classes. | [`minimizer-input-output-split.md`](accepted/minimizer-input-output-split.md) | +| Analysis and fitting | Superseded | Parameter-Level Posterior Projection | Superseded by minimizer-category consolidation; kept as historical context for `parameter.posterior`. | [`parameter-posterior-summary.md`](suggestions/parameter-posterior-summary.md) | +| Analysis and fitting | Accepted | Undo Fit | Builds rollback semantics and CLI behavior on already-persisted pre-fit scalar snapshots. | [`undo-fit.md`](accepted/undo-fit.md) | +| Core model | Accepted | Category Owners and Real Datablocks | Introduces `CategoryOwner` so singleton sections do not pretend to be real CIF datablocks. | [`category-owner-sections.md`](accepted/category-owner-sections.md) | +| Core model | Accepted | Enum-Backed Closed Value Sets | Requires finite option sets to use `(str, Enum)` classes for validation and dispatch. | [`enum-backed-closed-values.md`](accepted/enum-backed-closed-values.md) | +| Core model | Accepted | Guarded Public Properties | Uses property setters as the public writability contract for guarded objects. | [`guarded-public-properties.md`](accepted/guarded-public-properties.md) | +| Core model | Accepted | Two-Level Category Parameter Access | Keeps parameter access to `datablock.category.parameter` or `datablock.collection[id].parameter`. | [`category-parameter-access.md`](accepted/category-parameter-access.md) | +| Documentation | Accepted | Descriptor Property Docstring Template | Makes descriptor metadata the source of truth for public property docstrings and annotations. | [`property-docstring-template.md`](accepted/property-docstring-template.md) | +| Documentation | Accepted | Development Documentation Structure | Defines the `docs/dev` layout for ADRs, issues, plans, package structure, and roadmap. | [`development-docs-structure.md`](accepted/development-docs-structure.md) | +| Documentation | Accepted | Help Method Discoverability | Requires primary public objects and facades to expose consistent `help()` output. | [`help-discoverability.md`](accepted/help-discoverability.md) | +| Documentation | Accepted | Notebook Generation Source of Truth | Treats tutorial `.py` files as editable sources and notebooks as generated artifacts. | [`notebook-generation.md`](accepted/notebook-generation.md) | +| Documentation | Accepted | Plotting & Docs Performance for Interactive Figures | Self-hosts a lazy, shared figure runtime so docs pages load fast and progressively while staying interactive. | [`plotting-docs-performance.md`](accepted/plotting-docs-performance.md) | +| Documentation | Accepted | Documentation CI and Build Verification | Strict MkDocs builds, API-derived docs, snippet smoke tests, link checks, and prose/spelling checks. | [`documentation-ci-build.md`](accepted/documentation-ci-build.md) | +| Documentation | Accepted | Data Download Source Pinning | Pins downloadable data to one git commit kept in a packaged file, drops the redundant index checksum, cache-busting by commit-named index. | [`data-source-pinning.md`](accepted/data-source-pinning.md) | +| Experiment model | Accepted | Immutable Experiment Type | Makes experiment type axes creation-time state rather than mutable runtime state. | [`immutable-experiment-type.md`](accepted/immutable-experiment-type.md) | +| Experiment model | Accepted | Automatic Line-Segment Background Estimation | Detects line-segment background control points from the measured pattern, peak-insensitive and editable. | [`background-auto-estimate.md`](accepted/background-auto-estimate.md) | +| Experiment model | Accepted | Calculation Without Measured Data | Adds a writable `data_range` category so a structure-only experiment is calculable and plottable without loaded data. | [`calculation-without-measured-data.md`](accepted/calculation-without-measured-data.md) | +| Experiment model | Accepted | Preferred-Orientation Category | Adds a per-phase March–Dollase preferred-orientation category for textured powder refinement on the CrysPy backend. | [`preferred-orientation-category.md`](accepted/preferred-orientation-category.md) | +| Experiment model | Accepted | Model Sample Absorption (Debye–Scherrer, μR) | Switchable `absorption` category applying a calculator-independent cylindrical Hewat A(θ) envelope for powder samples. | [`model-sample-absorption.md`](accepted/model-sample-absorption.md) | +| Factories | Accepted | Factory Contracts and Metadata | Standardizes factory construction, metadata, compatibility, and registration behavior. | [`factory-contracts.md`](accepted/factory-contracts.md) | +| Naming | Accepted | Factory Tag Naming | Defines canonical factory tag style and standard abbreviations. | [`factory-tag-naming.md`](accepted/factory-tag-naming.md) | +| Naming | Accepted | Downloadable Resource Naming | Replaces integer dataset/tutorial ids with stable descriptive slugs and moves presentation order into separate metadata. | [`resource-naming.md`](accepted/resource-naming.md) | +| Persistence | Accepted | Free-Flag CIF Encoding | Encodes fit free/fixed state through CIF uncertainty syntax instead of a separate free list. | [`free-flag-cif-encoding.md`](accepted/free-flag-cif-encoding.md) | +| Persistence | Accepted | Loop Category Keys and Identity Naming | Documents loop collection keys and naming rules aligned with CIF category keys. | [`loop-category-key-identity.md`](accepted/loop-category-key-identity.md) | +| Persistence | Accepted | Project Facade and Persistence Layout | Documents the current `Project` facade and saved directory layout. | [`project-facade-and-persistence.md`](accepted/project-facade-and-persistence.md) | +| Persistence | Accepted | IUCr CIF Tag Alignment | Aligns default CIF tags with IUCr dictionaries and adds a clean IUCr-aligned report export. | [`iucr-cif-tag-alignment.md`](accepted/iucr-cif-tag-alignment.md) | +| Persistence | Accepted | Python and CIF Category Correspondence | Compares current Python paths and CIF tags, then records scoped one-to-one mapping for project-level categories. | [`python-cif-category-correspondence.md`](accepted/python-cif-category-correspondence.md) | +| Persistence | Accepted | Edi Project Persistence | Defines STAR-based EasyDiffraction project files with UX-oriented names and strict CIF reserved for report export. | [`edstar-project-persistence.md`](accepted/edstar-project-persistence.md) | +| Quality | Accepted | Lint Complexity Thresholds | Treats ruff PLR complexity limits as design guardrails that should not be bypassed. | [`lint-complexity-thresholds.md`](accepted/lint-complexity-thresholds.md) | +| Quality | Accepted | Lint Rule Scope and Test-File Exceptions | Records the standing tests/\*\* PLR/N812 ignores and CIF-aligned `id`/`type` builtin exception from the lint audit. | [`lint-rule-exceptions.md`](accepted/lint-rule-exceptions.md) | +| Quality | Accepted | Test Strategy | Defines layered unit, functional, integration, script, and notebook testing. | [`test-strategy.md`](accepted/test-strategy.md) | +| Quality | Accepted | Test Suite and Validation Strategy | Strict test layers, cost tiers, coverage/codecov policy, cross-engine verification docs, and a nightly validation harness. | [`test-suite-and-validation.md`](accepted/test-suite-and-validation.md) | +| Structure model | Accepted | Type-Neutral ADP Parameters | Keeps ADP parameter object identities stable across B/U and iso/ani switches. | [`type-neutral-adp-parameters.md`](accepted/type-neutral-adp-parameters.md) | +| Structure model | Accepted | Automatic Wyckoff Position Detection | Detects Wyckoff letter, multiplicity, and site symmetry from space group and coordinates; calculators consume them. | [`wyckoff-letter-detection.md`](accepted/wyckoff-letter-detection.md) | +| Structure model | Accepted | Complete Space-Group Reference Database | One-time build of a complete space_groups.json.gz (all 230 groups) from cctbx, verified against multiple sources. | [`space-group-database.md`](accepted/space-group-database.md) | +| User-facing API | Accepted | Crystal Structure 3D Visualization | Adds a renderer-neutral scene model drawn by ASCII and interactive Three.js engines for viewing crystal structures. | [`crysview-structure-visualization.md`](accepted/crysview-structure-visualization.md) | +| User-facing API | Accepted | Display UX Facade | Defines `project.display` and `project.rendering` responsibilities and display method names. | [`display-ux.md`](accepted/display-ux.md) | +| User-facing API | Accepted | Fit Results Display Naming | Short, IUCr/GUM-aligned column headers (`s.u.`, `value`, `95% CI`) with a footnote glossary on every fit table. | [`fit-results-display-naming.md`](accepted/fit-results-display-naming.md) | +| User-facing API | Accepted | Project Summary Rendering | Defines project report configuration plus terminal, HTML, TeX, PDF, and clean report-CIF metadata policy. | [`project-summary-rendering.md`](accepted/project-summary-rendering.md) | +| User-facing API | Accepted | Selector Families | Distinguishes backend selectors, switchable-category selectors, and active-sibling selectors. | [`selector-families.md`](accepted/selector-families.md) | +| User-facing API | Accepted | String Paths and Live Descriptors | Separates persisted field selectors from references to live model parameters. | [`string-paths-and-live-descriptors.md`](accepted/string-paths-and-live-descriptors.md) | +| User-facing API | Accepted | Switchable Category API | Places multi-type category selectors on the owner and omits public selectors for fixed or single-type categories. | [`switchable-category-api.md`](accepted/switchable-category-api.md) | +| User-facing API | Accepted | Switchable Category Owned Selectors | Moves the writable `type` selector and `show_supported()` onto the category itself; collapses the CIF duplication. | [`switchable-category-owned-selectors.md`](accepted/switchable-category-owned-selectors.md) | +| User-facing API | Accepted | Unified Pattern View | `pattern()` always renders available data, drops `include`, and unifies single- and three-panel figure sizing. | [`pattern-display-unification.md`](accepted/pattern-display-unification.md) | +| User-facing API | Accepted | Value-Selector Discovery | Gives enumerated value fields a per-descriptor `show_supported()`, beside the three category-level selector families. | [`value-selector-discovery.md`](accepted/value-selector-discovery.md) | diff --git a/docs/dev/adrs/suggestions/parameter-posterior-summary.md b/docs/dev/adrs/suggestions/parameter-posterior-summary.md index 88146ec19..7e435f698 100644 --- a/docs/dev/adrs/suggestions/parameter-posterior-summary.md +++ b/docs/dev/adrs/suggestions/parameter-posterior-summary.md @@ -185,8 +185,8 @@ Manual user edits of `uncertainty` and `posterior` are not supported, because both are read-only fit outputs. Configuration attributes such as `free`, `fit_min`, `fit_max`, units, -and `fit_bounds_uncertainty_multiplier` are not cleared by this policy. -They are parameter configuration or user intent, not posterior output. +and `bounds_uncertainty_multiplier` are not cleared by this policy. They +are parameter configuration or user intent, not posterior output. ### 7. Add a dedicated internal fit-application path diff --git a/docs/dev/benchmarking/20260614-210908_linux-x86_64_py314_tutorial-benchmarks.csv b/docs/dev/benchmarking/20260614-210908_linux-x86_64_py314_tutorial-benchmarks.csv new file mode 100644 index 000000000..b39258539 --- /dev/null +++ b/docs/dev/benchmarking/20260614-210908_linux-x86_64_py314_tutorial-benchmarks.csv @@ -0,0 +1,29 @@ +tutorial_name,elapsed_seconds,status +bayesian-dream-display-lbco-hrpt.py,10.815,ok +bayesian-dream-lbco-hrpt.py,243.089,ok +bayesian-emcee-lbco-hrpt.py,81.163,ok +bayesian-emcee-resume-lbco-hrpt.py,70.278,ok +bayesian-emcee-tbti-heidi.py,186.429,ok +calibrate-beer-ess.py,56.878,ok +fitting-exercise-si-lbco.py,47.517,ok +joint-si-bragg-pdf.py,178.861,ok +load-and-fit-lbco-hrpt.py,11.816,ok +pdf-nacl-xrd.py,19.225,ok +pdf-ni-npd.py,107.750,ok +pdf-si-nomad.py,24.634,ok +refine-cosio-d20-tscan-resumed.py,10.615,ok +refine-cosio-d20-tscan.py,173.624,ok +refine-cosio-d20.py,86.123,ok +refine-hs-hrpt.py,170.023,ok +refine-lbco-hrpt-from-cif.py,27.435,ok +refine-lbco-hrpt-from-data.py,30.840,ok +refine-lbco-hrpt-report.py,58.858,ok +refine-lbco-si-mcstas.py,22.431,ok +refine-ncaf-wish.py,307.206,ok +refine-pbso4-joint.py,8.212,ok +refine-si-sepd.py,368.010,ok +refine-taurine-senju.py,66.711,ok +refine-tbti-heidi.py,38.642,ok +simulate-lbco-cwl.py,7.010,ok +simulate-nacl-xray.py,7.611,ok +simulate-si-tof.py,8.012,ok diff --git a/docs/dev/index.md b/docs/dev/index.md index 00e474425..e44bc8d84 100644 --- a/docs/dev/index.md +++ b/docs/dev/index.md @@ -7,16 +7,16 @@ remains isolated under `docs/docs`. ## Structure -| Path | Purpose | -| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| [`adrs/index.md`](adrs/index.md) | Architecture and decision navigation, grouped by topic. | -| [`issues/open.md`](issues/open.md) | Prioritized open development issues and design questions. | -| [`issues/closed.md`](issues/closed.md) | Closed development issues retained for history. | -| [`issues/recommended-priorities.md`](issues/recommended-priorities.md) | Curated, re-tiered work recommendation across issues, ADRs, and roadmap. | -| [`package-structure/short.md`](package-structure/short.md) | Generated compact package tree. | -| [`package-structure/full.md`](package-structure/full.md) | Generated package tree with top-level classes. | -| [`plans/`](plans/) | Implementation plans for larger migrations. | -| [`roadmap/ROADMAP.md`](roadmap/ROADMAP.md) | Development roadmap. This may later be copied into `docs/docs` during the published-docs build. | +| Path | Purpose | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| [`adrs/index.md`](adrs/index.md) | Architecture and decision navigation, grouped by topic. | +| [`issues/index.md`](issues/index.md) | Issue index: open issues (ordered by priority) and closed issues, as tables linking to each file. | +| [`issues/open/`](issues/open/) | Open development issues, one file per issue (`_.md`). | +| [`issues/closed/`](issues/closed/) | Closed development issues retained for history, one file per issue (`<title>.md`). | +| [`package-structure/short.md`](package-structure/short.md) | Generated compact package tree. | +| [`package-structure/full.md`](package-structure/full.md) | Generated package tree with top-level classes. | +| [`plans/`](plans/) | Implementation plans for larger migrations. | +| [`roadmap/ROADMAP.md`](roadmap/ROADMAP.md) | Development roadmap. This may later be copied into `docs/docs` during the published-docs build. | ## Rules diff --git a/docs/dev/issues/closed/access-space-group-from-atomsites-for-wyckoff-letters.md b/docs/dev/issues/closed/access-space-group-from-atomsites-for-wyckoff-letters.md new file mode 100644 index 000000000..13b3a1bf3 --- /dev/null +++ b/docs/dev/issues/closed/access-space-group-from-atomsites-for-wyckoff-letters.md @@ -0,0 +1,8 @@ +# 51. Access Space Group from `AtomSites` for Wyckoff Letters + +Closed by the Wyckoff-letter-detection implementation. `AtomSite` now +derives its allowed Wyckoff letters from the parent structure's space +group (via `_resolve_structure_space_group`) instead of a hardcoded +list, and the missing-letter case is handled explicitly: untabulated +space groups leave the Wyckoff letter and multiplicity unset, while +tabulated groups detect and fill them during the update flow. diff --git a/docs/dev/issues/closed/add-help-methods-to-public-discovery-facades.md b/docs/dev/issues/closed/add-help-methods-to-public-discovery-facades.md new file mode 100644 index 000000000..b279ace06 --- /dev/null +++ b/docs/dev/issues/closed/add-help-methods-to-public-discovery-facades.md @@ -0,0 +1,10 @@ +# 77. Add Help Methods to Public Discovery Facades + +Added consistent `help()` methods for plain user-facing facade classes +that do not inherit the guarded object hierarchy: `project.display`, +`project.display.parameters`, `project.display.fit`, +`project.display.posterior`, `analysis.display`, and `project.summary`. +Introduced `render_object_help()` so these helpers share the same +property and method table style as `GuardedBase.help()`. Documented the +convention in +[`help-discoverability.md`](../adrs/accepted/help-discoverability.md). diff --git a/docs/dev/issues/closed/add-sequential-to-fitmodeenum-and-show-methods-to-analysis.md b/docs/dev/issues/closed/add-sequential-to-fitmodeenum-and-show-methods-to-analysis.md new file mode 100644 index 000000000..6a8163155 --- /dev/null +++ b/docs/dev/issues/closed/add-sequential-to-fitmodeenum-and-show-methods-to-analysis.md @@ -0,0 +1,20 @@ +# 78. Add `SEQUENTIAL` to `FitModeEnum` and Show Methods to Analysis + +Added `SEQUENTIAL = 'sequential'` to `FitModeEnum`. `fit_sequential()` +now sets `fit_mode.mode = 'sequential'` internally so the mode is +persisted in CIF. Added `show_fit_mode_types()` (filters by experiment +count: ≤1 → only `single`; >1 → all three) and `show_fit_mode_types()` +on `Analysis`. If `fit()` is called while mode is `'sequential'`, it +logs an error directing the user to `fit_sequential()`. Promoted +`fit_mode` from a pure single-type category to one with show methods. + +--- + +## Persist Per-Experiment Calculator Selection + +Calculator selection now lives in the experiment-side `calculation` +category and is persisted in experiment CIF via +`_calculation.calculator_type`. Loading restores the explicit backend +selection before dependent experiment categories are populated, so saved +projects keep the exact active calculator configuration instead of +falling back to auto-resolution. diff --git a/docs/dev/issues/closed/add-serial-pattern-generation-benchmarks.md b/docs/dev/issues/closed/add-serial-pattern-generation-benchmarks.md new file mode 100644 index 000000000..a7a67fa8e --- /dev/null +++ b/docs/dev/issues/closed/add-serial-pattern-generation-benchmarks.md @@ -0,0 +1,8 @@ +# 16. Add Serial Pattern-Generation Benchmarks + +Closed by the cross-engine verification work (#195): +`tests/benchmarks/test_calculate_pattern_benchmark.py` benchmarks +single-pattern calculation on fixed datasets across cryspy and crysfml, +and a dedicated serial `pixi run benchmarks` task runs it outside the +parallel xdist suite (no `-n auto`). Regression thresholds remain +deferred until timings stabilise, exactly as the issue specified. diff --git a/docs/dev/issues/closed/cli-fit-command-never-saves-results-to-disk.md b/docs/dev/issues/closed/cli-fit-command-never-saves-results-to-disk.md new file mode 100644 index 000000000..911a21ea2 --- /dev/null +++ b/docs/dev/issues/closed/cli-fit-command-never-saves-results-to-disk.md @@ -0,0 +1,10 @@ +# 137. CLI `fit` Command Never Saves Results to Disk + +Closed on re-verification — **not a defect**. `Analysis.fit()` +auto-saves via `self.project.save()` in `_run_single` / `_run_joint` / +`_run_sequential` (`analysis.py:2661,2688,2727`) whenever the project +path is set. The CLI `fit` command's non-`--dry` path leaves +`project.info.path` set, so results persist; `--dry` nulls the path to +suppress saving (asserted by `test_cli_fit_dry_clears_path`). The +original audit finding read the CLI in isolation and missed `fit()`'s +internal save; no code change is needed. diff --git a/docs/dev/issues/closed/collapse-duplicate-predictive-cache-key-helpers.md b/docs/dev/issues/closed/collapse-duplicate-predictive-cache-key-helpers.md new file mode 100644 index 000000000..97b211e9d --- /dev/null +++ b/docs/dev/issues/closed/collapse-duplicate-predictive-cache-key-helpers.md @@ -0,0 +1,6 @@ +# 100. Collapse Duplicate Predictive-Cache-Key Helpers + +Closed by the emcee minimizer implementation. +`posterior_predictive_cache_key()` in `analysis.fit_helpers.bayesian` is +now the single helper used by analysis, plotting, and project display +code. diff --git a/docs/dev/issues/closed.md b/docs/dev/issues/closed/consistent-type-suffix-in-switchable-category-api-names.md similarity index 58% rename from docs/dev/issues/closed.md rename to docs/dev/issues/closed/consistent-type-suffix-in-switchable-category-api-names.md index e6b000055..af47e4ad0 100644 --- a/docs/dev/issues/closed.md +++ b/docs/dev/issues/closed/consistent-type-suffix-in-switchable-category-api-names.md @@ -1,69 +1,4 @@ -# EasyDiffraction — Closed Issues - -Issues that have been fully resolved. Kept for historical reference. - ---- - -## 51. Access Space Group from `AtomSites` for Wyckoff Letters - -Closed by the Wyckoff-letter-detection implementation. `AtomSite` now -derives its allowed Wyckoff letters from the parent structure's space -group (via `_resolve_structure_space_group`) instead of a hardcoded -list, and the missing-letter case is handled explicitly: untabulated -space groups leave the Wyckoff letter and multiplicity unset, while -tabulated groups detect and fill them during the update flow. - ---- - -## 103. Make `_sync_engine_from_minimizer_category` Skip-Keys Declarative - -Closed by the emcee minimizer implementation. Minimizer categories now -declare `_engine_sync_skip_keys`, and analysis sync filters against that -set instead of hardcoding skipped keys. - ---- - -## 101. Remove Dead Branch in `_fit_state_categories` - -Closed by the emcee minimizer implementation. The deterministic branch -that returned the same category list as the fallthrough path was removed -while preserving unsupported `result_kind` warning behavior. - ---- - -## 100. Collapse Duplicate Predictive-Cache-Key Helpers - -Closed by the emcee minimizer implementation. -`posterior_predictive_cache_key()` in `analysis.fit_helpers.bayesian` is -now the single helper used by analysis, plotting, and project display -code. - ---- - -## 77. Add Help Methods to Public Discovery Facades - -Added consistent `help()` methods for plain user-facing facade classes -that do not inherit the guarded object hierarchy: `project.display`, -`project.display.parameters`, `project.display.fit`, -`project.display.posterior`, `analysis.display`, and `project.summary`. -Introduced `render_object_help()` so these helpers share the same -property and method table style as `GuardedBase.help()`. Documented the -convention in -[`help-discoverability.md`](../adrs/accepted/help-discoverability.md). - ---- - -## 72. Warn on All Switchable-Category Type Changes - -Closed by -[`switchable-category-owned-selectors.md`](../adrs/accepted/switchable-category-owned-selectors.md). -Type-change warnings now run through owner `_swap_<name>` hooks, so -every category-owned selector assignment has a uniform owner-mediated -place to warn about values that will be discarded. - ---- - -## 76. Consistent `_type` Suffix in Switchable-Category API Names +# 76. Consistent `_type` Suffix in Switchable-Category API Names Closed by [`switchable-category-owned-selectors.md`](../adrs/accepted/switchable-category-owned-selectors.md). @@ -192,26 +127,3 @@ Removed the `data_type` setter, `show_supported_data_types()`, and creation (resolved from `DataFactory.default_tag(...)` using the experiment's axes), like experiment type itself. This prevents switching to an incompatible data class and silently discarding loaded data. - ---- - -## 78. Add `SEQUENTIAL` to `FitModeEnum` and Show Methods to Analysis - -Added `SEQUENTIAL = 'sequential'` to `FitModeEnum`. `fit_sequential()` -now sets `fit_mode.mode = 'sequential'` internally so the mode is -persisted in CIF. Added `show_fit_mode_types()` (filters by experiment -count: ≤1 → only `single`; >1 → all three) and `show_fit_mode_types()` -on `Analysis`. If `fit()` is called while mode is `'sequential'`, it -logs an error directing the user to `fit_sequential()`. Promoted -`fit_mode` from a pure single-type category to one with show methods. - ---- - -## Persist Per-Experiment Calculator Selection - -Calculator selection now lives in the experiment-side `calculation` -category and is persisted in experiment CIF via -`_calculation.calculator_type`. Loading restores the explicit backend -selection before dependent experiment categories are populated, so saved -projects keep the exact active calculator configuration instead of -falling back to auto-resolution. diff --git a/docs/dev/issues/closed/fix-summary-display-inconsistencies.md b/docs/dev/issues/closed/fix-summary-display-inconsistencies.md new file mode 100644 index 000000000..5fe60a28f --- /dev/null +++ b/docs/dev/issues/closed/fix-summary-display-inconsistencies.md @@ -0,0 +1,6 @@ +# 43. Fix Summary Display Inconsistencies + +Closed: the `src/easydiffraction/summary/` module was removed and +summary rendering relocated to `project/display.py` (per the +`project-summary-rendering` ADR); the cited description-wrapping / +header-capitalisation TODOs no longer exist anywhere in the source. diff --git a/docs/dev/issues/closed/make-ascii-plot-width-configurable.md b/docs/dev/issues/closed/make-ascii-plot-width-configurable.md new file mode 100644 index 000000000..af4d30829 --- /dev/null +++ b/docs/dev/issues/closed/make-ascii-plot-width-configurable.md @@ -0,0 +1,6 @@ +# 56. Make ASCII Plot Width Configurable + +Closed: the hardcoded `width = 60` and its TODO were removed. ASCII plot +width is now derived from the terminal via `_chart_point_count()` +(`display/plotters/ascii.py:46`, used at `:289`), clamped to a minimum +point count. diff --git a/docs/dev/issues/closed/make-sync-engine-from-minimizer-category-skip-keys-declarative.md b/docs/dev/issues/closed/make-sync-engine-from-minimizer-category-skip-keys-declarative.md new file mode 100644 index 000000000..e3842f6ba --- /dev/null +++ b/docs/dev/issues/closed/make-sync-engine-from-minimizer-category-skip-keys-declarative.md @@ -0,0 +1,5 @@ +# 103. Make `_sync_engine_from_minimizer_category` Skip-Keys Declarative + +Closed by the emcee minimizer implementation. Minimizer categories now +declare `_engine_sync_skip_keys`, and analysis sync filters against that +set instead of hardcoding skipped keys. diff --git a/docs/dev/issues/closed/move-as-cif-show-as-cif-from-projectinfo-to-io-cif-serialize.md b/docs/dev/issues/closed/move-as-cif-show-as-cif-from-projectinfo-to-io-cif-serialize.md new file mode 100644 index 000000000..2dc58209c --- /dev/null +++ b/docs/dev/issues/closed/move-as-cif-show-as-cif-from-projectinfo-to-io-cif-serialize.md @@ -0,0 +1,6 @@ +# 58. Move `as_cif` / `show_as_cif` from `ProjectInfo` to `io.cif.serialize` + +Closed: serialization moved to `io/cif/serialize.py` +`project_info_to_cif` (`:548`); `ProjectInfo.as_cif` / `show_as_cif` now +delegate to it (`project/categories/info/default.py:173`). The original +"consider moving" TODOs are gone. diff --git a/docs/dev/issues/closed/process-default-values-on-experiment-creation.md b/docs/dev/issues/closed/process-default-values-on-experiment-creation.md new file mode 100644 index 000000000..7713e78e0 --- /dev/null +++ b/docs/dev/issues/closed/process-default-values-on-experiment-creation.md @@ -0,0 +1,8 @@ +# 24. Process Default Values on Experiment Creation + +Closed (#157): the CrysFML calculator no longer fills instrument/peak +defaults with inline `... if instrument else 1.0` fallbacks at +calculation time (none remain in `analysis/calculators/crysfml.py`); the +experiment dict is built from clean attribute maps via +`_copy_present_values`. The "process defaults on creation" TODO was +removed. diff --git a/docs/dev/issues/closed/rebuild-joint-fit-weights-on-every-fit.md b/docs/dev/issues/closed/rebuild-joint-fit-weights-on-every-fit.md new file mode 100644 index 000000000..d7fc96430 --- /dev/null +++ b/docs/dev/issues/closed/rebuild-joint-fit-weights-on-every-fit.md @@ -0,0 +1,10 @@ +# 3. Rebuild Joint-Fit Weights on Every Fit + +Closed: `Analysis._prepare_joint_fit()` (`analysis.py:1547`) now runs at +the start of every joint fit via `_run_fit_mode` (`analysis.py:1429`). +It validates `joint_fit` against the project experiments — raising +`ValueError` if a row references an experiment absent from the project +(removed/renamed), auto-creating rows for new experiments, and +re-asserting every experiment has a row — so stale weights can no longer +reach the minimiser. (Validating weight _values_ — negatives / all-zero +— remains tracked separately by issue #15.) diff --git a/docs/dev/issues/closed/reconcile-git-ignored-agents-md-claude-md-with-their-checked-in-role.md b/docs/dev/issues/closed/reconcile-git-ignored-agents-md-claude-md-with-their-checked-in-role.md new file mode 100644 index 000000000..5910302be --- /dev/null +++ b/docs/dev/issues/closed/reconcile-git-ignored-agents-md-claude-md-with-their-checked-in-role.md @@ -0,0 +1,12 @@ +# 164. Reconcile Git-Ignored `AGENTS.md` / `CLAUDE.md` With Their Checked-In Role + +Resolved by an explicit project-owner decision: `AGENTS.md` and +`CLAUDE.md` are **intentionally git-ignored** and kept local-only, not +committed repository artifacts. The `.gitignore` `# Agents` block keeps +both files ignored, and a note at the top of `AGENTS.md` (plus a clause +in §Change Discipline) records that they are deliberately local-only and +that their `AGENTS_review-N.md` / `AGENTS_reply-N.md` artifacts are +likewise untracked and may be cleaned up once a review cycle closes. +This removes the earlier contradiction (the document described itself as +checked-in while being ignored) by adopting the local-only branch rather +than committing the files. diff --git a/docs/dev/issues/closed/remove-dead-branch-in-fit-state-categories.md b/docs/dev/issues/closed/remove-dead-branch-in-fit-state-categories.md new file mode 100644 index 000000000..1d37ce4f3 --- /dev/null +++ b/docs/dev/issues/closed/remove-dead-branch-in-fit-state-categories.md @@ -0,0 +1,5 @@ +# 101. Remove Dead Branch in `_fit_state_categories` + +Closed by the emcee minimizer implementation. The deterministic branch +that returned the same category list as the fallthrough path was removed +while preserving unsupported `result_kind` warning behavior. diff --git a/docs/dev/issues/closed/remove-orphaned-fit-result-reset-helper.md b/docs/dev/issues/closed/remove-orphaned-fit-result-reset-helper.md new file mode 100644 index 000000000..2d6956af5 --- /dev/null +++ b/docs/dev/issues/closed/remove-orphaned-fit-result-reset-helper.md @@ -0,0 +1,7 @@ +# 105. Remove Orphaned Fit-Result Reset Helper + +Closed (#183): `Analysis._clear_fit_result_projection` is no longer +orphaned — it is called by the undo-fit rollback path +(`analysis.py:1412`), resetting fit-result descriptors in place while +preserving the active instance. This matches the issue's allowed +"reintroduce a caller" resolution. diff --git a/docs/dev/issues/closed/serialise-none-as-in-cif-output.md b/docs/dev/issues/closed/serialise-none-as-in-cif-output.md new file mode 100644 index 000000000..d0df2dab1 --- /dev/null +++ b/docs/dev/issues/closed/serialise-none-as-in-cif-output.md @@ -0,0 +1,7 @@ +# 84. Serialise `None` as `.` in CIF Output + +Closed: `io/cif/serialize.py` `format_value` (`:57-58`) maps +`None`/`NaN` to the CIF unknown marker `?` instead of literal `None`, +and `io/cif/parse.py` (`:58`) maps both `?` and `.` back to `None`, +preserving round-trip fidelity. The issue explicitly allowed `?` as an +acceptable encoding. diff --git a/docs/dev/issues/closed/warn-on-all-switchable-category-type-changes.md b/docs/dev/issues/closed/warn-on-all-switchable-category-type-changes.md new file mode 100644 index 000000000..314886149 --- /dev/null +++ b/docs/dev/issues/closed/warn-on-all-switchable-category-type-changes.md @@ -0,0 +1,7 @@ +# 72. Warn on All Switchable-Category Type Changes + +Closed by +[`switchable-category-owned-selectors.md`](../adrs/accepted/switchable-category-owned-selectors.md). +Type-change warnings now run through owner `_swap_<name>` hooks, so +every category-owned selector assignment has a uniform owner-mediated +place to warn about values that will be discarded. diff --git a/docs/dev/issues/index.md b/docs/dev/issues/index.md new file mode 100644 index 000000000..d174a157f --- /dev/null +++ b/docs/dev/issues/index.md @@ -0,0 +1,175 @@ +# EasyDiffraction — Issue Index + +Index of development issues. Each issue is one Markdown file; this page +is only a table of contents. **Rationale and details live in the +individual issue files** — not here. + +- Open issues are in [`open/`](open/), one file per issue named + `<priority>_<title>.md`, and are listed below ordered by priority + (`highest` → `lowest`), then by issue number. +- Closed issues are in [`closed/`](closed/), one file per issue named + `<title>.md` (no priority prefix). + +## Open Issues + +| # | Issue | Priority | Type | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | ----------------------------------------------------------- | +| 15 | [Validate Joint-Fit Weights Before Residual Normalisation](open/highest_validate-joint-fit-weights-before-residual-normalisation.md) | `[priority] highest` | Correctness | +| 85 | [Retain Per-Experiment Fitted Parameters for Plotting](open/highest_retain-per-experiment-fitted-parameters-for-plotting.md) | `[priority] highest` | Correctness / UX | +| 119 | [Model Sample Absorption (Debye–Scherrer, μR)](open/highest_model-sample-absorption-debye-scherrer-r.md) | `[priority] highest` | Physics / Engine feature | +| 130 | [cryspy Diverges on TOF Jorgensen–Von Dreele Lorentzian](open/highest_cryspy-diverges-on-tof-jorgensen-von-dreele-lorentzian.md) | `[priority] highest` | Correctness | +| 134 | [Investigate ed-crysfml TOF Jorgensen Profile Discrepancy](open/highest_investigate-ed-crysfml-tof-jorgensen-profile-discrepancy.md) | `[priority] highest` | Correctness | +| 138 | [`calculate_structure_factors` Contract Disagrees Across Backends and Caller](open/highest_calculate-structure-factors-contract-disagrees-across-backends-and-caller.md) | `[priority] highest` | Correctness / Maintainability | +| 139 | [Unknown Switchable-Category Type on CIF Restore Silently Drops Parameters](open/highest_unknown-switchable-category-type-on-cif-restore-silently-drops-parameters.md) | `[priority] highest` | Robustness | +| 140 | [Unify Uncertainty-Floor Handling Across Bragg PD, Single-Crystal, and PDF Data](open/highest_unify-uncertainty-floor-handling-across-bragg-pd-single-crystal-and-pdf-data.md) | `[priority] highest` | Correctness / Robustness | +| 8 | [Add Explicit `create()` Signatures on Collections](open/high_add-explicit-create-signatures-on-collections.md) | `[priority] high` | API safety | +| 61 | [Clarify Logger Default Reaction Mode](open/high_clarify-logger-default-reaction-mode.md) | `[priority] high` | Design | +| 66 | [Decide Error-Handling Strategy: `log.error` vs `raise`](open/high_decide-error-handling-strategy-log-error-vs-raise.md) | `[priority] high` | Design | +| 116 | [Add a Static Type Checker to the Quality Gate](open/high_add-a-static-type-checker-to-the-quality-gate.md) | `[priority] high` | Tooling / Correctness | +| 133 | [Rename `asym_empir_*` and Add the Physical FCJ Asymmetry Model](open/high_rename-asym-empir-and-add-the-physical-fcj-asymmetry-model.md) | `[priority] high` | Experiment model / Peak profile / API naming | +| 21 | [Clarify CrysPy TOF Background CIF Tag Names](open/medium_clarify-cryspy-tof-background-cif-tag-names.md) | `[priority] medium` | Correctness / Naming | +| 25 | [Refactor Data `_update` Methods (Split and Unify)](open/medium_refactor-data-update-methods-split-and-unify.md) | `[priority] medium` | Maintainability | +| 29 | [Standardise CIF ID Validator Pattern Across Categories](open/medium_standardise-cif-id-validator-pattern-across-categories.md) | `[priority] medium` | Consistency | +| 32 | [Move Common Methods to `DatablockCollection` Base Class](open/medium_move-common-methods-to-datablockcollection-base-class.md) | `[priority] medium` | Maintainability | +| 33 | [Make `DatablockItem._update_categories` Abstract](open/medium_make-datablockitem-update-categories-abstract.md) | `[priority] medium` | Design | +| 38 | [Fix `@typechecked` / gemmi Interaction in Factories](open/medium_fix-typechecked-gemmi-interaction-in-factories.md) | `[priority] medium` | Bug | +| 40 | [Implement Resetting `.user_constrained` to `False`](open/medium_implement-resetting-user-constrained-to-false.md) | `[priority] medium` | Correctness | +| 65 | [Replace All Bare `print()` Calls with Logging](open/medium_replace-all-bare-print-calls-with-logging.md) | `[priority] medium` | Code quality | +| 67 | [Custom Validation for Parameter/Descriptor and Category Types](open/medium_custom-validation-for-parameter-descriptor-and-category-types.md) | `[priority] medium` | Design | +| 70 | [Standardise Class Member Ordering and Visual Section Headers](open/medium_standardise-class-member-ordering-and-visual-section-headers.md) | `[priority] medium` | Code style | +| 74 | [Sync Property Type Hints with Private Attributes + Custom Lint](open/medium_sync-property-type-hints-with-private-attributes-custom-lint.md) | `[priority] medium` | Tooling / Correctness | +| 81 | [Enforce Docstrings on All Public Methods](open/medium_enforce-docstrings-on-all-public-methods.md) | `[priority] medium` | Code quality | +| 89 | [Parallel Independent Fits for Single/Independent Fit Mode](open/medium_parallel-independent-fits-for-single-independent-fit-mode.md) | `[priority] medium` | Performance | +| 93 | [Eliminate Flicker in Live Progress Tables](open/medium_eliminate-flicker-in-live-progress-tables.md) | `[priority] medium` | UX | +| 95 | [Re-Enable DREAM Multiprocessing in Direct Python Scripts](open/medium_re-enable-dream-multiprocessing-in-direct-python-scripts.md) | `[priority] medium` | Performance / Script runtime | +| 107 | [Validate Generated CIF Report Against Official IUCr Dictionaries](open/medium_validate-generated-cif-report-against-official-iucr-dictionaries.md) | `[priority] medium` | Test coverage | +| 113 | [Cross-Repository Validation Harness (nightly)](open/medium_cross-repository-validation-harness-nightly.md) | `[priority] medium` | Test infrastructure | +| 120 | [Decide Whether Inactive Fit-Mode Categories Stay Lenient](open/medium_decide-whether-inactive-fit-mode-categories-stay-lenient.md) | `[priority] medium` | API design | +| 121 | [Clarify `joint_fit` Lifecycle Outside Execution](open/medium_clarify-joint-fit-lifecycle-outside-execution.md) | `[priority] medium` | Fragility | +| 122 | [Define `joint_fit.weight` Bounds](open/medium_define-joint-fit-weight-bounds.md) | `[priority] medium` | Data model | +| 123 | [Define `sequential_fit_extract` Target Scope](open/medium_define-sequential-fit-extract-target-scope.md) | `[priority] medium` | Data model | +| 124 | [Decide Sequential Extraction Failure Policy](open/medium_decide-sequential-extraction-failure-policy.md) | `[priority] medium` | Runtime behaviour | +| 131 | [Add SyCos/SySin Systematic Peak-Position Corrections](open/medium_add-sycos-sysin-systematic-peak-position-corrections.md) | `[priority] medium` | Feature / Experiment model | +| 135 | [More Intuitive ADP Creation API (type-aware kwargs)](open/medium_more-intuitive-adp-creation-api-type-aware-kwargs.md) | `[priority] medium` | API design | +| 141 | [BUMPS Drops Uncertainties Silently on Singular Covariance](open/medium_bumps-drops-uncertainties-silently-on-singular-covariance.md) | `[priority] medium` | Correctness / Silent failure | +| 142 | [Numeric CIF Parse Failure Silently Stores `None`](open/medium_numeric-cif-parse-failure-silently-stores-none.md) | `[priority] medium` | Robustness | +| 143 | [Verify String-Field CIF Round-Trip Strips `;` Text Delimiters](open/medium_verify-string-field-cif-round-trip-strips-text-delimiters.md) | `[priority] medium` | Correctness | +| 144 | [`_find_loop_for_category` Missing None Guard](open/medium_find-loop-for-category-missing-none-guard.md) | `[priority] medium` | Robustness | +| 145 | [Escape User Names Before Rich Markup Rendering](open/medium_escape-user-names-before-rich-markup-rendering.md) | `[priority] medium` | Robustness | +| 146 | [Guard Hand-Edited Project Timestamps on Restore](open/medium_guard-hand-edited-project-timestamps-on-restore.md) | `[priority] medium` | Robustness | +| 147 | [Don't Report a PDF Report as Written When No TeX Engine Exists](open/medium_don-t-report-a-pdf-report-as-written-when-no-tex-engine-exists.md) | `[priority] medium` | UX | +| 148 | [Atom-Site / Phase-ID Label Regex Rejects Valid CIF Labels](open/medium_atom-site-phase-id-label-regex-rejects-valid-cif-labels.md) | `[priority] medium` | Robustness | +| 149 | [Validate Inverted Excluded Regions (start > end)](open/medium_validate-inverted-excluded-regions-start-end.md) | `[priority] medium` | API safety | +| 150 | [Bragg Powder ASCII Loader Returns Zero Points Instead of Raising](open/medium_bragg-powder-ascii-loader-returns-zero-points-instead-of-raising.md) | `[priority] medium` | Robustness | +| 151 | [Replace Dead `else` Branch in `_set_calc_status` With a Real Boolean Check](open/medium_replace-dead-else-branch-in-set-calc-status-with-a-real-boolean-check.md) | `[priority] medium` | Correctness / Dead code | +| 162 | [Untrack Generated Tutorial-Benchmark CSVs](open/medium_untrack-generated-tutorial-benchmark-csvs.md) | `[priority] medium` | Hygiene | +| 9 | [Add Future Enum Extensions](open/low_add-future-enum-extensions.md) | `[priority] low` | Design improvement | +| 10 | [Unify Project-Level Update Orchestration](open/low_unify-project-level-update-orchestration.md) | `[priority] low` | Maintainability | +| 11 | [Document Category `_update` Contract](open/low_document-category-update-contract.md) | `[priority] low` | Maintainability | +| 13 | [Suppress Redundant Dirty-Flag Sets in Symmetry Constraints](open/low_suppress-redundant-dirty-flag-sets-in-symmetry-constraints.md) | `[priority] low` | Performance | +| 14 | [Finer-Grained Parameter Change Tracking](open/low_finer-grained-parameter-change-tracking.md) | `[priority] low` | Performance | +| 17 | [Use PDF-Specific CIF Names for Total Scattering](open/low_use-pdf-specific-cif-names-for-total-scattering.md) | `[priority] low` | Naming | +| 18 | [Move CIF v2→v1 Conversion Out of Calculator](open/low_move-cif-v2-v1-conversion-out-of-calculator.md) | `[priority] low` | Maintainability | +| 19 | [Add Debug-Mode Logging for Calculator Imports](open/low_add-debug-mode-logging-for-calculator-imports.md) | `[priority] low` | Diagnostics | +| 20 | [Redirect or Suppress CrysPy stderr Warnings](open/low_redirect-or-suppress-cryspy-stderr-warnings.md) | `[priority] low` | UX | +| 22 | [Check CrysPy Single-Crystal Instrument Mapping](open/low_check-cryspy-single-crystal-instrument-mapping.md) | `[priority] low` | Correctness | +| 23 | [Investigate PyCrysFML Pattern Length Discrepancy](open/low_investigate-pycrysfml-pattern-length-discrepancy.md) | `[priority] low` | Correctness | +| 26 | [Clarify `dtype` Usage in Data Point Arrays](open/low_clarify-dtype-usage-in-data-point-arrays.md) | `[priority] low` | Cleanup | +| 27 | [Handle Zero Uncertainty in Bragg PD Data](open/low_handle-zero-uncertainty-in-bragg-pd-data.md) | `[priority] low` | Correctness | +| 28 | [Clarify Bragg PD Data Collection Description](open/low_clarify-bragg-pd-data-collection-description.md) | `[priority] low` | Cleanup | +| 30 | [Make `refinement_status` Default an Enum](open/low_make-refinement-status-default-an-enum.md) | `[priority] low` | Design | +| 31 | [Rename PD Data Point Mixins](open/low_rename-pd-data-point-mixins.md) | `[priority] low` | Naming | +| 34 | [Auto-Extract `PeakProfileTypeEnum` from Peak Classes](open/low_auto-extract-peakprofiletypeenum-from-peak-classes.md) | `[priority] low` | Design | +| 35 | [Rename `BeamModeEnum` Members to CWL/TOF](open/low_rename-beammodeenum-members-to-cwl-tof.md) | `[priority] low` | Naming | +| 36 | [Consider a Common `EnumBase` with `default()` / `description()`](open/low_consider-a-common-enumbase-with-default-description.md) | `[priority] low` | Design | +| 37 | [Rename Experiment `.type` Property](open/low_rename-experiment-type-property.md) | `[priority] low` | Naming | +| 39 | [Improve `_update_priority` Handling in Categories](open/low_improve-update-priority-handling-in-categories.md) | `[priority] low` | Design | +| 41 | [Check Whether `_mark_dirty` in `_set_value` is Actually Used](open/low_check-whether-mark-dirty-in-set-value-is-actually-used.md) | `[priority] low` | Cleanup | +| 42 | [MkDocs Doesn't Unpack Types in Validation Module](open/low_mkdocs-doesn-t-unpack-types-in-validation-module.md) | `[priority] low` | Docs | +| 44 | [Merge Parameter Record Construction in Analysis](open/low_merge-parameter-record-construction-in-analysis.md) | `[priority] low` | Cleanup | +| 45 | [Decide Default for Alias/Constraint Descriptors](open/low_decide-default-for-alias-constraint-descriptors.md) | `[priority] low` | Design | +| 46 | [Improve `JointFitItem` Descriptions](open/low_improve-jointfititem-descriptions.md) | `[priority] low` | Naming | +| 47 | [Improve Error Handling in Crystallography Utilities](open/low_improve-error-handling-in-crystallography-utilities.md) | `[priority] low` | Diagnostics | +| 48 | [Fix CrysPy TOF Instrument Default](open/low_fix-cryspy-tof-instrument-default.md) | `[priority] low` | Bug workaround | +| 49 | [Automate Space Group CIF Name Variants](open/low_automate-space-group-cif-name-variants.md) | `[priority] low` | Maintainability | +| 50 | [Clarify `Cell._update` Usage of `called_by_minimizer`](open/low_clarify-cell-update-usage-of-called-by-minimizer.md) | `[priority] low` | Cleanup | +| 52 | [Rename Line-Segment Background `y` to `intensity`](open/low_rename-line-segment-background-y-to-intensity.md) | `[priority] low` | Naming | +| 53 | [Move `show()` to `CategoryCollection` Base Class](open/low_move-show-to-categorycollection-base-class.md) | `[priority] low` | Maintainability | +| 54 | [Add `point_id` to Excluded Regions](open/low_add-point-id-to-excluded-regions.md) | `[priority] low` | Completeness | +| 55 | [Fix Jupyter Scroll Disabling for MkDocs](open/low_fix-jupyter-scroll-disabling-for-mkdocs.md) | `[priority] low` | Docs / UX | +| 57 | [Clean Up CIF Deserialisation Helpers](open/low_clean-up-cif-deserialisation-helpers.md) | `[priority] low` | Maintainability | +| 59 | [Add CIF Name Validation or Normalisation in Parse](open/low_add-cif-name-validation-or-normalisation-in-parse.md) | `[priority] low` | Robustness | +| 60 | [Unify `mkdir` Usage Across the Codebase](open/low_unify-mkdir-usage-across-the-codebase.md) | `[priority] low` | Cleanup | +| 62 | [Complete Migration from `render_table` to `TableRenderer`](open/low_complete-migration-from-render-table-to-tablerenderer.md) | `[priority] low` | Cleanup | +| 63 | [Fix Calculator `calculate_pattern` Signature Type](open/low_fix-calculator-calculate-pattern-signature-type.md) | `[priority] low` | Design | +| 64 | [Check Whether `_not_used_if_loading_from_cif` Code is Needed](open/low_check-whether-not-used-if-loading-from-cif-code-is-needed.md) | `[priority] low` | Cleanup | +| 68 | [Decide Whether to Apply `@typechecked` to All Public Methods](open/low_decide-whether-to-apply-typechecked-to-all-public-methods.md) | `[priority] low` | Design | +| 69 | [Shorter Public API Names via `__init__.py` Re-Exports](open/low_shorter-public-api-names-via-init-py-re-exports.md) | `[priority] low` | API ergonomics | +| 71 | [Create `_update_priority` Reference Table for Categories](open/low_create-update-priority-reference-table-for-categories.md) | `[priority] low` | Documentation | +| 73 | [Unify Setter Parameter Naming Convention](open/low_unify-setter-parameter-naming-convention.md) | `[priority] low` | Code style | +| 75 | [Add `show_supported_calculators()` on Analysis or Project](open/low_add-show-supported-calculators-on-analysis-or-project.md) | `[priority] low` | API completeness | +| 79 | [Verify Completeness of Analysis CIF Serialisation](open/low_verify-completeness-of-analysis-cif-serialisation.md) | `[priority] low` | Correctness | +| 80 | [Resolve `Any` vs `object` Type Annotation Policy](open/low_resolve-any-vs-object-type-annotation-policy.md) | `[priority] low` | Code style | +| 82 | [Document `param-docstring-fix` and `notebook-prepare` Workflow](open/low_document-param-docstring-fix-and-notebook-prepare-workflow.md) | `[priority] low` | Documentation | +| 83 | [Remove Redundant Parameter Listing from Parameter Itself](open/low_remove-redundant-parameter-listing-from-parameter-itself.md) | `[priority] low` | Cleanup | +| 86 | [Auto-Resolve `plot_param` X-Axis Descriptor and Add Units](open/low_auto-resolve-plot-param-x-axis-descriptor-and-add-units.md) | `[priority] low` | UX | +| 87 | [Redesign Tutorial Grouping and Categorisation](open/low_redesign-tutorial-grouping-and-categorisation.md) | `[priority] low` | Documentation / UX | +| 90 | [Show Experiment Number/Total During Sequential Fitting](open/low_show-experiment-number-total-during-sequential-fitting.md) | `[priority] low` | UX | +| 92 | [Make `save()` Respect Verbosity Settings](open/low_make-save-respect-verbosity-settings.md) | `[priority] low` | UX | +| 94 | [Revisit Powder `refln` Phase Labels and Row IDs](open/low_revisit-powder-refln-phase-labels-and-row-ids.md) | `[priority] low` | Naming / CIF UX | +| 102 | [Drop Compute-and-Ignore `result_kind` Validation in CIF Restore](open/low_drop-compute-and-ignore-result-kind-validation-in-cif-restore.md) | `[priority] low` | Dead code / clarity **Source:** Review 8 finding F7. | +| 104 | [Tighten `FitParameterItem.posterior_summary` NaN Behaviour](open/low_tighten-fitparameteritem-posterior-summary-nan-behaviour.md) | `[priority] low` | Robustness / partial-data edge case **Source:** Review 8 | +| 106 | [Document `FitResultBase.result_kind` Default Rationale](open/low_document-fitresultbase-result-kind-default-rationale.md) | `[priority] low` | Code readability **Source:** `minimizer-input-output-split` | +| 108 | [Smarter Automatic Bond Detection (Near-Neighbour Analysis)](open/low_smarter-automatic-bond-detection-near-neighbour-analysis.md) | `[priority] low` | UX / Visualization | +| 109 | [Let More Tables Adapt to Terminal Width](open/low_let-more-tables-adapt-to-terminal-width.md) | `[priority] low` | UX / Display | +| 110 | [Render Styled Multi-Line Table Cells in the HTML Backend](open/low_render-styled-multi-line-table-cells-in-the-html-backend.md) | `[priority] low` | Display / Notebook parity | +| 111 | [Add Test Coverage for `list_tutorials` Two-Line Rendering](open/low_add-test-coverage-for-list-tutorials-two-line-rendering.md) | `[priority] low` | Test coverage | +| 112 | [Suppress the Redundant Row-Index Column in Tables](open/low_suppress-the-redundant-row-index-column-in-tables.md) | `[priority] low` | Display / UX | +| 114 | [External Link Checking in the Docs Gate](open/low_external-link-checking-in-the-docs-gate.md) | `[priority] low` | CI / Documentation | +| 115 | [Expand Cross-Engine Verification Coverage](open/low_expand-cross-engine-verification-coverage.md) | `[priority] low` | Test coverage / Documentation | +| 125 | [Decide Whether Sequential Extraction Should Be Cached](open/low_decide-whether-sequential-extraction-should-be-cached.md) | `[priority] low` | Performance | +| 126 | [Decide How Mid-Run Sequential Failures Persist](open/low_decide-how-mid-run-sequential-failures-persist.md) | `[priority] low` | Recovery design | +| 127 | [Decide Whether CLI Should Override Extract Rules](open/low_decide-whether-cli-should-override-extract-rules.md) | `[priority] low` | CLI design | +| 128 | [Align `dir()` With Help Filtering](open/low_align-dir-with-help-filtering.md) | `[priority] low` | Discoverability | +| 129 | [Decide Whether `single_fit` Needs a Future Category](open/low_decide-whether-single-fit-needs-a-future-category.md) | `[priority] low` | Scope planning | +| 132 | [Decide Future of `show_residual` in `plot_meas_vs_calc`](open/low_decide-future-of-show-residual-in-plot-meas-vs-calc.md) | `[priority] low` | API cleanup | +| 136 | [Draw ADP Ellipsoids for Beta-Tensor Atoms](open/low_draw-adp-ellipsoids-for-beta-tensor-atoms.md) | `[priority] low` | Display / Visualization | +| 152 | [`help()` Mislabels Boolean Descriptors as "numeric"](open/low_help-mislabels-boolean-descriptors-as-numeric.md) | `[priority] low` | API safety / UX | +| 153 | [`value` Setter Re-Validates on Every NaN Assignment](open/low_value-setter-re-validates-on-every-nan-assignment.md) | `[priority] low` | Performance | +| 154 | [Remove Dead Auto-Populate Branch in `_run_joint`](open/low_remove-dead-auto-populate-branch-in-run-joint.md) | `[priority] low` | Dead code | +| 155 | [Fix Reversed Abstract `_sync_result_to_parameters` Signature](open/low_fix-reversed-abstract-sync-result-to-parameters-signature.md) | `[priority] low` | Maintainability | +| 156 | [Size `chapter()` Divider From the Live Console Width](open/low_size-chapter-divider-from-the-live-console-width.md) | `[priority] low` | Robustness / Display | +| 157 | [Add Public API to Clear a Project Path (CLI `fit --dry`)](open/low_add-public-api-to-clear-a-project-path-cli-fit-dry.md) | `[priority] low` | API safety | +| 158 | [Remove Stale Commented-Out / Dead Code in `core/` and `io/`](open/low_remove-stale-commented-out-dead-code-in-core-and-io.md) | `[priority] low` | Dead code | +| 159 | [Narrow Defensive getattr-Chain in Aniso ADP Unit Resolution](open/low_narrow-defensive-getattr-chain-in-aniso-adp-unit-resolution.md) | `[priority] low` | Maintainability | +| 160 | [Replace No-Op `assert True` in `test_logging.py`](open/low_replace-no-op-assert-true-in-test-logging-py.md) | `[priority] low` | Test coverage | +| 161 | [Add Boundary Tests for `verification.py` FullProf/IGOR Parsers](open/low_add-boundary-tests-for-verification-py-fullprof-igor-parsers.md) | `[priority] low` | Test coverage | +| 163 | [Fix `.gitignore` Gaps and Remove the Stale `absorption/` Package](open/low_fix-gitignore-gaps-and-remove-the-stale-absorption-package.md) | `[priority] low` | Hygiene | +| 165 | [cryspy Backend Hardcodes `flag_only_nuclear` (No Magnetic Structures)](open/low_cryspy-backend-hardcodes-flag-only-nuclear-no-magnetic-structures.md) | `[priority] low` | Engine limitation | +| 88 | [Fix Dataset 26 Description (47 Files, Not 57)](open/lowest_fix-dataset-26-description-47-files-not-57.md) | `[priority] lowest` | Data | +| 91 | [Disable TODO Comment Checks in CodeFactor PRs](open/lowest_disable-todo-comment-checks-in-codefactor-prs.md) | `[priority] lowest` | CI / Tooling | +| 117 | [Live-Notebook Plotly Delivery: Loader vs Native Mimetype](open/lowest_live-notebook-plotly-delivery-loader-vs-native-mimetype.md) | `[priority] lowest` | Display / Architecture | +| 118 | [Plotly Figures Show Empty Rows in the VISA JupyterLab](open/lowest_plotly-figures-show-empty-rows-in-the-visa-jupyterlab.md) | `[priority] lowest` | Display / Environment | + +## Closed Issues + +| # | Issue | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 3 | [Rebuild Joint-Fit Weights on Every Fit](closed/rebuild-joint-fit-weights-on-every-fit.md) | +| 16 | [Add Serial Pattern-Generation Benchmarks](closed/add-serial-pattern-generation-benchmarks.md) | +| 24 | [Process Default Values on Experiment Creation](closed/process-default-values-on-experiment-creation.md) | +| 43 | [Fix Summary Display Inconsistencies](closed/fix-summary-display-inconsistencies.md) | +| 51 | [Access Space Group from `AtomSites` for Wyckoff Letters](closed/access-space-group-from-atomsites-for-wyckoff-letters.md) | +| 56 | [Make ASCII Plot Width Configurable](closed/make-ascii-plot-width-configurable.md) | +| 58 | [Move `as_cif` / `show_as_cif` from `ProjectInfo` to `io.cif.serialize`](closed/move-as-cif-show-as-cif-from-projectinfo-to-io-cif-serialize.md) | +| 72 | [Warn on All Switchable-Category Type Changes](closed/warn-on-all-switchable-category-type-changes.md) | +| 76 | [Consistent `_type` Suffix in Switchable-Category API Names](closed/consistent-type-suffix-in-switchable-category-api-names.md) | +| 77 | [Add Help Methods to Public Discovery Facades](closed/add-help-methods-to-public-discovery-facades.md) | +| 78 | [Add `SEQUENTIAL` to `FitModeEnum` and Show Methods to Analysis](closed/add-sequential-to-fitmodeenum-and-show-methods-to-analysis.md) | +| 84 | [Serialise `None` as `.` in CIF Output](closed/serialise-none-as-in-cif-output.md) | +| 100 | [Collapse Duplicate Predictive-Cache-Key Helpers](closed/collapse-duplicate-predictive-cache-key-helpers.md) | +| 101 | [Remove Dead Branch in `_fit_state_categories`](closed/remove-dead-branch-in-fit-state-categories.md) | +| 103 | [Make `_sync_engine_from_minimizer_category` Skip-Keys Declarative](closed/make-sync-engine-from-minimizer-category-skip-keys-declarative.md) | +| 105 | [Remove Orphaned Fit-Result Reset Helper](closed/remove-orphaned-fit-result-reset-helper.md) | +| 137 | [CLI `fit` Command Never Saves Results to Disk](closed/cli-fit-command-never-saves-results-to-disk.md) | +| 164 | [Reconcile Git-Ignored `AGENTS.md` / `CLAUDE.md` With Their Checked-In Role](closed/reconcile-git-ignored-agents-md-claude-md-with-their-checked-in-role.md) | diff --git a/docs/dev/issues/open.md b/docs/dev/issues/open.md deleted file mode 100644 index e3f101022..000000000 --- a/docs/dev/issues/open.md +++ /dev/null @@ -1,2443 +0,0 @@ -# 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 -[`adrs/index.md`](../adrs/index.md) or the relevant ADR if needed. - -**Legend:** 🔴 High · 🟡 Medium · 🟢 Low - ---- - -## 119. 🟡 Model Sample Absorption (Debye–Scherrer, μR) - -**Type:** Physics / Engine feature - -The calculators (`cryspy`, `crysfml`) apply no sample-absorption -correction. For a cylindrical sample in Debye–Scherrer geometry this is -an angle-dependent intensity factor that boosts high-angle peaks. The -LaB₆ verification reference (`pd-neut-cwl_tch-fcj_lab6`) was refined in -FullProf with `μR = 0.7`; the unmodelled correction is the _entire_ -intensity residual on the companion `pd-neut-cwl_tch-fcj_abs_lab6` page -(≈5% profile difference), while the `μR = 0` page passes to corr 0.9999. - -**Correction (Hewat, Debye–Scherrer), validated to 4 decimals against -FullProf output:** - -``` -A(θ) = exp( -(1.7133 − 0.0368·sin²θ)·μR + (0.0927 + 0.375·sin²θ)·μR² ) -``` - -A Lobanov–Alte-da-Veiga form covers `μR > 3`. - -**Design:** captured in -[`adrs/accepted/model-sample-absorption.md`](../adrs/accepted/model-sample-absorption.md) -— a switchable `experiment.absorption` category (mirroring `extinction`) -with a calculator-independent A(θ) envelope. - -**What the backends actually provide (corrected):** - -- `cryspy`: **no** absorption code at all (only Debye–Waller and sphere - _extinction_); its CW intensity loop has no slot to multiply A(θ). -- `crysfml`: CrysFML08 implements `Lorentz_abs_CW` in Fortran, but it is - **not** wrapped in `PythonAPI/`, and the high-level - `cw_powder_pattern_from_dict` path we call applies a plain Lorentz - factor with no absorption. So it is **not** reachable through - pycrysfml today without upstream changes. - -**Implication:** neither backend can apply the correction internally -without changes we do not own. The chosen approach computes A(θ) in -EasyDiffraction and applies it as a pointwise envelope on the calculated -pattern, identically for both calculators (see the ADR). - -**Note:** absorption is nearly degenerate with Biso + scale (its angle -term is linear in `sin²θ`, like the Debye–Waller), so refining Biso can -partly absorb it — but that biases Biso, so an explicit correction is -preferable. In FullProf `μR` is normally **fixed**, not refined. - -**References:** - -- A. W. Hewat, _Acta Cryst._ A35 (1979) 248 — cylindrical absorption. -- N. N. Lobanov & L. Alte da Veiga, 6th EPDIC, Abstract P12-16 (1998). -- CrysFML08: - [`Src/CFML_Powder/Pow_Lorentz_Absorption.f90`](https://code.ill.fr/scientific-software/CrysFML2008/-/blob/master/Src/CFML_Powder/Pow_Lorentz_Absorption.f90), - `Lorentz_abs_CW`. -- FullProf splits absorption into a refineable **magnitude** and a - **type**: CW uses `μR` on the `.pcr` Lambda line (fixed there — no - refinement codeword), with the cylindrical Hewat form implied; TOF - uses `Iabscor` (`1` flat plate, `2` cylinder, `3` exponential - `exp(−ABS·λᶜ)`). `Cthm`/`Rpolarz`/`2nd-muR` on the Lambda line are - polarization and container terms, not the primary absorption knob. - -**Depends on:** the switchable `experiment.absorption` category in the -ADR above (supersedes the earlier "add a `μR` instrument parameter" -sketch). - ---- - -## 3. 🟡 Rebuild Joint-Fit Weights on Every Fit - -**Type:** Fragility - -`joint_fit` 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` at the start of every joint -fit. At minimum, `fit()` should assert that the weight keys exactly -match `project.experiments.names`. - -**Depends on:** nothing. - ---- - -## 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 the CategoryOwner migration. - ---- - -## 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. - ---- - -## 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. - ---- - -## 120. 🟡 Decide Whether Inactive Fit-Mode Categories Stay Lenient - -**Type:** API design - -`Analysis` currently allows direct access to inactive mode-specific -categories such as `joint_fit` or `sequential_fit`. The values remain -editable, but inactive sections are hidden from help and dropped during -serialization. - -**Fix:** confirm whether this lenient access is the long-term contract, -or replace it with a dedicated mode error to prevent silent state loss -on save. - -**Depends on:** nothing. - ---- - -## 121. 🟡 Clarify `joint_fit` Lifecycle Outside Execution - -**Type:** Fragility - -`joint_fit` is validated and auto-populated at `fit()` time, but it does -not react when experiments are later renamed or removed. - -**Fix:** decide whether `joint_fit` should stay passive until execution, -or listen for experiment lifecycle changes and prune or warn earlier. - -**Depends on:** nothing. - ---- - -## 122. 🟡 Define `joint_fit.weight` Bounds - -**Type:** Data model - -Joint-fit rows currently allow any non-negative weight, but the public -contract is still unclear about whether `0` means exclusion and whether -an upper bound should exist. - -**Fix:** define the supported range and validator semantics for -`joint_fit.weight`. - -**Depends on:** nothing. - ---- - -## 123. 🟡 Define `sequential_fit_extract` Target Scope - -**Type:** Data model - -Sequential extract rules currently target one numeric descriptor under -`experiment.diffrn`. Open questions remain around nested targets, -duplicate rules writing the same target, and how additional supported -prefixes should be introduced when new environment categories appear. - -**Fix:** pin the allowed target grammar and duplicate-target behaviour -in an ADR and validation rules. - -**Depends on:** nothing. - ---- - -## 124. 🟡 Decide Sequential Extraction Failure Policy - -**Type:** Runtime behaviour - -Today a failed required extract rule marks that file as failed and the -run continues. The overall aggregation policy is still undefined. - -**Fix:** decide whether one failed file should abort the whole run, -remain an isolated row-level failure, or count toward a configurable -failure threshold. - -**Depends on:** nothing. - ---- - -## 125. 🟢 Decide Whether Sequential Extraction Should Be Cached - -**Type:** Performance - -Sequential metadata extraction currently re-reads input files when the -run is repeated or resumed. - -**Fix:** decide whether extracted `diffrn.*` values should be cached in -`analysis/results.csv` only, or also in a dedicated reusable cache. - -**Depends on:** nothing. - ---- - -## 126. 🟢 Decide How Mid-Run Sequential Failures Persist - -**Type:** Recovery design - -If a sequential fit fails partway through, the recovery and persistence -contract for `analysis/results.csv` is not fully specified. - -**Fix:** define whether partial CSV output is authoritative for resume, -left untouched for manual recovery, or replaced on the next run. - -**Depends on:** nothing. - ---- - -## 127. 🟢 Decide Whether CLI Should Override Extract Rules - -**Type:** CLI design - -The CLI can override mode and worker settings, but persisted -`sequential_fit_extract` rules are not yet overridable from the command -line. - -**Fix:** decide whether extraction rules stay project-file-only or gain -an explicit CLI override syntax. - -**Depends on:** nothing. - ---- - -## 128. 🟢 Align `dir()` With Help Filtering - -**Type:** Discoverability - -`help()` now hides inactive analysis categories by fitting mode, while -`dir()` and tab completion still expose the full class surface. - -**Fix:** decide whether `dir()` should mirror the help filter or remain -an always-complete developer surface. - -**Depends on:** nothing. - ---- - -## 129. 🟢 Decide Whether `single_fit` Needs a Future Category - -**Type:** Scope planning - -Single mode currently has no dedicated persisted category. Future -single-mode settings could require one, but the threshold is not yet -defined. - -**Fix:** decide what concrete single-mode behaviour would justify a -`single_fit` category instead of keeping the mode configuration on the -owner only. - -**Depends on:** nothing. - ---- - -## 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. 🟢 Add Serial Pattern-Generation Benchmarks - -**Type:** Performance - -The dev environment previously installed `pytest-benchmark`, but the -repository does not currently define any benchmark tests. At the same -time, the integration, script, and notebook pytest tasks all run with -`pytest-xdist`, so benchmark plugins only add warning noise and do not -provide reliable performance regression coverage. - -Performance regressions are still worth tracking, especially for single -diffraction-pattern calculation where backend or profile changes can -quietly slow interactive workflows. - -**Fix:** add a dedicated serial benchmark task outside the normal -parallel pytest suite. Benchmark representative single-pattern -calculations on fixed datasets and calculators, run without `-n auto`, -and define regression thresholds only after measurements are stable on a -controlled runner. - -**Depends on:** nothing. - ---- - -## 17. 🟢 Use PDF-Specific CIF Names for Total Scattering - -**Type:** Naming - -The `TotalPdDataPoint` class reuses Bragg powder CIF tag names (e.g. -`_pd_data.point_id`, `_pd_proc.r`, `_pd_meas.intensity_total`) as -placeholders. These should be replaced with proper total-scattering / -PDF-specific CIF names. - -**TODOs:** - -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L48) - — `_pd_data.point_id` -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L62) - — `_pd_proc.r` -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L74) - — `_pd_meas.intensity_total` -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L87) - — `_pd_meas.intensity_total_su` -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L99) - — `_pd_calc.intensity_total` -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L112) - — `_pd_data.refinement_status` - -**Depends on:** nothing. - ---- - -## 18. 🟢 Move CIF v2→v1 Conversion Out of Calculator - -**Type:** Maintainability - -`PdffitCalculator.calculate_pattern` contains inline CIF v2→v1 -conversion (dot-to-underscore rewriting). This should live in a shared -`io` module. - -**TODOs:** - -- [pdffit.py](src/easydiffraction/analysis/calculators/pdffit.py#L118) - -**Depends on:** nothing. - ---- - -## 19. 🟢 Add Debug-Mode Logging for Calculator Imports - -**Type:** Diagnostics - -Several calculator modules have commented-out print statements for -import success/failure. These should be wired into the logging system -under a debug level. - -**TODOs:** - -- [pdffit.py](src/easydiffraction/analysis/calculators/pdffit.py#L34) -- [pdffit.py](src/easydiffraction/analysis/calculators/pdffit.py#L37) -- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L19) -- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L23) -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L25) -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L28) - -**Depends on:** nothing. - ---- - -## 20. 🟢 Redirect or Suppress CrysPy stderr Warnings - -**Type:** UX - -CrysPy emits warnings to stderr during pattern calculation. The code has -TODO markers to redirect these. - -**TODOs:** - -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L112) -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L184) - -**Depends on:** nothing. - ---- - -## 21. 🟡 Clarify CrysPy TOF Background CIF Tag Names - -**Type:** Correctness / Naming - -The CrysPy calculator uses TOF background CIF tags -(`_tof_backgroundpoint_time`, `_tof_backgroundpoint_intensity`) and -hardcoded `0.0` intensity values marked with `TODO: !!!!????`. The -mapping and the hardcoded defaults need verification. - -**TODOs:** - -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L734) -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L735) -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L738) -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L739) - -**Depends on:** nothing. - ---- - -## 130. 🟡 cryspy Diverges on TOF Jorgensen–Von Dreele Lorentzian - -**Type:** Correctness - -For time-of-flight powder data using the Jorgensen–Von Dreele peak -profile, the `cryspy` backend diverges from FullProf and `crysfml` -whenever the Lorentzian term (`broad_lorentz_gamma_*`) is non-zero. On -the Si Verification reference case the profile difference reaches ≈22% -with an integrated-intensity ratio ≈0.72–0.76, while `crysfml` matches -FullProf to <1%. When the Lorentzian term is zero (NaCaAlF) `cryspy` -agrees to <1%, which localises the problem to the cryspy translation of -the pseudo-Voigt (Gaussian ⊗ Lorentzian) mixing for TOF. - -**Fix:** verify how `broad_lorentz_gamma_*` is passed to cryspy for the -`jorgensen-von-dreele` profile and reconcile the convention with -crysfml/FullProf. - -**Visible on:** the Si TOF Verification page (`pd-neut-tof_jvd_si`), -whose closeness table flags the `cryspy` rows in red — reported via a -non-raising agreement check, not enforced, so CI stays green. -Re-introduce a strict check, or skip the page via -`docs/docs/verification/ci_skip.txt`, once work on the cryspy backend -begins. - -**Depends on:** nothing. - ---- - -## 131. 🟡 Add SyCos/SySin Systematic Peak-Position Corrections - -**Type:** Feature / Experiment model - -FullProf models systematic peak-position aberrations with `SyCos` -(sample displacement) and `SySin` (transparency), shifting peaks as a -function of angle on top of the `Zero` offset. EasyDiffraction has no -category for these, so it cannot reproduce datasets that use them. The -cryspy side is implemented in -[cryspy PR #46](https://github.com/ikibalin/cryspy/pull/46) (see -[issue #38](https://github.com/ikibalin/cryspy/issues/38)); the -EasyDiffraction side — an instrument-category parameter pair plus the -calculator wiring — is still to do. - -A prepared verification page, -`docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.py`, uses the issue #38 -LaB6 dataset and is skipped via `ci_skip.txt`. Finishing it also needs a -custom ¹¹B scattering length, the Thompson–Cox–Hastings profile, and a -FullProf-style polynomial background, which that dataset relies on. - -**Fix:** add `SyCos`/`SySin` to the CWL instrument category, pass them -to the calculators, then un-skip the LaB6 page. - -**Depends on:** nothing. - ---- - -## 22. 🟢 Check CrysPy Single-Crystal Instrument Mapping - -**Type:** Correctness - -`_cif_instrument_section` uses an empty `instrument_mapping` dict for -single crystal and a `TODO: Check this mapping!` marker. - -**TODOs:** - -- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L506) - -**Depends on:** nothing. - ---- - -## 23. 🟢 Investigate PyCrysFML Pattern Length Discrepancy - -**Type:** Correctness - -CrysFML calculator adjusts pattern length post-calculation with a TODO -asking to investigate the origin of the off-by-one discrepancy. The same -epsilon workaround appears in the dict builder. - -**TODOs:** - -- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L124) -- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L253) - -**Depends on:** nothing. - ---- - -## 24. 🟢 Process Default Values on Experiment Creation - -**Type:** Design - -Default instrument/peak values for the CrysFML dict are filled in at -calculation time with inline fallbacks rather than being set at -experiment creation. - -**TODOs:** - -- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L233) - -**Depends on:** nothing. - ---- - -## 25. 🟡 Refactor Data `_update` Methods (Split and Unify) - -**Type:** Maintainability - -Multiple `_update` helpers in Bragg PD, Bragg SC, and Total PD data -classes have `TODO: split into multiple methods` or -`TODO: refactor _get_valid_linked_phases` markers. The update logic -should be decomposed and the `_get_valid_linked_phases` responsibility -should be narrowed. The Total PD and Bragg PD classes should also adapt -the pattern from `bragg_sc.py`. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L386) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L389) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L506) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L585) -- [bragg_sc.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py#L271) -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L254) -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L257) -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L349) - -**Depends on:** nothing. - ---- - -## 26. 🟢 Clarify `dtype` Usage in Data Point Arrays - -**Type:** Cleanup - -Many array constructions pass `dtype=float` or `dtype=object` with a -`TODO: needed? DataTypes.NUMERIC?` comment. Decide whether explicit -dtype is needed and align with `DataTypes`. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L415) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L423) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L431) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L456) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L466) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L474) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L543) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L556) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L624) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L637) -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L370) -- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L378) - -**Depends on:** nothing. - ---- - -## 27. 🟢 Handle Zero Uncertainty in Bragg PD Data - -**Type:** Correctness - -A temporary workaround exists for zero uncertainties in measured data. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L442) - -**Depends on:** nothing. - ---- - -## 28. 🟢 Clarify Bragg PD Data Collection Description - -**Type:** Cleanup - -`PdCwlDataCollection` has a commented-out `_description` and a -`TODO: ???` marker. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L482) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L304) - -**Depends on:** nothing. - ---- - -## 29. 🟡 Standardise CIF ID Validator Pattern Across Categories - -**Type:** Consistency - -Multiple category item classes use the same regex `r'^[A-Za-z0-9_]*$'` -for their id/label validators with an identical TODO about CIF label vs. -internal label conversion. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L43) -- [bragg_sc.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py#L39) -- [chebyshev.py](src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py#L52) -- [line_segment.py](src/easydiffraction/datablocks/experiment/categories/background/line_segment.py#L45) -- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L39) -- [default.py](src/easydiffraction/datablocks/structure/categories/atom_sites/default.py#L45) - -**Depends on:** nothing. - ---- - -## 30. 🟢 Make `refinement_status` Default an Enum - -**Type:** Design - -`bragg_pd.py` uses `default='incl'` as a raw string with a TODO to make -it an Enum. The `_pd_data.refinement_status` CIF name should also be -renamed to `calc_status`. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L113) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L118) - -**Depends on:** nothing. - ---- - -## 31. 🟢 Rename PD Data Point Mixins - -**Type:** Naming - -Mixin classes `PdDataPointBaseMixin` and `PdCwlDataPointMixin` have TODO -markers suggesting a rename to `BasePdDataPointMixin` and -`CwlPdDataPointMixin` for consistency. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L268) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L269) -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L271) - -**Depends on:** nothing. - ---- - -## 32. 🟡 Move Common Methods to `DatablockCollection` Base Class - -**Type:** Maintainability - -Both `Experiments` and `Structures` collections duplicate methods -(`from_cif_str`, `from_cif_file`, `show`, `show_as_cif`, etc.) that -could live in the base `DatablockCollection`. - -**TODOs:** - -- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L29) - — `Make abstract in DatablockCollection?` -- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L65) - — `Move to DatablockCollection?` -- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L82) -- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L145) -- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L151) -- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L30) -- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L48) -- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L65) -- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L82) -- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L88) - -**Depends on:** nothing. - ---- - -## 33. 🟡 Make `DatablockItem._update_categories` Abstract - -**Type:** Design - -`DatablockItem._update_categories` has a TODO to make it abstract and -implement it in subclasses for structures (symmetry + constraints) and -experiments (calculation updates). Currently it is a concrete no-op. - -**TODOs:** - -- [datablock.py](src/easydiffraction/core/datablock.py#L39) - -**Depends on:** related to issue 11. - ---- - -## 34. 🟢 Auto-Extract `PeakProfileTypeEnum` from Peak Classes - -**Type:** Design - -Three related TODOs in `enums.py` ask whether `PeakProfileTypeEnum` -values can be auto-extracted from the actual peak profile classes in -`peak/cwl.py`, `tof.py`, `total.py` instead of being hardcoded, and -whether the same pattern can be reused for other enums. - -**TODOs:** - -- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L153) -- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L157) -- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L158) - -**Depends on:** related to issue 9. - ---- - -## 35. 🟢 Rename `BeamModeEnum` Members to CWL/TOF - -**Type:** Naming - -`BeamModeEnum.CONSTANT_WAVELENGTH` and `TIME_OF_FLIGHT` have a TODO to -be renamed to `CWL` and `TOF`. - -**TODOs:** - -- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L113) - -**Depends on:** nothing. - ---- - -## 36. 🟢 Consider a Common `EnumBase` with `default()` / `description()` - -**Type:** Design - -`BackgroundTypeEnum` and other enums repeat the same `default()` / -`description()` method pattern. A shared `EnumBase` would reduce -boilerplate. - -**TODOs:** - -- [enums.py](src/easydiffraction/datablocks/experiment/categories/background/enums.py#L10) - -**Depends on:** related to issue 9. - ---- - -## 37. 🟢 Rename Experiment `.type` Property - -**Type:** Naming - -`ExperimentBase.type` returns experimental metadata but the name shadows -the built-in `type`. A TODO suggests finding a better name. - -**TODOs:** - -- [base.py](src/easydiffraction/datablocks/experiment/item/base.py#L75) - -**Depends on:** nothing. - ---- - -## 38. 🟡 Fix `@typechecked` / gemmi Interaction in Factories - -**Type:** Bug - -Both `StructureFactory` and `ExperimentFactory` have `from_cif_str` -methods where `@typechecked` is commented out because it "fails to find -gemmi". They also share TODOs about adding minimal default configuration -for missing parameters and reading content from files. - -**TODOs:** - -- [factory.py](src/easydiffraction/datablocks/structure/item/factory.py#L41) -- [factory.py](src/easydiffraction/datablocks/structure/item/factory.py#L91) -- [factory.py](src/easydiffraction/datablocks/structure/item/factory.py#L115) -- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L59) - — `Add to core/factory.py?` -- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L108) -- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L177) -- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L201) - -**Depends on:** nothing. - ---- - -## 39. 🟢 Improve `_update_priority` Handling in Categories - -**Type:** Design - -`CategoryItem` and `CategoryCollection` both define -`_update_priority = 10` with a TODO to set different defaults and use -them during CIF serialisation. The duplicated `_update` no-op methods -are also marked. - -**TODOs:** - -- [category.py](src/easydiffraction/core/category.py#L21) -- [category.py](src/easydiffraction/core/category.py#L23) -- [category.py](src/easydiffraction/core/category.py#L32) -- [category.py](src/easydiffraction/core/category.py#L174) -- [category.py](src/easydiffraction/core/category.py#L199) - -**Depends on:** related to issues 10, 11. - ---- - -## 132. 🟢 Decide Future of `show_residual` in `plot_meas_vs_calc` - -**Type:** API cleanup - -Powder Bragg plots now show the residual row by default when -`show_residual=None`, but the public `show_residual` argument still -exists and some call sites still pass `show_residual=True` explicitly. -The API should be clarified: either keep the argument as a compatibility -option, remove it, or standardize a single meaning across powder and -single-crystal plots. - -**TODOs:** - -- [plotting.py](src/easydiffraction/display/plotting.py#L459) -- [\_\_main\_\_.py](src/easydiffraction/__main__.py#L105) - -**Depends on:** nothing. - ---- - -## 94. 🟢 Revisit Powder `refln` Phase Labels and Row IDs - -**Type:** Naming / CIF UX - -The implemented powder reflection category uses `phase_id` throughout -(`experiment.refln`, `PowderReflnRecord`, Bragg tick labels) and assigns -global sequential row ids. This matches the current implementation, but -the archived planning notes left two follow-up questions open: - -1. whether `structure_id` would be clearer than `phase_id` in the public - API / CIF output, and -2. whether phase-prefixed row ids would make CIF inspection and - debugging easier than simple `1`, `2`, `3`, ... - -**TODOs:** - -- [refln_pd.py](src/easydiffraction/datablocks/experiment/categories/data/refln_pd.py#L37) -- [refln_pd.py](src/easydiffraction/datablocks/experiment/categories/data/refln_pd.py#L154) -- [base.py](src/easydiffraction/display/plotters/base.py#L24) - -**Depends on:** nothing. - ---- - -## 40. 🟢 Implement Resetting `.user_constrained` to `False` - -**Type:** Feature - -`ConstraintsHandler` has a TODO to implement changing the -`.user_constrained` attribute back to `False` when constraints are -removed. - -**TODOs:** - -- [singleton.py](src/easydiffraction/core/singleton.py#L37) - -**Depends on:** nothing. - ---- - -## 41. 🟢 Check Whether `_mark_dirty` in `_set_value` is Actually Used - -**Type:** Cleanup - -`GenericDescriptorBase._set_value` marks the parent datablock dirty with -a TODO questioning whether this path is exercised. - -**TODOs:** - -- [variable.py](src/easydiffraction/core/variable.py#L154) - -**Depends on:** nothing. - ---- - -## 42. 🟢 MkDocs Doesn't Unpack Types in Validation Module - -**Type:** Docs - -A TODO in `validation.py` notes that MkDocs doesn't unpack types -properly. - -**TODOs:** - -- [validation.py](src/easydiffraction/core/validation.py#L25) - -**Depends on:** nothing. - ---- - -## 43. 🟢 Fix Summary Display Inconsistencies - -**Type:** UX - -The summary module has TODOs about fixing description wrapping and -inconsistent header capitalisation. - -**TODOs:** - -- [summary.py](src/easydiffraction/summary/summary.py#L52) -- [summary.py](src/easydiffraction/summary/summary.py#L164) - -**Depends on:** nothing. - ---- - -## 44. 🟢 Merge Parameter Record Construction in Analysis - -**Type:** Cleanup - -`Analysis._params_to_dataframe` has TODOs to merge record construction -for `StringDescriptor`/`NumericDescriptor`/`Parameter` and to use `repr` -formatting for `StringDescriptor` values. - -**TODOs:** - -- [analysis.py](src/easydiffraction/analysis/analysis.py#L461) -- [analysis.py](src/easydiffraction/analysis/analysis.py#L462) - -**Depends on:** nothing. - ---- - -## 45. 🟢 Decide Default for Alias/Constraint Descriptors - -**Type:** Design - -`Aliases` and `Constraints` categories use `default='_'` with a -`TODO, Maybe None?` marker. - -**TODOs:** - -- [default.py](src/easydiffraction/analysis/categories/aliases/default.py#L40) -- [default.py](src/easydiffraction/analysis/categories/constraints/default.py#L33) - -**Depends on:** nothing. - ---- - -## 46. 🟢 Improve `JointFitItem` Descriptions - -**Type:** Naming - -`JointFitItem` uses `name='experiment_id'`, but two description fields -are still incomplete. - -**TODOs:** - -- [default.py](src/easydiffraction/analysis/categories/joint_fit/default.py#L31) -- [default.py](src/easydiffraction/analysis/categories/joint_fit/default.py#L32) -- [default.py](src/easydiffraction/analysis/categories/joint_fit/default.py#L41) - -**Depends on:** nothing. - ---- - -## 47. 🟢 Improve Error Handling in Crystallography Utilities - -**Type:** Diagnostics - -`crystallography.py` logs errors with a TODO asking whether these should -raise `ValueError` or provide better diagnostics. - -**TODOs:** - -- [crystallography.py](src/easydiffraction/crystallography/crystallography.py#L39) -- [crystallography.py](src/easydiffraction/crystallography/crystallography.py#L45) -- [crystallography.py](src/easydiffraction/crystallography/crystallography.py#L84) - -**Depends on:** nothing. - ---- - -## 48. 🟢 Fix CrysPy TOF Instrument Default - -**Type:** Bug workaround - -`TofInstrument.calib_d_to_tof_quad` defaults to `-0.00001` because -CrysPy does not accept `0`. - -**TODOs:** - -- [tof.py](src/easydiffraction/datablocks/experiment/categories/instrument/tof.py#L95) - -**Depends on:** upstream CrysPy fix. - ---- - -## 49. 🟢 Automate Space Group CIF Name Variants - -**Type:** Maintainability - -`SpaceGroup.name_h_m` lists multiple CIF tag variants (with `.` and -`_`). A TODO asks to keep only the dotted version and automate variant -generation. - -**TODOs:** - -- [default.py](src/easydiffraction/datablocks/structure/categories/space_group/default.py#L52) - -**Depends on:** nothing. - ---- - -## 50. 🟢 Clarify `Cell._update` Usage of `called_by_minimizer` - -**Type:** Cleanup - -`Cell._update` deletes `called_by_minimizer` with a `TODO: ???`. - -**TODOs:** - -- [default.py](src/easydiffraction/datablocks/structure/categories/cell/default.py#L146) - -**Depends on:** related to issue 11. - ---- - -## 52. 🟢 Rename Line-Segment Background `y` to `intensity` - -**Type:** Naming - -`LineSegmentBackgroundPoint.y` has TODOs to rename to `intensity`. - -**TODOs:** - -- [line_segment.py](src/easydiffraction/datablocks/experiment/categories/background/line_segment.py#L67) -- [line_segment.py](src/easydiffraction/datablocks/experiment/categories/background/line_segment.py#L72) - -**Depends on:** nothing. - ---- - -## 53. 🟢 Move `show()` to `CategoryCollection` Base Class - -**Type:** Maintainability - -`ExcludedRegions.show()` and `BackgroundBase.show()` duplicate table- -rendering logic. The TODO suggests moving it to the base class. - -**TODOs:** - -- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L166) -- [base.py](src/easydiffraction/datablocks/experiment/categories/background/base.py#L19) - -**Depends on:** nothing. - ---- - -## 54. 🟢 Add `point_id` to Excluded Regions - -**Type:** Completeness - -`ExcludedRegion` has a TODO to add `point_id` similar to background -categories. - -**TODOs:** - -- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L33) - -**Depends on:** nothing. - ---- - -## 55. 🟢 Fix Jupyter Scroll Disabling for MkDocs - -**Type:** Docs / UX - -`display/__init__.py` has disabled `JupyterScrollManager` because it -breaks MkDocs builds. - -**TODOs:** - -- [**init**.py](src/easydiffraction/display/__init__.py#L15) - -**Depends on:** nothing. - ---- - -## 56. 🟢 Make ASCII Plot Width Configurable - -**Type:** UX - -`ascii.py` hardcodes `width = 60` with a TODO to make it configurable. - -**TODOs:** - -- [ascii.py](src/easydiffraction/display/plotters/ascii.py#L144) -- [ascii.py](src/easydiffraction/display/plotters/ascii.py#L98) - -**Depends on:** nothing. - ---- - -## 57. 🟢 Clean Up CIF Deserialisation Helpers - -**Type:** Maintainability - -`serialize.py` has several TODOs: verify methods after the -`format_param_value` section, extract a helper for quoted-string -stripping, find a better way to set `_item_type` on -`CategoryCollection`, rename it to `_item_cls`, and remove duplicated -`param_from_cif` logic. - -**TODOs:** - -- [serialize.py](src/easydiffraction/io/cif/serialize.py#L454) -- [serialize.py](src/easydiffraction/io/cif/serialize.py#L562) -- [serialize.py](src/easydiffraction/io/cif/serialize.py#L617) -- [serialize.py](src/easydiffraction/io/cif/serialize.py#L619) -- [serialize.py](src/easydiffraction/io/cif/serialize.py#L656) - -**Depends on:** nothing. - ---- - -## 58. 🟢 Move `as_cif` / `show_as_cif` from `ProjectInfo` to `io.cif.serialize` - -**Type:** Maintainability - -`ProjectInfo` methods `as_cif` and `show_as_cif` have TODOs suggesting -they belong in the serialisation module. - -**TODOs:** - -- [project_info.py](src/easydiffraction/project/project_info.py#L123) -- [project_info.py](src/easydiffraction/project/project_info.py#L128) - -**Depends on:** nothing. - ---- - -## 59. 🟢 Add CIF Name Validation or Normalisation in Parse - -**Type:** Robustness - -`io/cif/parse.py` has a TODO about adding a validator or normalisation -step. - -**TODOs:** - -- [parse.py](src/easydiffraction/io/cif/parse.py#L29) - -**Depends on:** nothing. - ---- - -## 60. 🟢 Unify `mkdir` Usage Across the Codebase - -**Type:** Cleanup - -`io/ascii.py` has a TODO to unify directory creation with other uses. - -**TODOs:** - -- [ascii.py](src/easydiffraction/io/ascii.py#L118) - -**Depends on:** nothing. - ---- - -## 61. 🟢 Clarify Logger Default Reaction Mode - -**Type:** Design - -`Logger._reaction` defaults to `Reaction.RAISE` with a -`TODO: not default?` marker. - -**TODOs:** - -- [logging.py](src/easydiffraction/utils/logging.py#L430) - -**Depends on:** nothing. - ---- - -## 62. 🟢 Complete Migration from `render_table` to `TableRenderer` - -**Type:** Cleanup - -`utils.py` has a temporary `render_table` utility that should be -replaced with `TableRenderer`. - -**TODOs:** - -- [utils.py](src/easydiffraction/utils/utils.py#L510) - -**Depends on:** nothing. - ---- - -## 63. 🟢 Fix Calculator `calculate_pattern` Signature Type - -**Type:** Design - -`CalculatorBase.calculate_pattern` takes `structure: Structures` but the -TODO asks whether it should be `Structure` (singular). - -**TODOs:** - -- [base.py](src/easydiffraction/analysis/calculators/base.py#L40) - -**Depends on:** nothing. - ---- - -## 64. 🟢 Check Whether `_not_used_if_loading_from_cif` Code is Needed - -**Type:** Cleanup - -`BraggPdExperiment` has a block marked -`TODO: Not used if loading from cif file?`. - -**TODOs:** - -- [bragg_pd.py](src/easydiffraction/datablocks/experiment/item/bragg_pd.py#L112) - -**Depends on:** nothing. - ---- - -## 65. 🟡 Replace All Bare `print()` Calls with Logging - -**Type:** Code quality - -A few bare `print()` calls remain in `src/` (not `console.print()`, not -commented out, excluding vendored code). All output should go through -`log` or `console` so that verbosity is controllable. Current offenders: - -- [ascii.py](src/easydiffraction/display/plotters/ascii.py#L211) -- [ascii.py](src/easydiffraction/display/plotters/ascii.py#L354) -- [display.py](src/easydiffraction/project/display.py#L687) - -Most earlier offenders are already resolved; the remaining calculator -import prints are commented out and tracked separately under issue 19. - -**Depends on:** nothing. - ---- - -## 66. 🟡 Decide Error-Handling Strategy: `log.error` vs `raise` - -**Type:** Design - -The codebase mixes `log.error(msg)` (which may raise depending on -`Reaction` mode) and direct `raise ValueError(...)`. A consistent -strategy is needed: when to use `log.error` (user-facing, recoverable) -vs native exceptions (programmer errors, unrecoverable). This also -relates to the `Reaction` mode setting (issue 61). - -**Depends on:** issue 61. - ---- - -## 67. 🟡 Custom Validation for Parameter/Descriptor and Category Types - -**Type:** Design - -Parameters and Descriptors use `RangeValidator`, `RegexValidator`, -`MembershipValidator` for values but rely on `@typechecked` (only in -some places) for type checking. Category switchable types use different -validation paths. Decide whether to: - -- Use custom validators for both types and values on Parameters. -- Use custom validators for category type setters. -- Standardise the approach across the codebase. - -**Depends on:** issue 38 (`@typechecked` / gemmi interaction). - ---- - -## 68. 🟢 Decide Whether to Apply `@typechecked` to All Public Methods - -**Type:** Design - -`@typechecked` is currently applied only in ~24 places (factories, -collections). Decide whether it should be applied systematically to all -public method signatures, or whether custom validation (issue 67) is -preferred. - -**Depends on:** issue 67. - ---- - -## 69. 🟢 Shorter Public API Names via `__init__.py` Re-Exports - -**Type:** API ergonomics - -Classes are imported in `__init__.py` files but users still need deep -paths to reach them. Consider whether top-level re-exports (e.g. -`from easydiffraction import Project, Structure, Experiment`) should -provide shorter access, and document the policy. - -**Depends on:** nothing. - ---- - -## 70. 🟡 Standardise Class Member Ordering and Visual Section Headers - -**Type:** Code style - -Agree on and enforce a consistent ordering within every class: - -1. Class-level attributes / metadata -2. `__init__` -3. Private helper methods -4. Public properties (getters/setters) -5. Public methods - -Each group should have a comment header (e.g. -`# --- Public properties ---`) for visual separation. Some classes -already use this pattern; apply it uniformly. - -**Depends on:** nothing. - ---- - -## 71. 🟢 Create `_update_priority` Reference Table for Categories - -**Type:** Documentation - -Create and maintain a table listing all categories that implement -`_update()`, sorted by their `_update_priority`. This makes the update -order explicit and helps catch priority conflicts. - -**Depends on:** related to issues 11, 39. - ---- - -## 73. 🟢 Unify Setter Parameter Naming Convention - -**Type:** Code style - -Some setters use `new`, others use `value`, others use the attribute -name. For example: - -```python -@id.setter -def id(self, new): - self._id.value = new -``` - -Agree on a single convention (e.g. always `value`) and apply -consistently. - -**Depends on:** nothing. - ---- - -## 74. 🟡 Sync Property Type Hints with Private Attributes + Custom Lint - -**Type:** Tooling / Correctness - -Public property getters return `Parameter` / `StringDescriptor` etc., -and setters accept `float` / `str` etc. These annotations must stay in -sync with the private `_attr` type. Currently there is no automated -check. Options: - -- A custom script (like `param_consistency.py`) to verify sync. -- A ruff plugin or post-ruff check step. -- Also covers: enforcing `Base` suffix (not prefix), checking missing - docstrings (issue 81), and other project-specific conventions. - -**Depends on:** nothing. - ---- - -## 75. 🟢 Add `show_supported_calculators()` on Analysis or Project - -**Type:** API completeness - -`show_calculator_types()` exists per-experiment, but there is no -project/analysis-level method to list all available calculator engines. -Users exploring the API have no single entry point to see what -calculators are installed. - -**Depends on:** nothing. - ---- - -## 79. 🟢 Verify Completeness of Analysis CIF Serialisation - -**Type:** Correctness - -`analysis_to_cif()` and `analysis_from_cif()` exist, but audit whether -**all** analysis state is persisted: aliases, constraints, fit mode, -joint-fit weights, minimiser type, calculator assignments. Any missing -fields means a loaded project silently differs from the saved one. - -**Depends on:** related to issue 121. - ---- - -## 80. 🟢 Resolve `Any` vs `object` Type Annotation Policy - -**Type:** Code style - -Both `Any` and `object` are used as generic parameter types. Current -pattern: `Any` inside containers (`dict[str, Any]`), `object` for -standalone params (often to avoid circular imports). Decide on a policy: - -- Use protocol types / `TYPE_CHECKING` imports instead of `object`. -- Reserve `Any` for genuinely unknown types. -- Document when each is appropriate. - -**Depends on:** nothing. - ---- - -## 81. 🟡 Enforce Docstrings on All Public Methods - -**Type:** Code quality - -Some public methods (e.g. `plot_meas_vs_calc`, others) lack docstrings. -Decide: - -- All public methods **must** have numpy-style docstrings. -- Private helpers: minimal one-liner docstring or none? Choose a policy. -- Enable a ruff rule (e.g. `D103`, `D102`) or add a custom check to - enforce. - -**Depends on:** nothing. - ---- - -## 82. 🟢 Document `param-docstring-fix` and `notebook-prepare` Workflow - -**Type:** Documentation - -Two manual workflow steps are required between releases/changes: - -1. `pixi run param-docstring-fix` — sync Parameter docstrings. -2. `pixi run notebook-prepare` — regenerate tutorial notebooks from - scripts. - -Document these in `CONTRIBUTING.md` or a relevant ADR so they are not -forgotten. - -**Depends on:** nothing. - ---- - -## 83. 🟢 Remove Redundant Parameter Listing from Parameter Itself - -**Type:** Cleanup - -Parameters currently carry some form of self-listing metadata that is -redundant with the category/collection level. Remove it to keep the -single-responsibility principle. - -**Depends on:** nothing. - ---- - -## 84. 🟡 Serialise `None` as `.` in CIF Output - -**Type:** Correctness - -CIF output currently writes `None` as literal text for some fields (e.g. -`_diffrn.ambient_pressure None`). CIF convention uses `.` for -inapplicable values and `?` for unknown. The CIF writer should map -`None` → `.` (or `?` depending on semantics), and the reader should map -`.` → `None`. - -**Depends on:** nothing. - ---- - -## 85. 🟡 Retain Per-Experiment Fitted Parameters for Plotting - -**Type:** Correctness / UX - -In `single` fit mode, only the last experiment's fit results are -retained because structure parameters are overwritten on each iteration. -This means earlier experiments cannot be plotted correctly after -fitting. - -**Fix:** after fitting each experiment, store a snapshot of its fitted -parameters (both structure and experiment) so that any experiment can be -re-plotted or inspected later. Also clarify: does `fit_results` need to -keep the last mutable parameter set after adding a snapshot? - -**Depends on:** nothing (issue 78 resolved). - ---- - -## 86. 🟢 Auto-Resolve `plot_param` X-Axis Descriptor and Add Units - -**Type:** UX - -`plot_param_series` currently requires the user to manually specify the -x-axis parameter (e.g. `x_axis='temperature'` → look up -`diffrn.ambient_temperature`). This should be auto-resolved from the -parameter name. Additionally, axis labels should include units (e.g. -"Temperature (K)"). - -**Depends on:** nothing. - ---- - -## 87. 🟢 Redesign Tutorial Grouping and Categorisation - -**Type:** Documentation / UX - -The current tutorial index uses a flat `"level": "advanced"` tag. A -richer categorisation system is needed: - -- Group by topic (matching the docs structure). -- Multiple difficulty levels. -- Multiple Python-knowledge levels. - -**Depends on:** nothing. - ---- - -## 88. 🟢 Fix Dataset 26 Description (47 Files, Not 57) - -**Type:** Data - -Dataset 26 description says "57 files" but should say "47 files": -`"Co2SiO4, D20 (ILL), 57 files, T from ~50K to ~500K"` → -`"Co2SiO4, D20 (ILL), 47 files, T from ~50K to ~500K"`. - -**Depends on:** nothing. - ---- - -## 89. 🟡 Parallel Independent Fits for Single/Independent Fit Mode - -**Type:** Performance - -In `single` (independent) fit mode, each experiment has its own -structure parameters and is completely independent. These fits could run -in parallel threads. Sequential mode, by contrast, must remain single- -threaded because each step's output is the next step's input. - -**Depends on:** nothing (issue 78 resolved). - ---- - -## 95. 🟡 Re-Enable DREAM Multiprocessing in Direct Python Scripts - -**Type:** Performance / Script runtime - -On macOS and other spawn-based platforms, direct Bayesian tutorial -execution via `python script.py` or wrappers such as -`pixi run tutorial docs/docs/tutorials/ed-21.py` can fail during BUMPS -`MPMapper` startup because worker processes re-import `__main__` and -re-execute top-level tutorial code. The current defensive workaround is -to fall back to serial execution for these direct-script entry points, -which avoids the crash but disables DREAM multiprocessing and causes a -large performance drop. - -Observed behavior for `ed-21` today: - -- Jupyter execution and `easydiffraction PROJECT_DIR fit` both appear to - use working parallel DREAM and complete `361/361` in about 40 seconds. -- Direct Python-script execution of the same tutorial runs `361/361` in - about 220 seconds, consistent with the serial fallback path. - -**Possible solution:** keep the existing tracker-state cleanup before -pickling and mapper startup, but replace the blanket serial fallback -with an EasyDiffraction-controlled multiprocessing context policy. For -direct Python script entry points, prefer a `fork` context when -available so workers do not re-import the tutorial top level. Keep the -existing behavior for import-safe module entry points such as -`easydiffraction PROJECT_DIR fit` and for platforms where `fork` is -unavailable. Document the tradeoff clearly because `fork` on macOS is -less conservative than `spawn`. - -**Depends on:** related to issue 89, but independent. - ---- - -## 90. 🟢 Show Experiment Number/Total During Sequential Fitting - -**Type:** UX - -Currently prints: `Using experiment 🔬 'd20_30' for 'single' fitting` - -Should print: -`Using experiment 🔬 'd20_30' (No. 30 of 47) for 'single' fitting` - -**Depends on:** nothing. - ---- - -## 91. 🟢 Disable TODO Comment Checks in CodeFactor PRs - -**Type:** CI / Tooling - -CodeFactor flags TODO comments as unresolved issues (rule C100) in PRs. -Since TODOs are tracked in `issues/open.md`, the CodeFactor check adds -noise. Disable the C100 rule or configure CodeFactor to ignore TODO -comments. - -**Depends on:** nothing. - ---- - -## 92. 🟢 Make `save()` Respect Verbosity Settings - -**Type:** UX - -`Project.save()` unconditionally prints progress via `console.print()`. -It should respect the logger's verbosity mode so that silent/quiet -operation is possible (e.g. in automated pipelines or tests). - -**Depends on:** nothing. - ---- - -## 93. 🟡 Eliminate Flicker in Live Progress Tables - -**Type:** UX - -The shared `ActivityIndicator` / Rich `Live` region used by single fit, -sequential fit, and DREAM sampling visibly flickers in terminals -whenever the live renderable grows (new rows appended) or is updated at -a moderate rate. The effect is most pronounced in sequential fit because -rows are added more frequently than in single fit. - -**Findings from current investigation:** - -- Both single fit (`FitProgressTracker._refresh_activity_indicator`) and - sequential fit (`_report_chunk_progress`) push a fresh - `build_table_renderable(...)` into - `ActivityIndicator.update(content=...)` on each progress event. The - Rich `Table` instance is rebuilt from scratch every time. -- `_TerminalLiveHandle` / `ActivityIndicator` start `rich.live.Live` - with `auto_refresh=True`, - `refresh_per_second=1/_SPINNER_FRAME_SECONDS` (≈10 Hz), and - `vertical_overflow='visible'`. At every refresh tick, Rich re-renders - the full multi-line region (table + spinner line), which on many - terminals causes a visible flicker that scales with row count. -- Earlier attempts to mitigate this in sequential fit by switching to a - single-line spinner-only `Live` and printing rows above it (so Rich's - print-above-live mechanism handled them) removed flicker entirely, but - produced a different visual style from single fit and could not show - the closing border during the run. That approach was reverted for - consistency with single fit; flicker came back with it. -- `vertical_overflow='visible'` is required so the growing table is not - clipped, but it also forces Rich to repaint the whole region rather - than scroll/append. -- The spinner animation itself drives the refresh rate; lowering - `refresh_per_second` reduces flicker frequency but makes the spinner - feel sluggish. -- Single fit appears smoother in practice mainly because content changes - are throttled (`FIT_PROGRESS_UPDATE_SECONDS = 5.0`) and rows grow - slowly; the underlying mechanism is the same and it still flickers - when many iterations are appended quickly. - -**Possible directions (not yet evaluated):** - -- Decouple spinner refresh from content refresh: drive `Live` at a low - `refresh_per_second` (e.g. 2–4 Hz) and update content explicitly only - when a new row arrives, while animating the spinner via the label - string rather than Rich's renderable diff. -- Render the table once as static `console.print(...)` above a - single-line spinner-only `Live`, and re-print only the _new_ row(s) on - each update — restore the streaming approach but emit the bottom - border at the end (accept the trade-off that the closing border is not - visible during the run, or print it as part of every update with ANSI - cursor movement). -- Use `rich.live.Live(transient=False, auto_refresh=False)` and call - `live.refresh()` manually only when content changes; let the spinner - animate via a separate background timer or label updates. -- Investigate `rich.progress.Progress` with custom columns and a table - panel — Rich has optimised diff rendering there. -- Evaluate the actual cause on macOS Terminal / iTerm2 / VS Code - terminal separately — flicker behaviour differs across emulators. - -**Depends on:** nothing. Affects single fit, sequential fit, and DREAM -sampler progress displays — any fix should keep their visuals consistent -(issue #93 should be solved for all three at once). - ---- - -## 102. 🟢 Drop Compute-and-Ignore `result_kind` Validation in CIF Restore - -**Type:** Dead code / clarity **Source:** Review 8 finding F7. -**Recommended:** fold into the emcee-minimizer plan. - -`_restore_persisted_fit_state` -([serialize.py:595-611](../../../src/easydiffraction/io/cif/serialize.py)) -calls `FitResultKindEnum(result_kind_value)` purely for the warning side -effect; the result is discarded. After P1.10 absorbed the -Bayesian-specific categories there is nothing else to do per -`result_kind`. - -**Fix:** replace with a validator helper that takes a string and logs -the warning, or move the warning into `fit_result.result_kind` setter so -invalid values are caught on read. Either removes the "compute and -ignore" pattern. - -**Depends on:** nothing. - ---- - -## 104. 🟢 Tighten `FitParameterItem.posterior_summary` NaN Behaviour - -**Type:** Robustness / partial-data edge case **Source:** Review 8 -finding F9. - -`FitParameterItem.has_posterior_summary` returns `True` if any posterior -field is set, and `posterior_summary` then builds a -`PosteriorParameterSummary` whose missing floats become `NaN`. A -hand-edited or partially-written CIF row with only -`posterior_gelman_rubin = 1.02` and the rest unset produces a summary -whose `median`, `standard_deviation`, and both interval bounds are -`NaN`. Downstream plotting and the `display.fit_results` table render -NaN intervals — harder to debug than a clean "no posterior" outcome. - -The deterministic-fit case is fine: deterministic fits set all required -fields to `None`, so `has_posterior_summary()` returns `False`. - -**Fix:** tighten `has_posterior_summary` to require the core stats (at -least `posterior_median` and one interval bound) before emitting a -summary, or split the dataclass into required-statistics and -optional-diagnostics components. - -**Depends on:** nothing. - ---- - -## 105. 🟢 Remove Orphaned Fit-Result Reset Helper - -**Type:** Cleanup **Source:** `minimizer-input-output-split` review 6. - -`Analysis._clear_fit_result_projection` is a private method with no -callers after `_clear_persisted_fit_state` switched to replacing -`self._fit_result` with a fresh paired result instance. - -**TODOs:** - -- [analysis.py](src/easydiffraction/analysis/analysis.py#L1217) - -**Fix:** delete the unused helper, or reintroduce a caller only if a -future fit-result reset path genuinely needs to preserve the active -instance. - -**Depends on:** nothing. - ---- - -## 106. 🟢 Document `FitResultBase.result_kind` Default Rationale - -**Type:** Code readability **Source:** `minimizer-input-output-split` -review 6. - -Most `FitResultBase` descriptors use `default=None, allow_none=True` so -pre-fit CIF output serializes unknown values as `?`. `result_kind` -intentionally keeps a valid enum default because it drives deterministic -versus Bayesian projection handling, but that exception is not -documented in code. - -**TODOs:** - -- [base.py](src/easydiffraction/analysis/categories/fit_result/base.py#L44) - -**Fix:** add a short code comment near the `result_kind` descriptor -explaining why it keeps a concrete default while unknown result values -use `None`. - -**Depends on:** nothing. - ---- - -## 107. 🟡 Validate Generated CIF Report Against Official IUCr Dictionaries - -**Type:** Test coverage - -The runtime gemmi self-check in the IUCr CIF writer was removed (it -validated our own deterministic output at write time and depended on -dictionaries under `tmp/iucr-dicts/`; see the §2.5 amendment in -[`iucr-cif-tag-alignment.md`](../adrs/accepted/iucr-cif-tag-alignment.md)). -That spec-compliance guarantee now needs to live in a dev-time test -instead. - -**Fix:** add a unit or functional test that renders both a powder and a -single-crystal IUCr report CIF and validates every emitted tag against -the latest official COMCIFS `cif_core.dic` and `cif_pow.dic`. -Requirements: - -- Cover both powder (`_pd_*`, profile/reflection loops) and - single-crystal report outputs. -- Do **not** read `tmp/` at runtime — pass the dictionaries explicitly - as a committed test fixture (or fetch them in test setup and pass the - path in). The check belongs in the test suite, not in the user's write - path. -- Parse the DDLm/CIF2 form correctly: the current dictionaries use - `save_<name>` frames with `_definition.id`, which the removed helper's - `save__tag` regex and a plain `gemmi.cif.read_file` could not handle. - Use gemmi's DDL reader or a scan adapted to the DDLm layout. -- Allow the project's private `_easydiffraction_*` extension namespace. - -**Depends on:** nothing. - ---- - -## 108. 🟢 Smarter Automatic Bond Detection (Near-Neighbour Analysis) - -**Type:** UX / Visualization - -crysview generates bonds with the cif_core distance rule -(`min_bond_distance_cutoff ≤ d ≤ r_bond(A) + r_bond(B) + bond_distance_incr`), -then prunes to the first coordination shell — a contact survives only if -it is within `1.3×` the nearer atom's nearest-neighbour distance -(`COORDINATION_SHELL_FACTOR` in `display/structure/builder.py`). This -stop-gap handles the common cases (e.g. LBCO renders just the Co–O -octahedron) without a new dependency, but the fixed factor is still a -heuristic: it can over-prune strongly distorted shells (e.g. elongated -Jahn–Teller octahedra) or under-prune others, and it is not yet -user-configurable. - -**Fix:** consider a robust, configurable near-neighbour algorithm for -automatic "reasonable" bonding — e.g. a Voronoi / solid-angle method -such as pymatgen's `CrystalNN` or `VoronoiNN`, which weights neighbours -by solid angle instead of a single relative cutoff. The Voronoi route is -the most robust across arbitrary structures but introduces a heavyweight -dependency (pymatgen), so it needs a dependency decision; an -ASE/Jmol-style multiplicative covalent tolerance is lighter but, like -the current factor, cannot separate shells when ionic-cation covalent -radii are large. - -**Depends on:** dependency decision for pymatgen (if the Voronoi route -is chosen). - ---- - -## 109. 🟢 Let More Tables Adapt to Terminal Width - -**Type:** UX / Display - -`list_tutorials` now renders its table at the real terminal width via a -new optional `width` parameter threaded through the table render path -(`render_table` → `TableRenderer.render` → backend `render`; Rich -applies it, the HTML backend ignores it). Every other table and all log -output still go through the shared Rich console, whose width is floored -at `ConsoleManager._MIN_CONSOLE_WIDTH = 130` ("to avoid cramped -layouts"). On a standard ~80-column terminal that floor makes wide -tables overflow and soft-wrap badly. - -**Fix:** decide on a global policy — either have `_detect_width` trust -the detected terminal width (keeping 130 only as a fallback when -detection fails), or pass the terminal width into more table call sites -the way `list_tutorials` now does. A global change affects every table -(fit results, parameters, ...) and all logs, so weigh it against the -deliberate minimum-width choice. - -**Depends on:** related to issue 62. - ---- - -## 110. 🟢 Render Styled Multi-Line Table Cells in the HTML Backend - -**Type:** Display / Notebook parity - -`list_tutorials` shows a two-line cell in the terminal — a colored title -on the first line and a dimmed description on the second — using Rich -markup and an embedded newline. The Jupyter table backend -(`PandasTableBackend`) cannot render this: `_strip_rich_markup` only -matches a single full-cell `[color]text[/color]`, and HTML collapses the -newline, so the markup would show as literal text. `list_tutorials` is -therefore gated via `in_jupyter()` to show only the plain title in -notebooks, which drops the description and the color there. - -**Fix:** teach the HTML backend to render the same styling — translate -embedded newlines to `<br>`, map `[dim]` to reduced opacity, and accept -multiple/mixed markup tags per cell — then remove the terminal-only gate -in `list_tutorials` so notebooks also get the styled two-line entry. - -**Depends on:** related to issue 62. - ---- - -## 111. 🟢 Add Test Coverage for `list_tutorials` Two-Line Rendering - -**Type:** Test coverage - -The `list_tutorials` table gained a styled two-line cell (colored title -plus dimmed description), a terminal-only `in_jupyter()` gate that falls -back to the plain title, and a new optional `width` parameter on the -table render path. Existing tests only assert that titles appear in the -output. - -**Fix:** add unit tests for the description line appearing in the -terminal (non-Jupyter) path, the Jupyter-gated path showing the plain -title with no literal Rich markup, and the `width` parameter sizing the -rendered Rich table. Run `pixi run fix` / `check` / `unit-tests` to -confirm the shared-renderer signature change. - -**Depends on:** nothing. - ---- - -## 112. 🟢 Suppress the Redundant Row-Index Column in Tables - -**Type:** Display / UX - -`TableRenderer._prepare_dataframe` bumps the DataFrame index to 1-based, -and both the Rich and pandas backends always render it as the first -column. For tables that already carry an explicit identifier — e.g. -`list_tutorials`, whose `id` column duplicates that 1-based counter — -the leading index column is redundant and reads as a duplicate. - -**Fix:** add an opt-out (e.g. a `show_index` flag on the render path) so -callers with their own id column can hide the auto-generated index, or -only render the index column when no explicit id column is present. - -**Depends on:** nothing. - ---- - -## 113. 🟡 Cross-Repository Validation Harness (nightly) - -**Type:** Test infrastructure - -Deferred cross-repository work for the -[Test Suite and Validation Strategy](../adrs/accepted/test-suite-and-validation.md) -ADR (§7, §8). The harness code lives in `diffraction-lib`; the corpus, -results database, and benchmark/reference history live in the -`diffraction` data repository, fetched at runtime and written back by a -nightly job that installs easydiffraction from PyPI (acceptance-style). - -**Items:** - -- **COD corpus check.** Download ~100–200 CIF files from the - Crystallography Open Database, load each, and record per-file status - (`ok` / `partial` + missing fields / `fail`) in a git-diffable CSV - keyed and ordered by COD id. Add a `--recheck-failed` flag to re-run - only the failed/partial entries after fixes (instead of new random - files). Extend with per-engine calculation results on the corpus. -- **Generative fuzzing.** Randomly generate ~100–200 structures (random - space group, cell, 1–10 atoms with random coordinates/ADP/occupancy), - compute patterns across engines, and record disagreements in the same - database. -- **Benchmark history + gate.** Commit `pytest-benchmark` baseline JSON - to the data repository and add a regression threshold once timing - variance is characterised on a controlled runner. (The serial - benchmark task itself now exists — see issue 16 — this is the - history/gating remainder.) -- **External-software comparison.** Add FullProf (then GSAS-II/TOPAS) - pre-calculated profiles as zipped projects in the data repository so - the Verification pages can overlay them against easydiffraction. - -**Depends on:** the `diffraction` data repository; cross-repo -coordination. - ---- - -## 114. 🟢 External Link Checking in the Docs Gate - -**Type:** CI / Documentation - -The fast docs gate (`docs-build` + `link-check` + `spell-check`) catches -broken nav/internal links and typos on every push, but does not yet -check external URLs. Add a `lychee` link checker (with an allowlist for -rate-limited/unstable domains), coordinated with the -[Documentation CI and Build Verification](../adrs/accepted/documentation-ci-build.md) -ADR. Run it nightly or on pull requests to avoid flakiness from external -sites. Also covers link-checking of URLs that appear only inside -executed notebook output cells (a feature that does not exist yet). - -**Depends on:** nothing. - ---- - -## 115. 🟢 Expand Cross-Engine Verification Coverage - -**Type:** Test coverage / Documentation - -The Verification docs section ships with the framework and the first -cross-engine comparison page (constant-wavelength powder, cryspy ↔ -crysfml). Extend it to the remaining supported combinations declared by -the calculator support matrix — time-of-flight powder (cryspy ↔ crysfml) -and single crystal — so every valid experiment/instrument combination is -documented and regression-checked at least once. Each new page is a -calculation-only `.py` under `docs/docs/verification/` wired into -`script-tests` and `notebook-tests`, with explicit metric tolerances. - -**Depends on:** nothing. - ---- - -## 116. 🟡 Add a Static Type Checker to the Quality Gate - -**Type:** Tooling / Correctness - -The project type-annotates public signatures but runs no static type -checker (only ruff's `TC` import-placement rules). A genuine bug slipped -through as a result: -`Plotter._plot_single_crystal_posterior_predictive_summary` called -`PlotlyPlotter._get_diagonal_shape()` with no arguments while the method -requires `(minimum, maximum)`, so the single-crystal posterior- -predictive plot raised `TypeError` whenever reached. A type checker -would have flagged the wrong-arity call at lint time, without needing a -test to exercise the path — and would catch the whole class of such -errors. - -**Fix:** add a checker (mypy, pyright, or `ty`) as a `pixi` task wired -into `check` and the lint CI workflow, alongside the existing ruff / -pydoclint / interrogate gates. Roll out incrementally to manage the -initial error backlog: start lenient (e.g. `--follow-imports=silent` or -a per-package allowlist) and gate only new/changed code first, then -tighten. The codebase already annotates public signatures, so it is -well-positioned. - -**Depends on:** nothing. Best landed as its own focused effort because -enabling a checker on an existing codebase surfaces a backlog that needs -a baseline-cleanup plan. - ---- - -## 117. 🟢 Live-Notebook Plotly Delivery: Loader vs Native Mimetype - -**Type:** Display / Architecture - -Records the two viable strategies for rendering interactive Plotly -figures in live notebooks, so the trade-off is not re-litigated. See -[`plotting-docs-performance.md`](../adrs/accepted/plotting-docs-performance.md). - -**Background.** Live notebooks historically rendered via -`display(HTML(pio.to_html(..., include_plotlyjs='cdn')))`, which caused -an empty first plot after kernel restart (the CDN `<script src>` loaded -asynchronously while `Plotly.newPlot` ran immediately) and a loading -gap. Two ways to fix it: - -- **Option 1 — Native mimetype renderer (`fig.show()`).** Emit the - `application/vnd.plotly.v1+json` mime bundle and let the JupyterLab - Plotly extension render it. Pros: simplest, lowest maintenance, - officially supported, no CDN, no script-in-output artifacts. Cons: - live notebooks lose the three custom post-script behaviours (dynamic - theme-sync on JupyterLab light/dark toggle, hidden-tab resize, the - modebar legend-toggle button); a saved `.ipynb`'s plot output is the - spec JSON, not self-contained HTML. - -- **Option 2 — Self-hosted loader (current).** Ship the vendored Plotly - bundle + the shared `ed-figures.js` loader in the wheel; the first - figure injects them once per kernel session and each figure renders - via `window.edFigures.renderSpec(id, spec)` delivered as a - `display(Javascript(...))` output, so the HTML output is just the plot - div (no `<script>` tags some hosts render as empty rows). Pros: keeps - all three custom behaviours; CDN-free/archival; unified with the docs - delivery. Cons: more moving parts; depends on the notebook being - **trusted** for a reopened (not re-run) notebook to re-render. - -**Current choice:** Option 2 (keeps the live-notebook extras). Revisit -Option 1 if the loader proves fragile across notebook frontends or the -maintenance cost outweighs the three behaviours. - -**Depends on:** nothing. - ---- - -## 118. 🟢 Plotly Figures Show Empty Rows in the VISA JupyterLab - -**Type:** Display / Environment - -**Symptom.** In the **VISA-hosted, iframe-embedded** JupyterLab -(`visa.ess.eu/.../jupyter/.../lab`), every interactive Plotly figure is -preceded by several empty rows that fill in over ~1–2 s, and the plot -often renders collapsed. On a **standard local JupyterLab the branch is -fine** — the only issue there is the CDN race (issue addressed by the -self-hosted runtime; see below). So this is **specific to the VISA -environment**, not the library. - -**Root cause (from a DOM inspection in VISA).** The plot element renders -at **height 0**, nested under -`div.jp-WindowedPanel-viewport.jp-content-visibility-mode`. VISA's -JupyterLab runs notebooks in **windowed mode** (`content-visibility` -virtualization). The output container is **0×0 at the moment Plotly -draws**, and because the figure config is `responsive: true`, Plotly -sizes to that 0×0 container and renders a zero-size plot that does not -recover. The "empty rows" are that collapsed zero-height output as the -viewport re-measures. This is a known Plotly ✕ JupyterLab-windowing -interaction. - -**Workaround (user side).** JupyterLab → Settings → Notebook → -**Windowing mode = `defer`/`none`** disables the virtualization. (The -user reported this alone did **not** resolve it in VISA, so VISA may -force or wrap the setting — needs confirmation.) - -**Attempts that did NOT resolve it in VISA** (all reverted to keep the -code minimal; recorded so they are not retried blindly): - -- Self-hosted runtime instead of CDN, delivered as a single HTML output, - as a `display(Javascript(...))` output, and as multiple/one inline - `<script>` tags. (The self-hosting itself **is** kept — it fixes the - real CDN race — but none of the delivery variants changed the VISA - empty rows.) -- Removing the loading skeleton; removing then restoring the - `min-height` reservation. -- Trimming the figure top margin + `title.automargin` (chasing a misread - "default margin" theory). -- Preloading the runtime on `import easydiffraction` (regressed: added a - visible empty output line on the import cell). -- Skipping the resize `ResizeObserver` for live figures. -- Reserving an explicit container height for windowing. -- Deferring the render until the container reports a non-zero size - (`requestAnimationFrame` poll). This **did** get the plot to render at - full height in VISA (DOM showed `h=565` instead of `0`), but the user - still reported empty rows visually — so it is necessary-but-not- - sufficient there. - -**Promising future directions.** Set `responsive: false` with an -explicit width/height for live figures so Plotly never depends on the -0×0 container; or render the figure off-screen and swap it in once -sized; or detect the VISA/windowed environment and special-case it. -Needs to be developed and tested **inside VISA**, since it does not -reproduce on a standard JupyterLab. - -**Depends on:** nothing. Lower priority — affects only the VISA -deployment, and a user-side windowing-mode change may suffice. - ---- - -## 133. 🟢 Rename `asym_empir_*` and Add the Physical FCJ Asymmetry Model - -**Type:** Experiment model / Peak profile / API naming - -The four empirical peak-asymmetry parameters (`asym_empir_1`…`4`, the -`pd-neut-cwl_pv-asym_empir_pbso4` Verification page) are the -**Bérar–Baldinozzi** correction — FullProf's `P1`–`P4` — a -phenomenological sum of functions in `1/tan θ` and `1/tan 2θ`. It can -fit an asymmetric peak, but the parameters carry **no physical -meaning**, are strongly correlated, do **not** transfer between -datasets, and can misbehave (over-correction, unphysical profile -shapes). - -The **Finger–Cox–Jephcoat (FCJ)** `S_L`/`D_L` model is physically based: -just **two** parameters tied to real instrument geometry (sample and -slit/detector heights over the goniometer radius), with the correct -built-in angular dependence — asymmetry that vanishes at `2θ = 90°` and -reverses past it. Fewer parameters, better-conditioned, and -instrument-meaningful. - -**Two future considerations:** - -1. **Rename** the empirical parameters so the name states what they are - — e.g. `asym_berar_baldinozzi_1`…`4` (or a `berar_baldinozzi_p*` - form) — rather than the generic `asym_empir_*`, which hides their - origin and conflates "empirical asymmetry" with the specific - Bérar–Baldinozzi formula. -2. **Add the FCJ model alongside** the empirical one (not as a - replacement), as a switchable asymmetry choice, so users can pick the - physically-based two-parameter model when the instrument geometry is - known and fall back to the empirical correction otherwise. - -**Relates to:** the asymmetry discrepancy tracked on the -`pd-neut-cwl_pv-asym_empir_pbso4` Verification page (currently in -`docs/docs/verification/ci_skip.txt`), and the TCH/FCJ work noted on the -`pd-neut-cwl_tch-fcj_lab6` page. - -**Depends on:** calculator-backend support for the FCJ asymmetry -parameters (cryspy/crysfml) before the second item can be wired through. - ---- - -## 134. 🟡 Investigate ed-crysfml TOF Jorgensen Profile Discrepancy - -**Type:** Correctness - -For time-of-flight powder data using the plain Jorgensen profile -(back-to-back exponentials ⊗ Gaussian, no Lorentzian), the `crysfml` -backend diverges from FullProf and `cryspy` after the scale is fitted: -the profile is ≈8.5% off with an integrated-area ratio ≈1.09 (corr -≈0.997), while `cryspy` matches FullProf. This localises the problem to -the crysfml translation of the Jorgensen TOF profile, and is -complementary to the `cryspy` Jorgensen–Von Dreele Lorentzian divergence -tracked in issue 130. - -**Visible on:** the Si TOF Jorgensen Verification page -(`pd-neut-tof_j_si`), currently skipped via -`docs/docs/verification/ci_skip.txt`. Re-enable the page (or tighten its -agreement check) once the crysfml profile is reconciled. - -**Depends on:** nothing. - ---- - -## 135. 🟡 More Intuitive ADP Creation API (type-aware kwargs) - -**Type:** API design - -Creating an atom currently requires setting `adp_type` and the -type-neutral `adp_iso`/`adp_11`… values separately (for example -`add(..., adp_type='Biso', adp_iso=0.5)`). For scientists this is less -discoverable than naming the displacement convention directly. Options -to explore: a richer creation surface with type-aware convenience -keywords (`b_iso=`/`u_iso=`/`beta=`) that set `adp_type` automatically, -and/or CIF-style auto-attachment of the sibling isotropic/anisotropic -values when `adp_type` is set. This revisits the accepted -[type-neutral-adp-parameters](docs/dev/adrs/accepted/type-neutral-adp-parameters.md) -ADR — which deliberately chose type-neutral storage to keep parameter -identity stable across switches — so it needs its own ADR + plan and is -independent of the β-tensor work that surfaced it. - -**Depends on:** the β-tensor ADP support -([adp-beta-tensor plan](docs/dev/plans/adp-beta-tensor.md)). - ---- - -## 136. 🟢 Draw ADP Ellipsoids for Beta-Tensor Atoms - -**Type:** Display / Visualization - -The 3D structure view -([builder.py](src/easydiffraction/display/structure/builder.py)) draws -anisotropic displacement ellipsoids only for the `Bani`/`Uani` ADP -types. Atoms stored as the dimensionless `beta` tensor currently fall -through to a plain sphere. Drawing their ellipsoids needs a β→U -conversion in the renderer (using the reciprocal cell), analogous to the -existing B→U step. The model-layer β↔U conversion already exists -(`AtomSite._convert_adp_values_beta`) and could be reused. - -**Depends on:** the β-tensor ADP support -([adp-beta-tensor plan](docs/dev/plans/adp-beta-tensor.md)). - ---- - -## Summary - -| # | Issue | Severity | Type | -| --- | --------------------------------------------------- | -------- | ---------------------------- | -| 3 | Rebuild joint-fit weights | 🟡 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 | -| 13 | Suppress redundant dirty-flag sets | 🟢 Low | Performance | -| 14 | Finer-grained change tracking | 🟢 Low | Performance | -| 15 | Validate joint-fit weights | 🟡 Med | Correctness | -| 16 | Add serial pattern-generation benchmarks | 🟢 Low | Performance | -| 17 | Use PDF-specific CIF names | 🟢 Low | Naming | -| 18 | Move CIF v2→v1 conversion out of calculator | 🟢 Low | Maintainability | -| 19 | Debug-mode logging for calculator imports | 🟢 Low | Diagnostics | -| 20 | Redirect/suppress CrysPy stderr | 🟢 Low | UX | -| 21 | Clarify CrysPy TOF background CIF tags | 🟡 Med | Correctness | -| 22 | Check SC instrument mapping in CrysPy | 🟢 Low | Correctness | -| 23 | Investigate PyCrysFML pattern length discrepancy | 🟢 Low | Correctness | -| 24 | Process defaults on experiment creation | 🟢 Low | Design | -| 25 | Refactor data `_update` methods | 🟡 Med | Maintainability | -| 26 | Clarify `dtype` usage in data arrays | 🟢 Low | Cleanup | -| 27 | Handle zero uncertainty in Bragg PD | 🟢 Low | Correctness | -| 28 | Clarify Bragg PD data collection description | 🟢 Low | Cleanup | -| 29 | Standardise CIF ID validator pattern | 🟡 Med | Consistency | -| 30 | Make `refinement_status` default an Enum | 🟢 Low | Design | -| 31 | Rename PD data point mixins | 🟢 Low | Naming | -| 32 | Move common methods to `DatablockCollection` | 🟡 Med | Maintainability | -| 33 | Make `_update_categories` abstract | 🟡 Med | Design | -| 34 | Auto-extract `PeakProfileTypeEnum` | 🟢 Low | Design | -| 35 | Rename `BeamModeEnum` members to CWL/TOF | 🟢 Low | Naming | -| 36 | Common `EnumBase` class | 🟢 Low | Design | -| 37 | Rename experiment `.type` property | 🟢 Low | Naming | -| 38 | Fix `@typechecked`/gemmi in factories | 🟡 Med | Bug | -| 39 | Improve `_update_priority` handling | 🟢 Low | Design | -| 40 | Reset `.user_constrained` to `False` | 🟢 Low | Feature | -| 41 | Check `_mark_dirty` in `_set_value` | 🟢 Low | Cleanup | -| 42 | MkDocs type unpacking in validation | 🟢 Low | Docs | -| 43 | Fix summary display inconsistencies | 🟢 Low | UX | -| 44 | Merge parameter record construction | 🟢 Low | Cleanup | -| 45 | Decide alias/constraint descriptor default | 🟢 Low | Design | -| 46 | Improve `JointFitItem` descriptions | 🟢 Low | Naming | -| 47 | Improve error handling in crystallography | 🟢 Low | Diagnostics | -| 48 | Fix CrysPy TOF instrument default | 🟢 Low | Bug workaround | -| 49 | Automate space group CIF name variants | 🟢 Low | Maintainability | -| 50 | Clarify `Cell._update` minimizer param | 🟢 Low | Cleanup | -| 52 | Rename line-segment `y` to `intensity` | 🟢 Low | Naming | -| 53 | Move `show()` to `CategoryCollection` | 🟢 Low | Maintainability | -| 54 | Add `point_id` to excluded regions | 🟢 Low | Completeness | -| 55 | Fix Jupyter scroll disabling for MkDocs | 🟢 Low | Docs / UX | -| 56 | Make ASCII plot width configurable | 🟢 Low | UX | -| 57 | Clean up CIF deserialisation helpers | 🟢 Low | Maintainability | -| 58 | Move `ProjectInfo` CIF methods to `serialize` | 🟢 Low | Maintainability | -| 59 | Add CIF name validation in parse | 🟢 Low | Robustness | -| 60 | Unify `mkdir` usage | 🟢 Low | Cleanup | -| 61 | Clarify logger default reaction mode | 🟢 Low | Design | -| 62 | Complete `render_table` → `TableRenderer` | 🟢 Low | Cleanup | -| 63 | Fix calculator `calculate_pattern` signature | 🟢 Low | Design | -| 64 | Check unused-if-loading-from-CIF code | 🟢 Low | Cleanup | -| 65 | Replace all bare `print()` with logging | 🟡 Med | Code quality | -| 66 | Error-handling strategy: `log.error` vs `raise` | 🟡 Med | Design | -| 67 | Custom validation for params and category types | 🟡 Med | Design | -| 68 | `@typechecked` on all public methods? | 🟢 Low | Design | -| 69 | Shorter public API names via `__init__` | 🟢 Low | API ergonomics | -| 70 | Standardise class member ordering + headers | 🟡 Med | Code style | -| 71 | `_update_priority` reference table | 🟢 Low | Documentation | -| 73 | Unify setter parameter naming | 🟢 Low | Code style | -| 74 | Sync property type hints + custom lint rules | 🟡 Med | Tooling | -| 75 | `show_supported_calculators()` on Analysis | 🟢 Low | API completeness | -| 79 | Verify analysis CIF serialisation completeness | 🟢 Low | Correctness | -| 80 | Resolve `Any` vs `object` annotation policy | 🟢 Low | Code style | -| 81 | Enforce docstrings on all public methods | 🟡 Med | Code quality | -| 82 | Document `param-docstring-fix` workflow | 🟢 Low | Documentation | -| 83 | Remove redundant parameter listing | 🟢 Low | Cleanup | -| 84 | Serialise `None` as `.` in CIF output | 🟡 Med | Correctness | -| 85 | Retain per-experiment fitted params for plotting | 🟡 Med | Correctness | -| 86 | Auto-resolve `plot_param` x-axis + add units | 🟢 Low | UX | -| 87 | Redesign tutorial grouping/categorisation | 🟢 Low | Documentation | -| 88 | Fix Dataset 26 description (47 not 57) | 🟢 Low | Data | -| 89 | Parallel independent fits for single mode | 🟡 Med | Performance | -| 95 | Re-enable DREAM multiprocessing in direct scripts | 🟡 Med | Performance / Script runtime | -| 102 | Drop compute-and-ignore result_kind validation | 🟢 Low | Dead code / clarity | -| 104 | Tighten posterior_summary NaN behaviour | 🟢 Low | Robustness | -| 90 | Show experiment number during sequential fitting | 🟢 Low | UX | -| 91 | Disable TODO checks in CodeFactor PRs | 🟢 Low | CI / Tooling | -| 92 | Make `save()` respect verbosity | 🟢 Low | UX | -| 93 | Eliminate flicker in live progress tables | 🟡 Med | UX | -| 94 | Revisit powder refln phase labels and row ids | 🟢 Low | Naming / CIF UX | -| 105 | Remove orphaned fit-result reset helper | 🟢 Low | Cleanup | -| 106 | Document `FitResultBase.result_kind` default | 🟢 Low | Code readability | -| 107 | Validate CIF report vs IUCr dictionaries | 🟡 Med | Test coverage | -| 108 | Smarter automatic bond detection (near-neighbour) | 🟢 Low | UX / Visualization | -| 109 | Let more tables adapt to terminal width | 🟢 Low | UX / Display | -| 110 | Styled multi-line table cells in HTML backend | 🟢 Low | Display / Notebook parity | -| 111 | Test coverage for `list_tutorials` rendering | 🟢 Low | Test coverage | -| 112 | Suppress redundant row-index column in tables | 🟢 Low | Display / UX | -| 113 | Cross-repository validation harness (nightly) | 🟡 Med | Test infrastructure | -| 114 | External link checking in the docs gate | 🟢 Low | CI / Documentation | -| 115 | Expand cross-engine verification coverage | 🟢 Low | Test coverage | -| 116 | Add a static type checker to the quality gate | 🟡 Med | Tooling / Correctness | -| 117 | Live-notebook Plotly: loader vs native mimetype | 🟢 Low | Display / Architecture | -| 118 | Plotly empty rows in the VISA JupyterLab | 🟢 Low | Display / Environment | -| 119 | Model sample absorption (Debye–Scherrer μR) | 🟡 Med | Physics / Engine feature | -| 120 | Decide if inactive fit-mode categories stay lenient | 🟡 Med | API design | -| 121 | Clarify joint_fit lifecycle outside execution | 🟡 Med | Fragility | -| 122 | Define joint_fit.weight bounds | 🟡 Med | Data model | -| 123 | Define sequential_fit_extract target scope | 🟡 Med | Data model | -| 124 | Decide sequential extraction failure policy | 🟡 Med | Runtime behaviour | -| 125 | Decide whether sequential extraction is cached | 🟢 Low | Performance | -| 126 | Decide how mid-run sequential failures persist | 🟢 Low | Recovery design | -| 127 | Decide whether CLI overrides extract rules | 🟢 Low | CLI design | -| 128 | Align dir() with help filtering | 🟢 Low | Discoverability | -| 129 | Decide whether single_fit needs a category | 🟢 Low | Scope planning | -| 130 | cryspy diverges on TOF JVD Lorentzian | 🟡 Med | Correctness | -| 131 | Add SyCos/SySin peak-position corrections | 🟡 Med | Feature / Experiment model | -| 132 | Decide future of show_residual in plots | 🟢 Low | API cleanup | -| 133 | Rename asym*empir*\* and add FCJ asymmetry | 🟢 Low | Experiment model / Naming | -| 134 | Investigate ed-crysfml TOF Jorgensen discrepancy | 🟡 Med | Correctness | -| 135 | More intuitive ADP creation API (type-aware kwargs) | 🟡 Med | API design | -| 136 | Draw ADP ellipsoids for beta-tensor atoms | 🟢 Low | Display / Visualization | diff --git a/docs/dev/issues/open/high_add-a-static-type-checker-to-the-quality-gate.md b/docs/dev/issues/open/high_add-a-static-type-checker-to-the-quality-gate.md new file mode 100644 index 000000000..5b5bab892 --- /dev/null +++ b/docs/dev/issues/open/high_add-a-static-type-checker-to-the-quality-gate.md @@ -0,0 +1,33 @@ +# 116. Add a Static Type Checker to the Quality Gate + +**Priority:** `[priority] high` + +**Type:** Tooling / Correctness + +The project type-annotates public signatures but runs no static type +checker (only ruff's `TC` import-placement rules). A genuine bug slipped +through as a result: +`Plotter._plot_single_crystal_posterior_predictive_summary` called +`PlotlyPlotter._get_diagonal_shape()` with no arguments while the method +requires `(minimum, maximum)`, so the single-crystal posterior- +predictive plot raised `TypeError` whenever reached. A type checker +would have flagged the wrong-arity call at lint time, without needing a +test to exercise the path — and would catch the whole class of such +errors. + +**Fix:** add a checker (mypy, pyright, or `ty`) as a `pixi` task wired +into `check` and the lint CI workflow, alongside the existing ruff / +pydoclint / interrogate gates. Roll out incrementally to manage the +initial error backlog: start lenient (e.g. `--follow-imports=silent` or +a per-package allowlist) and gate only new/changed code first, then +tighten. The codebase already annotates public signatures, so it is +well-positioned. + +**Depends on:** nothing. Best landed as its own focused effort because +enabling a checker on an existing codebase surfaces a backlog that needs +a baseline-cleanup plan. + +**Recommended-priority note:** A real wrong-arity `TypeError` already +shipped because nothing catches it; high leverage — land as its own +baseline-cleanup effort. **Tier 2 (tooling that prevents whole bug +classes).** diff --git a/docs/dev/issues/open/high_add-explicit-create-signatures-on-collections.md b/docs/dev/issues/open/high_add-explicit-create-signatures-on-collections.md new file mode 100644 index 000000000..b5ec85882 --- /dev/null +++ b/docs/dev/issues/open/high_add-explicit-create-signatures-on-collections.md @@ -0,0 +1,20 @@ +# 8. Add Explicit `create()` Signatures on Collections + +**Priority:** `[priority] high` + +**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. + +**Recommended-priority note:** Typos in `create(**kwargs)` are silently +dropped today. **Tier 2 (tooling that prevents whole bug classes).** diff --git a/docs/dev/issues/open/high_clarify-logger-default-reaction-mode.md b/docs/dev/issues/open/high_clarify-logger-default-reaction-mode.md new file mode 100644 index 000000000..e819ad1e7 --- /dev/null +++ b/docs/dev/issues/open/high_clarify-logger-default-reaction-mode.md @@ -0,0 +1,17 @@ +# 61. Clarify Logger Default Reaction Mode + +**Priority:** `[priority] high` + +**Type:** Design + +`Logger._reaction` defaults to `Reaction.RAISE` with a +`TODO: not default?` marker. + +**TODOs:** + +- [logging.py](src/easydiffraction/utils/logging.py#L430) + +**Depends on:** nothing. + +**Recommended-priority note:** Logger default reaction mode — pairs with +#66's error-handling strategy decision. **Tier 2.** diff --git a/docs/dev/issues/open/high_decide-error-handling-strategy-log-error-vs-raise.md b/docs/dev/issues/open/high_decide-error-handling-strategy-log-error-vs-raise.md new file mode 100644 index 000000000..490c73515 --- /dev/null +++ b/docs/dev/issues/open/high_decide-error-handling-strategy-log-error-vs-raise.md @@ -0,0 +1,16 @@ +# 66. Decide Error-Handling Strategy: `log.error` vs `raise` + +**Priority:** `[priority] high` + +**Type:** Design + +The codebase mixes `log.error(msg)` (which may raise depending on +`Reaction` mode) and direct `raise ValueError(...)`. A consistent +strategy is needed: when to use `log.error` (user-facing, recoverable) +vs native exceptions (programmer errors, unrecoverable). This also +relates to the `Reaction` mode setting (issue 61). + +**Depends on:** issue 61. + +**Recommended-priority note:** Pin the error-handling strategy (paired +with #61): when to use `log.error` vs `raise`. **Tier 2.** diff --git a/docs/dev/issues/open/high_rename-asym-empir-and-add-the-physical-fcj-asymmetry-model.md b/docs/dev/issues/open/high_rename-asym-empir-and-add-the-physical-fcj-asymmetry-model.md new file mode 100644 index 000000000..9644ad6e5 --- /dev/null +++ b/docs/dev/issues/open/high_rename-asym-empir-and-add-the-physical-fcj-asymmetry-model.md @@ -0,0 +1,45 @@ +# 133. Rename `asym_empir_*` and Add the Physical FCJ Asymmetry Model + +**Priority:** `[priority] high` + +**Type:** Experiment model / Peak profile / API naming + +The four empirical peak-asymmetry parameters (`asym_empir_1`…`4`, the +`pd-neut-cwl_pv-asym_empir_pbso4` Verification page) are the +**Bérar–Baldinozzi** correction — FullProf's `P1`–`P4` — a +phenomenological sum of functions in `1/tan θ` and `1/tan 2θ`. It can +fit an asymmetric peak, but the parameters carry **no physical +meaning**, are strongly correlated, do **not** transfer between +datasets, and can misbehave (over-correction, unphysical profile +shapes). + +The **Finger–Cox–Jephcoat (FCJ)** `S_L`/`D_L` model is physically based: +just **two** parameters tied to real instrument geometry (sample and +slit/detector heights over the goniometer radius), with the correct +built-in angular dependence — asymmetry that vanishes at `2θ = 90°` and +reverses past it. Fewer parameters, better-conditioned, and +instrument-meaningful. + +**Two future considerations:** + +1. **Rename** the empirical parameters so the name states what they are + — e.g. `asym_berar_baldinozzi_1`…`4` (or a `berar_baldinozzi_p*` + form) — rather than the generic `asym_empir_*`, which hides their + origin and conflates "empirical asymmetry" with the specific + Bérar–Baldinozzi formula. +2. **Add the FCJ model alongside** the empirical one (not as a + replacement), as a switchable asymmetry choice, so users can pick the + physically-based two-parameter model when the instrument geometry is + known and fall back to the empirical correction otherwise. + +**Relates to:** the asymmetry discrepancy tracked on the +`pd-neut-cwl_pv-asym_empir_pbso4` Verification page (currently in +`docs/docs/verification/ci_skip.txt`), and the TCH/FCJ work noted on the +`pd-neut-cwl_tch-fcj_lab6` page. + +**Depends on:** calculator-backend support for the FCJ asymmetry +parameters (cryspy/crysfml) before the second item can be wired through. + +**Recommended-priority note:** Physical FCJ asymmetry model plus +renaming `asym_empir_*`; unblocks further CI-skipped asymmetry pages. +**Tier 3 (user-visible roadmap feature).** diff --git a/docs/dev/issues/open/highest_calculate-structure-factors-contract-disagrees-across-backends-and-caller.md b/docs/dev/issues/open/highest_calculate-structure-factors-contract-disagrees-across-backends-and-caller.md new file mode 100644 index 000000000..6b0142e95 --- /dev/null +++ b/docs/dev/issues/open/highest_calculate-structure-factors-contract-disagrees-across-backends-and-caller.md @@ -0,0 +1,35 @@ +# 138. `calculate_structure_factors` Contract Disagrees Across Backends and Caller + +**Priority:** `[priority] highest` + +**Type:** Correctness / Maintainability + +`CalculatorBase.calculate_structure_factors` declares +`(structure, experiment, *, called_by_minimizer) -> None`, but the +single real caller (`bragg_sc.py`) unpacks a `(stol, raw_calc)` tuple. +`CryspyCalculator` returns that tuple (and `[], []` on `KeyError`, +silently yielding empty calc downstream), while `CrysfmlCalculator` and +`PdffitCalculator` use a different signature +`(self, structures, experiments)` with no `called_by_minimizer` kwarg. +Selecting crysfml/pdffit for a single-crystal HKL calc raises a +confusing `TypeError`/unpack error instead of a clear "not supported" +message. + +**Fix:** unify the abstract signature and return type to +`tuple[np.ndarray, np.ndarray]`, update all three backends, and have +non-supporting backends raise a clear `NotImplementedError`. + +**TODOs / locations:** + +- [base.py](src/easydiffraction/analysis/calculators/base.py#L49) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L127) +- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L112) +- [pdffit.py](src/easydiffraction/analysis/calculators/pdffit.py#L70) +- [bragg_sc.py](src/easydiffraction/datablocks/experiment/categories/refln/bragg_sc.py#L400) + +**Depends on:** related to issue 63 (the sibling `calculate_pattern` +signature question). + +**Recommended-priority note:** Promoted to **highest** by the 2026-06-13 +audit: a confirmed correctness defect — the backend contract mismatch +causes a wrong/crashing single-crystal HKL calculation. diff --git a/docs/dev/issues/open/highest_cryspy-diverges-on-tof-jorgensen-von-dreele-lorentzian.md b/docs/dev/issues/open/highest_cryspy-diverges-on-tof-jorgensen-von-dreele-lorentzian.md new file mode 100644 index 000000000..6b53095af --- /dev/null +++ b/docs/dev/issues/open/highest_cryspy-diverges-on-tof-jorgensen-von-dreele-lorentzian.md @@ -0,0 +1,31 @@ +# 130. cryspy Diverges on TOF Jorgensen–Von Dreele Lorentzian + +**Priority:** `[priority] highest` + +**Type:** Correctness + +For time-of-flight powder data using the Jorgensen–Von Dreele peak +profile, the `cryspy` backend diverges from FullProf and `crysfml` +whenever the Lorentzian term (`broad_lorentz_gamma_*`) is non-zero. On +the Si Verification reference case the profile difference reaches ≈22% +with an integrated-intensity ratio ≈0.72–0.76, while `crysfml` matches +FullProf to <1%. When the Lorentzian term is zero (NaCaAlF) `cryspy` +agrees to <1%, which localises the problem to the cryspy translation of +the pseudo-Voigt (Gaussian ⊗ Lorentzian) mixing for TOF. + +**Fix:** verify how `broad_lorentz_gamma_*` is passed to cryspy for the +`jorgensen-von-dreele` profile and reconcile the convention with +crysfml/FullProf. + +**Visible on:** the Si TOF Verification page (`pd-neut-tof_jvd_si`), +whose closeness table flags the `cryspy` rows in red — reported via a +non-raising agreement check, not enforced, so CI stays green. +Re-introduce a strict check, or skip the page via +`docs/docs/verification/ci_skip.txt`, once work on the cryspy backend +begins. + +**Depends on:** nothing. + +**Recommended-priority note:** cryspy TOF Jorgensen–Von Dreele +Lorentzian is ~22% off and has a CI-skipped verification page (paired +with #134). **Tier 1 (do first).** diff --git a/docs/dev/issues/open/highest_investigate-ed-crysfml-tof-jorgensen-profile-discrepancy.md b/docs/dev/issues/open/highest_investigate-ed-crysfml-tof-jorgensen-profile-discrepancy.md new file mode 100644 index 000000000..460762fde --- /dev/null +++ b/docs/dev/issues/open/highest_investigate-ed-crysfml-tof-jorgensen-profile-discrepancy.md @@ -0,0 +1,25 @@ +# 134. Investigate ed-crysfml TOF Jorgensen Profile Discrepancy + +**Priority:** `[priority] highest` + +**Type:** Correctness + +For time-of-flight powder data using the plain Jorgensen profile +(back-to-back exponentials ⊗ Gaussian, no Lorentzian), the `crysfml` +backend diverges from FullProf and `cryspy` after the scale is fitted: +the profile is ≈8.5% off with an integrated-area ratio ≈1.09 (corr +≈0.997), while `cryspy` matches FullProf. This localises the problem to +the crysfml translation of the Jorgensen TOF profile, and is +complementary to the `cryspy` Jorgensen–Von Dreele Lorentzian divergence +tracked in issue 130. + +**Visible on:** the Si TOF Jorgensen Verification page +(`pd-neut-tof_j_si`), currently skipped via +`docs/docs/verification/ci_skip.txt`. Re-enable the page (or tighten its +agreement check) once the crysfml profile is reconciled. + +**Depends on:** nothing. + +**Recommended-priority note:** crysfml TOF Jorgensen is ~8.5% off and +has a CI-skipped verification page (paired with #130). **Tier 1 (do +first).** diff --git a/docs/dev/issues/open/highest_model-sample-absorption-debye-scherrer-r.md b/docs/dev/issues/open/highest_model-sample-absorption-debye-scherrer-r.md new file mode 100644 index 000000000..82dd56cbb --- /dev/null +++ b/docs/dev/issues/open/highest_model-sample-absorption-debye-scherrer-r.md @@ -0,0 +1,86 @@ +# 119. Model Sample Absorption (Debye–Scherrer, μR) + +**Priority:** `[priority] highest` + +**Type:** Physics / Engine feature + +The calculators (`cryspy`, `crysfml`) apply no sample-absorption +correction. For a cylindrical sample in Debye–Scherrer geometry this is +an angle-dependent intensity factor that boosts high-angle peaks. The +LaB₆ verification reference (`pd-neut-cwl_tch-fcj_lab6`) was refined in +FullProf with `μR = 0.7`; the unmodelled correction is the _entire_ +intensity residual on the companion `pd-neut-cwl_tch-fcj_abs_lab6` page +(≈5% profile difference), while the `μR = 0` page passes to corr 0.9999. + +**Correction (Hewat, Debye–Scherrer), validated to 4 decimals against +FullProf output:** + +``` +A(θ) = exp( -(1.7133 − 0.0368·sin²θ)·μR + (0.0927 + 0.375·sin²θ)·μR² ) +``` + +A Lobanov–Alte-da-Veiga form covers `μR > 3`. + +**Design:** captured in +[`adrs/accepted/model-sample-absorption.md`](../../adrs/accepted/model-sample-absorption.md) +— a switchable `experiment.absorption` category (mirroring `extinction`) +with a calculator-independent A(θ) envelope. + +**What the backends actually provide (corrected):** + +- `cryspy`: **no** absorption code at all (only Debye–Waller and sphere + _extinction_); its CW intensity loop has no slot to multiply A(θ). +- `crysfml`: CrysFML08 implements `Lorentz_abs_CW` in Fortran, but it is + **not** wrapped in `PythonAPI/`, and the high-level + `cw_powder_pattern_from_dict` path we call applies a plain Lorentz + factor with no absorption. So it is **not** reachable through + pycrysfml today without upstream changes. + +**Implication:** neither backend can apply the correction internally +without changes we do not own. The chosen approach computes A(θ) in +EasyDiffraction and applies it as a pointwise envelope on the calculated +pattern, identically for both calculators (see the ADR). + +**Note:** absorption is nearly degenerate with Biso + scale (its angle +term is linear in `sin²θ`, like the Debye–Waller), so refining Biso can +partly absorb it — but that biases Biso, so an explicit correction is +preferable. In FullProf `μR` is normally **fixed**, not refined. + +**References:** + +- A. W. Hewat, _Acta Cryst._ A35 (1979) 248 — cylindrical absorption. +- N. N. Lobanov & L. Alte da Veiga, 6th EPDIC, Abstract P12-16 (1998). +- CrysFML08: + [`Src/CFML_Powder/Pow_Lorentz_Absorption.f90`](https://code.ill.fr/scientific-software/CrysFML2008/-/blob/master/Src/CFML_Powder/Pow_Lorentz_Absorption.f90), + `Lorentz_abs_CW`. +- FullProf splits absorption into a refineable **magnitude** and a + **type**: CW uses `μR` on the `.pcr` Lambda line (fixed there — no + refinement codeword), with the cylindrical Hewat form implied; TOF + uses `Iabscor` (`1` flat plate, `2` cylinder, `3` exponential + `exp(−ABS·λᶜ)`). `Cthm`/`Rpolarz`/`2nd-muR` on the Lambda line are + polarization and container terms, not the primary absorption knob. + +**Depends on:** the switchable `experiment.absorption` category in the +ADR above (supersedes the earlier "add a `μR` instrument parameter" +sketch). + +**Recommended-priority note:** Accounts for the entire intensity +residual on the LaB₆ verification page; well-specified (Hewat formula). +**Tier 1 (do first).** + +**Architecture note (absorption correction).** Both backends return only +a finished, convolved profile to the EasyDiffraction layer +(`cryspy.calculate_pattern` → `signal_plus + signal_minus`; +`crysfml.calculate_pattern` → `np.asarray(y)`), and neither exposes a CW +absorption knob. So: + +- An **in-project point-wise** `A(2θ)` correction is feasible now — + multiply the summed structure profile by `A` in `bragg_pd.py` _before_ + adding the background (`_set_intensity_calc(calc + intensity_bkg)`), + reusing the per-phase scale-factor precedent. Backend-agnostic, a + small change plus a `μR` parameter (follows the + `calib_sample_displacement` SyCos precedent). +- The **physically-exact per-reflection** `A(θ_hkl)`-before-convolution + is **not** possible in our layer (both engines convolve internally); + it requires owning the engine — the motivation of the + in-house-calculation-engine ADR. diff --git a/docs/dev/issues/open/highest_retain-per-experiment-fitted-parameters-for-plotting.md b/docs/dev/issues/open/highest_retain-per-experiment-fitted-parameters-for-plotting.md new file mode 100644 index 000000000..79cbd9e27 --- /dev/null +++ b/docs/dev/issues/open/highest_retain-per-experiment-fitted-parameters-for-plotting.md @@ -0,0 +1,21 @@ +# 85. Retain Per-Experiment Fitted Parameters for Plotting + +**Priority:** `[priority] highest` + +**Type:** Correctness / UX + +In `single` fit mode, only the last experiment's fit results are +retained because structure parameters are overwritten on each iteration. +This means earlier experiments cannot be plotted correctly after +fitting. + +**Fix:** after fitting each experiment, store a snapshot of its fitted +parameters (both structure and experiment) so that any experiment can be +re-plotted or inspected later. Also clarify: does `fit_results` need to +keep the last mutable parameter set after adding a snapshot? + +**Depends on:** nothing (issue 78 resolved). + +**Recommended-priority note:** In `single` mode only the last +experiment's results survive, so earlier experiments plot incorrectly +after fitting. **Tier 1 (do first).** diff --git a/docs/dev/issues/open/highest_unify-uncertainty-floor-handling-across-bragg-pd-single-crystal-and-pdf-data.md b/docs/dev/issues/open/highest_unify-uncertainty-floor-handling-across-bragg-pd-single-crystal-and-pdf-data.md new file mode 100644 index 000000000..1f455cfa4 --- /dev/null +++ b/docs/dev/issues/open/highest_unify-uncertainty-floor-handling-across-bragg-pd-single-crystal-and-pdf-data.md @@ -0,0 +1,40 @@ +# 140. Unify Uncertainty-Floor Handling Across Bragg PD, Single-Crystal, and PDF Data + +**Priority:** `[priority] highest` + +**Type:** Correctness / Robustness + +The minimizer residual divides by the measured-uncertainty array, so a +zero/NaN/negative uncertainty produces `inf`/`NaN` residuals fed +silently to the minimiser. The floor is applied inconsistently: + +- Bragg powder replaces near-zero uncertainties with `1.0`, but its + guard is `original < _MIN_UNCERTAINTY`, which does **not** catch `NaN` + (NaN comparisons are False) or negative values. +- Single-crystal `intensity_meas_su` has **no** guard at all. +- Total-scattering (PDF) `g_r_meas_su` has **no** guard, and the PDF + ASCII loader never applies the `< _MIN_UNCERTAINTY → 1.0` substitution + that the Bragg loader does. +- `_MIN_UNCERTAINTY = 0.0001` is duplicated in two modules, and the PDF + ASCII default `0.03` is a third independent literal. + +**Fix:** centralize a single finite-positive uncertainty-floor policy on +the descriptor (per the existing TODO) and apply it uniformly to all +three data families and all loaders; reject/clamp non-finite and +non-positive values at the boundary. + +**TODOs / locations:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L648) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L673) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L324) +- [bragg_sc.py](src/easydiffraction/datablocks/experiment/categories/refln/bragg_sc.py#L466) +- [fitting.py](src/easydiffraction/analysis/fitting.py#L449) + +**Depends on:** supersedes the narrower issue 27 (Bragg PD zero +uncertainty). Related to issue 15 (joint-fit weights). + +**Recommended-priority note:** Promoted to **highest** by the 2026-06-13 +audit: inconsistent uncertainty-floor handling yields silent NaN/inf +residuals — the same residual-safety class as the Tier 1 joint-fit +weight issues (#3 / #15). diff --git a/docs/dev/issues/open/highest_unknown-switchable-category-type-on-cif-restore-silently-drops-parameters.md b/docs/dev/issues/open/highest_unknown-switchable-category-type-on-cif-restore-silently-drops-parameters.md new file mode 100644 index 000000000..cbe3db383 --- /dev/null +++ b/docs/dev/issues/open/highest_unknown-switchable-category-type-on-cif-restore-silently-drops-parameters.md @@ -0,0 +1,38 @@ +# 139. Unknown Switchable-Category Type on CIF Restore Silently Drops Parameters + +**Priority:** `[priority] highest` + +**Type:** Robustness + +On restore, `_restore_switchable_types` calls each swap hook with +`strict=False`; an unrecognized `_peak.type` / `_background.type` / +`_calculator.type` / `_extinction.type` (hand-edited or version-skewed +CIF) only logs a suppressible warning and leaves the **default** +implementation active. The subsequent `category.from_cif(block)` then +silently drops every parameter belonging to the intended implementation, +because those descriptors do not exist on the default — yielding a +quietly-wrong restored model. Persisted-state restore is a boundary +input per `AGENTS.md` and should fail loudly. + +**Fix:** reject an unknown persisted type tag with a clear, non- +suppressible error during restore. This is exactly the contract the Edi +persistence ADR's **Selector Validation Contract** proposes; the current +code is the concrete pre-ADR behaviour it would correct. + +**TODOs / locations:** + +- [base.py](src/easydiffraction/datablocks/experiment/item/base.py#L687) + — `_replace_peak_profile` `strict=False` path +- [base.py](src/easydiffraction/datablocks/experiment/item/base.py#L350) + — `_swap_calculator` +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L206) + — background swap + +**Depends on:** the Edi persistence ADR +([`edstar-project-persistence.md`](../../adrs/accepted/edstar-project-persistence.md)), +which formalizes the reject-on-disagreement rule. Related to issues +120, 121. + +**Recommended-priority note:** Promoted to **highest** by the 2026-06-13 +audit: a confirmed robustness defect — an unknown persisted type +silently drops parameters on restore (silent wrong-science class). diff --git a/docs/dev/issues/open/highest_validate-joint-fit-weights-before-residual-normalisation.md b/docs/dev/issues/open/highest_validate-joint-fit-weights-before-residual-normalisation.md new file mode 100644 index 000000000..2aa6cfe1c --- /dev/null +++ b/docs/dev/issues/open/highest_validate-joint-fit-weights-before-residual-normalisation.md @@ -0,0 +1,23 @@ +# 15. Validate Joint-Fit Weights Before Residual Normalisation + +**Priority:** `[priority] highest` + +**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. + +**Recommended-priority note:** Joint-fit weight safety (paired with #3): +validate weights before residual normalisation so invalid/all-zero sets +cannot reach the minimiser as `nan`/division-by-zero. **Tier 1 (do +first).** diff --git a/docs/dev/issues/open/low_add-boundary-tests-for-verification-py-fullprof-igor-parsers.md b/docs/dev/issues/open/low_add-boundary-tests-for-verification-py-fullprof-igor-parsers.md new file mode 100644 index 000000000..4c12da9f3 --- /dev/null +++ b/docs/dev/issues/open/low_add-boundary-tests-for-verification-py-fullprof-igor-parsers.md @@ -0,0 +1,22 @@ +# 161. Add Boundary Tests for `verification.py` FullProf/IGOR Parsers + +**Priority:** `[priority] low` + +**Type:** Test coverage + +A `test_verification.py` mirror exists, but the FullProf/IGOR parsers +(`_parse_fullprof_header` fixed-width fallback, `_parse_igor_profile`, +`_parse_array_background`, `_parse_columned_background`, +`load_fullprof_sc_f2calc`) are dense external-file-format parsers — the +"file formats / external-library boundary" cases `AGENTS.md` says must +be tested for every code path. + +**Fix:** add targeted cases for malformed headers, missing tables, and +non-numeric rows in the existing mirror. + +**TODOs / locations:** + +- [verification.py](src/easydiffraction/analysis/verification.py) (tests + in `tests/unit/easydiffraction/analysis/test_verification.py`) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_add-cif-name-validation-or-normalisation-in-parse.md b/docs/dev/issues/open/low_add-cif-name-validation-or-normalisation-in-parse.md new file mode 100644 index 000000000..3096f1fab --- /dev/null +++ b/docs/dev/issues/open/low_add-cif-name-validation-or-normalisation-in-parse.md @@ -0,0 +1,14 @@ +# 59. Add CIF Name Validation or Normalisation in Parse + +**Priority:** `[priority] low` + +**Type:** Robustness + +`io/cif/parse.py` has a TODO about adding a validator or normalisation +step. + +**TODOs:** + +- [parse.py](src/easydiffraction/io/cif/parse.py#L29) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_add-debug-mode-logging-for-calculator-imports.md b/docs/dev/issues/open/low_add-debug-mode-logging-for-calculator-imports.md new file mode 100644 index 000000000..40355cc64 --- /dev/null +++ b/docs/dev/issues/open/low_add-debug-mode-logging-for-calculator-imports.md @@ -0,0 +1,20 @@ +# 19. Add Debug-Mode Logging for Calculator Imports + +**Priority:** `[priority] low` + +**Type:** Diagnostics + +Several calculator modules have commented-out print statements for +import success/failure. These should be wired into the logging system +under a debug level. + +**TODOs:** + +- [pdffit.py](src/easydiffraction/analysis/calculators/pdffit.py#L34) +- [pdffit.py](src/easydiffraction/analysis/calculators/pdffit.py#L37) +- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L19) +- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L23) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L25) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L28) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_add-future-enum-extensions.md b/docs/dev/issues/open/low_add-future-enum-extensions.md new file mode 100644 index 000000000..5086f970c --- /dev/null +++ b/docs/dev/issues/open/low_add-future-enum-extensions.md @@ -0,0 +1,23 @@ +# 9. Add Future Enum Extensions + +**Priority:** `[priority] low` + +**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. diff --git a/docs/dev/issues/open/low_add-point-id-to-excluded-regions.md b/docs/dev/issues/open/low_add-point-id-to-excluded-regions.md new file mode 100644 index 000000000..263509613 --- /dev/null +++ b/docs/dev/issues/open/low_add-point-id-to-excluded-regions.md @@ -0,0 +1,14 @@ +# 54. Add `point_id` to Excluded Regions + +**Priority:** `[priority] low` + +**Type:** Completeness + +`ExcludedRegion` has a TODO to add `point_id` similar to background +categories. + +**TODOs:** + +- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L33) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_add-public-api-to-clear-a-project-path-cli-fit-dry.md b/docs/dev/issues/open/low_add-public-api-to-clear-a-project-path-cli-fit-dry.md new file mode 100644 index 000000000..5c800c552 --- /dev/null +++ b/docs/dev/issues/open/low_add-public-api-to-clear-a-project-path-cli-fit-dry.md @@ -0,0 +1,20 @@ +# 157. Add Public API to Clear a Project Path (CLI `fit --dry`) + +**Priority:** `[priority] low` + +**Type:** API safety + +The CLI dry-run path sets `project.info._path = None` directly instead +of using the public `path` setter (which only accepts a +`Path`-convertible value, with no documented way to clear it). This +couples the CLI to a private attribute and means there is no supported +public API to "unset" a project path. + +**Fix:** add a public method/setter on `ProjectInfo` to clear the path +(e.g. accept `None`), then call that from the CLI. + +**TODOs / locations:** + +- [\_\_main\_\_.py](src/easydiffraction/__main__.py#L266) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_add-show-supported-calculators-on-analysis-or-project.md b/docs/dev/issues/open/low_add-show-supported-calculators-on-analysis-or-project.md new file mode 100644 index 000000000..365577ee4 --- /dev/null +++ b/docs/dev/issues/open/low_add-show-supported-calculators-on-analysis-or-project.md @@ -0,0 +1,12 @@ +# 75. Add `show_supported_calculators()` on Analysis or Project + +**Priority:** `[priority] low` + +**Type:** API completeness + +`show_calculator_types()` exists per-experiment, but there is no +project/analysis-level method to list all available calculator engines. +Users exploring the API have no single entry point to see what +calculators are installed. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_add-test-coverage-for-list-tutorials-two-line-rendering.md b/docs/dev/issues/open/low_add-test-coverage-for-list-tutorials-two-line-rendering.md new file mode 100644 index 000000000..872bab7bd --- /dev/null +++ b/docs/dev/issues/open/low_add-test-coverage-for-list-tutorials-two-line-rendering.md @@ -0,0 +1,19 @@ +# 111. Add Test Coverage for `list_tutorials` Two-Line Rendering + +**Priority:** `[priority] low` + +**Type:** Test coverage + +The `list_tutorials` table gained a styled two-line cell (colored title +plus dimmed description), a terminal-only `in_jupyter()` gate that falls +back to the plain title, and a new optional `width` parameter on the +table render path. Existing tests only assert that titles appear in the +output. + +**Fix:** add unit tests for the description line appearing in the +terminal (non-Jupyter) path, the Jupyter-gated path showing the plain +title with no literal Rich markup, and the `width` parameter sizing the +rendered Rich table. Run `pixi run fix` / `check` / `unit-tests` to +confirm the shared-renderer signature change. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_align-dir-with-help-filtering.md b/docs/dev/issues/open/low_align-dir-with-help-filtering.md new file mode 100644 index 000000000..1997cdcef --- /dev/null +++ b/docs/dev/issues/open/low_align-dir-with-help-filtering.md @@ -0,0 +1,13 @@ +# 128. Align `dir()` With Help Filtering + +**Priority:** `[priority] low` + +**Type:** Discoverability + +`help()` now hides inactive analysis categories by fitting mode, while +`dir()` and tab completion still expose the full class surface. + +**Fix:** decide whether `dir()` should mirror the help filter or remain +an always-complete developer surface. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_auto-extract-peakprofiletypeenum-from-peak-classes.md b/docs/dev/issues/open/low_auto-extract-peakprofiletypeenum-from-peak-classes.md new file mode 100644 index 000000000..427e63e7f --- /dev/null +++ b/docs/dev/issues/open/low_auto-extract-peakprofiletypeenum-from-peak-classes.md @@ -0,0 +1,18 @@ +# 34. Auto-Extract `PeakProfileTypeEnum` from Peak Classes + +**Priority:** `[priority] low` + +**Type:** Design + +Three related TODOs in `enums.py` ask whether `PeakProfileTypeEnum` +values can be auto-extracted from the actual peak profile classes in +`peak/cwl.py`, `tof.py`, `total.py` instead of being hardcoded, and +whether the same pattern can be reused for other enums. + +**TODOs:** + +- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L153) +- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L157) +- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L158) + +**Depends on:** related to issue 9. diff --git a/docs/dev/issues/open/low_auto-resolve-plot-param-x-axis-descriptor-and-add-units.md b/docs/dev/issues/open/low_auto-resolve-plot-param-x-axis-descriptor-and-add-units.md new file mode 100644 index 000000000..2a9219867 --- /dev/null +++ b/docs/dev/issues/open/low_auto-resolve-plot-param-x-axis-descriptor-and-add-units.md @@ -0,0 +1,13 @@ +# 86. Auto-Resolve `plot_param` X-Axis Descriptor and Add Units + +**Priority:** `[priority] low` + +**Type:** UX + +`plot_param_series` currently requires the user to manually specify the +x-axis parameter (e.g. `x_axis='temperature'` → look up +`diffrn.ambient_temperature`). This should be auto-resolved from the +parameter name. Additionally, axis labels should include units (e.g. +"Temperature (K)"). + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_automate-space-group-cif-name-variants.md b/docs/dev/issues/open/low_automate-space-group-cif-name-variants.md new file mode 100644 index 000000000..bb9c11298 --- /dev/null +++ b/docs/dev/issues/open/low_automate-space-group-cif-name-variants.md @@ -0,0 +1,15 @@ +# 49. Automate Space Group CIF Name Variants + +**Priority:** `[priority] low` + +**Type:** Maintainability + +`SpaceGroup.name_h_m` lists multiple CIF tag variants (with `.` and +`_`). A TODO asks to keep only the dotted version and automate variant +generation. + +**TODOs:** + +- [default.py](src/easydiffraction/datablocks/structure/categories/space_group/default.py#L52) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_check-cryspy-single-crystal-instrument-mapping.md b/docs/dev/issues/open/low_check-cryspy-single-crystal-instrument-mapping.md new file mode 100644 index 000000000..c7fdd91b3 --- /dev/null +++ b/docs/dev/issues/open/low_check-cryspy-single-crystal-instrument-mapping.md @@ -0,0 +1,14 @@ +# 22. Check CrysPy Single-Crystal Instrument Mapping + +**Priority:** `[priority] low` + +**Type:** Correctness + +`_cif_instrument_section` uses an empty `instrument_mapping` dict for +single crystal and a `TODO: Check this mapping!` marker. + +**TODOs:** + +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L506) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_check-whether-mark-dirty-in-set-value-is-actually-used.md b/docs/dev/issues/open/low_check-whether-mark-dirty-in-set-value-is-actually-used.md new file mode 100644 index 000000000..442d7721c --- /dev/null +++ b/docs/dev/issues/open/low_check-whether-mark-dirty-in-set-value-is-actually-used.md @@ -0,0 +1,14 @@ +# 41. Check Whether `_mark_dirty` in `_set_value` is Actually Used + +**Priority:** `[priority] low` + +**Type:** Cleanup + +`GenericDescriptorBase._set_value` marks the parent datablock dirty with +a TODO questioning whether this path is exercised. + +**TODOs:** + +- [variable.py](src/easydiffraction/core/variable.py#L154) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_check-whether-not-used-if-loading-from-cif-code-is-needed.md b/docs/dev/issues/open/low_check-whether-not-used-if-loading-from-cif-code-is-needed.md new file mode 100644 index 000000000..50b7c2d8f --- /dev/null +++ b/docs/dev/issues/open/low_check-whether-not-used-if-loading-from-cif-code-is-needed.md @@ -0,0 +1,14 @@ +# 64. Check Whether `_not_used_if_loading_from_cif` Code is Needed + +**Priority:** `[priority] low` + +**Type:** Cleanup + +`BraggPdExperiment` has a block marked +`TODO: Not used if loading from cif file?`. + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/item/bragg_pd.py#L112) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_clarify-bragg-pd-data-collection-description.md b/docs/dev/issues/open/low_clarify-bragg-pd-data-collection-description.md new file mode 100644 index 000000000..9abf62626 --- /dev/null +++ b/docs/dev/issues/open/low_clarify-bragg-pd-data-collection-description.md @@ -0,0 +1,15 @@ +# 28. Clarify Bragg PD Data Collection Description + +**Priority:** `[priority] low` + +**Type:** Cleanup + +`PdCwlDataCollection` has a commented-out `_description` and a +`TODO: ???` marker. + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L482) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L304) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_clarify-cell-update-usage-of-called-by-minimizer.md b/docs/dev/issues/open/low_clarify-cell-update-usage-of-called-by-minimizer.md new file mode 100644 index 000000000..e21124bfb --- /dev/null +++ b/docs/dev/issues/open/low_clarify-cell-update-usage-of-called-by-minimizer.md @@ -0,0 +1,13 @@ +# 50. Clarify `Cell._update` Usage of `called_by_minimizer` + +**Priority:** `[priority] low` + +**Type:** Cleanup + +`Cell._update` deletes `called_by_minimizer` with a `TODO: ???`. + +**TODOs:** + +- [default.py](src/easydiffraction/datablocks/structure/categories/cell/default.py#L146) + +**Depends on:** related to issue 11. diff --git a/docs/dev/issues/open/low_clarify-dtype-usage-in-data-point-arrays.md b/docs/dev/issues/open/low_clarify-dtype-usage-in-data-point-arrays.md new file mode 100644 index 000000000..64a82c30a --- /dev/null +++ b/docs/dev/issues/open/low_clarify-dtype-usage-in-data-point-arrays.md @@ -0,0 +1,26 @@ +# 26. Clarify `dtype` Usage in Data Point Arrays + +**Priority:** `[priority] low` + +**Type:** Cleanup + +Many array constructions pass `dtype=float` or `dtype=object` with a +`TODO: needed? DataTypes.NUMERIC?` comment. Decide whether explicit +dtype is needed and align with `DataTypes`. + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L415) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L423) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L431) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L456) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L466) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L474) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L543) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L556) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L624) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L637) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L370) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L378) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_clean-up-cif-deserialisation-helpers.md b/docs/dev/issues/open/low_clean-up-cif-deserialisation-helpers.md new file mode 100644 index 000000000..8be9bb11b --- /dev/null +++ b/docs/dev/issues/open/low_clean-up-cif-deserialisation-helpers.md @@ -0,0 +1,21 @@ +# 57. Clean Up CIF Deserialisation Helpers + +**Priority:** `[priority] low` + +**Type:** Maintainability + +`serialize.py` has several TODOs: verify methods after the +`format_param_value` section, extract a helper for quoted-string +stripping, find a better way to set `_item_type` on +`CategoryCollection`, rename it to `_item_cls`, and remove duplicated +`param_from_cif` logic. + +**TODOs:** + +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L454) +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L562) +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L617) +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L619) +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L656) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_complete-migration-from-render-table-to-tablerenderer.md b/docs/dev/issues/open/low_complete-migration-from-render-table-to-tablerenderer.md new file mode 100644 index 000000000..f0a598f42 --- /dev/null +++ b/docs/dev/issues/open/low_complete-migration-from-render-table-to-tablerenderer.md @@ -0,0 +1,14 @@ +# 62. Complete Migration from `render_table` to `TableRenderer` + +**Priority:** `[priority] low` + +**Type:** Cleanup + +`utils.py` has a temporary `render_table` utility that should be +replaced with `TableRenderer`. + +**TODOs:** + +- [utils.py](src/easydiffraction/utils/utils.py#L510) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_consider-a-common-enumbase-with-default-description.md b/docs/dev/issues/open/low_consider-a-common-enumbase-with-default-description.md new file mode 100644 index 000000000..849fe8aaf --- /dev/null +++ b/docs/dev/issues/open/low_consider-a-common-enumbase-with-default-description.md @@ -0,0 +1,15 @@ +# 36. Consider a Common `EnumBase` with `default()` / `description()` + +**Priority:** `[priority] low` + +**Type:** Design + +`BackgroundTypeEnum` and other enums repeat the same `default()` / +`description()` method pattern. A shared `EnumBase` would reduce +boilerplate. + +**TODOs:** + +- [enums.py](src/easydiffraction/datablocks/experiment/categories/background/enums.py#L10) + +**Depends on:** related to issue 9. diff --git a/docs/dev/issues/open/low_create-update-priority-reference-table-for-categories.md b/docs/dev/issues/open/low_create-update-priority-reference-table-for-categories.md new file mode 100644 index 000000000..574372701 --- /dev/null +++ b/docs/dev/issues/open/low_create-update-priority-reference-table-for-categories.md @@ -0,0 +1,11 @@ +# 71. Create `_update_priority` Reference Table for Categories + +**Priority:** `[priority] low` + +**Type:** Documentation + +Create and maintain a table listing all categories that implement +`_update()`, sorted by their `_update_priority`. This makes the update +order explicit and helps catch priority conflicts. + +**Depends on:** related to issues 11, 39. diff --git a/docs/dev/issues/open/low_cryspy-backend-hardcodes-flag-only-nuclear-no-magnetic-structures.md b/docs/dev/issues/open/low_cryspy-backend-hardcodes-flag-only-nuclear-no-magnetic-structures.md new file mode 100644 index 000000000..3455d0b95 --- /dev/null +++ b/docs/dev/issues/open/low_cryspy-backend-hardcodes-flag-only-nuclear-no-magnetic-structures.md @@ -0,0 +1,21 @@ +# 165. cryspy Backend Hardcodes `flag_only_nuclear` (No Magnetic Structures) + +**Priority:** `[priority] low` + +**Type:** Engine limitation + +The cryspy calculator hardcodes `flag_only_nuclear = True` for all +structures, so magnetic structures cannot be calculated through this +backend. This is an undocumented capability gap a user can hit by +supplying a magnetic model. + +**Fix:** thread the nuclear/magnetic flag from the structure model, and +surface a clear "magnetic structures not yet supported" message until +the backend path is implemented. + +**TODOs / locations:** + +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L176) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L264) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_decide-default-for-alias-constraint-descriptors.md b/docs/dev/issues/open/low_decide-default-for-alias-constraint-descriptors.md new file mode 100644 index 000000000..016f78b91 --- /dev/null +++ b/docs/dev/issues/open/low_decide-default-for-alias-constraint-descriptors.md @@ -0,0 +1,15 @@ +# 45. Decide Default for Alias/Constraint Descriptors + +**Priority:** `[priority] low` + +**Type:** Design + +`Aliases` and `Constraints` categories use `default='_'` with a +`TODO, Maybe None?` marker. + +**TODOs:** + +- [default.py](src/easydiffraction/analysis/categories/aliases/default.py#L40) +- [default.py](src/easydiffraction/analysis/categories/constraints/default.py#L33) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_decide-future-of-show-residual-in-plot-meas-vs-calc.md b/docs/dev/issues/open/low_decide-future-of-show-residual-in-plot-meas-vs-calc.md new file mode 100644 index 000000000..66432ea15 --- /dev/null +++ b/docs/dev/issues/open/low_decide-future-of-show-residual-in-plot-meas-vs-calc.md @@ -0,0 +1,19 @@ +# 132. Decide Future of `show_residual` in `plot_meas_vs_calc` + +**Priority:** `[priority] low` + +**Type:** API cleanup + +Powder Bragg plots now show the residual row by default when +`show_residual=None`, but the public `show_residual` argument still +exists and some call sites still pass `show_residual=True` explicitly. +The API should be clarified: either keep the argument as a compatibility +option, remove it, or standardize a single meaning across powder and +single-crystal plots. + +**TODOs:** + +- [plotting.py](src/easydiffraction/display/plotting.py#L459) +- [\_\_main\_\_.py](src/easydiffraction/__main__.py#L105) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_decide-how-mid-run-sequential-failures-persist.md b/docs/dev/issues/open/low_decide-how-mid-run-sequential-failures-persist.md new file mode 100644 index 000000000..9dd2e0943 --- /dev/null +++ b/docs/dev/issues/open/low_decide-how-mid-run-sequential-failures-persist.md @@ -0,0 +1,13 @@ +# 126. Decide How Mid-Run Sequential Failures Persist + +**Priority:** `[priority] low` + +**Type:** Recovery design + +If a sequential fit fails partway through, the recovery and persistence +contract for `analysis/results.csv` is not fully specified. + +**Fix:** define whether partial CSV output is authoritative for resume, +left untouched for manual recovery, or replaced on the next run. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_decide-whether-cli-should-override-extract-rules.md b/docs/dev/issues/open/low_decide-whether-cli-should-override-extract-rules.md new file mode 100644 index 000000000..7cf1d5d8e --- /dev/null +++ b/docs/dev/issues/open/low_decide-whether-cli-should-override-extract-rules.md @@ -0,0 +1,14 @@ +# 127. Decide Whether CLI Should Override Extract Rules + +**Priority:** `[priority] low` + +**Type:** CLI design + +The CLI can override mode and worker settings, but persisted +`sequential_fit_extract` rules are not yet overridable from the command +line. + +**Fix:** decide whether extraction rules stay project-file-only or gain +an explicit CLI override syntax. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_decide-whether-sequential-extraction-should-be-cached.md b/docs/dev/issues/open/low_decide-whether-sequential-extraction-should-be-cached.md new file mode 100644 index 000000000..d8af2fa24 --- /dev/null +++ b/docs/dev/issues/open/low_decide-whether-sequential-extraction-should-be-cached.md @@ -0,0 +1,13 @@ +# 125. Decide Whether Sequential Extraction Should Be Cached + +**Priority:** `[priority] low` + +**Type:** Performance + +Sequential metadata extraction currently re-reads input files when the +run is repeated or resumed. + +**Fix:** decide whether extracted `diffrn.*` values should be cached in +`analysis/results.csv` only, or also in a dedicated reusable cache. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_decide-whether-single-fit-needs-a-future-category.md b/docs/dev/issues/open/low_decide-whether-single-fit-needs-a-future-category.md new file mode 100644 index 000000000..a20623dbe --- /dev/null +++ b/docs/dev/issues/open/low_decide-whether-single-fit-needs-a-future-category.md @@ -0,0 +1,15 @@ +# 129. Decide Whether `single_fit` Needs a Future Category + +**Priority:** `[priority] low` + +**Type:** Scope planning + +Single mode currently has no dedicated persisted category. Future +single-mode settings could require one, but the threshold is not yet +defined. + +**Fix:** decide what concrete single-mode behaviour would justify a +`single_fit` category instead of keeping the mode configuration on the +owner only. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_decide-whether-to-apply-typechecked-to-all-public-methods.md b/docs/dev/issues/open/low_decide-whether-to-apply-typechecked-to-all-public-methods.md new file mode 100644 index 000000000..0b21802e9 --- /dev/null +++ b/docs/dev/issues/open/low_decide-whether-to-apply-typechecked-to-all-public-methods.md @@ -0,0 +1,12 @@ +# 68. Decide Whether to Apply `@typechecked` to All Public Methods + +**Priority:** `[priority] low` + +**Type:** Design + +`@typechecked` is currently applied only in ~24 places (factories, +collections). Decide whether it should be applied systematically to all +public method signatures, or whether custom validation (issue 67) is +preferred. + +**Depends on:** issue 67. diff --git a/docs/dev/issues/open/low_document-category-update-contract.md b/docs/dev/issues/open/low_document-category-update-contract.md new file mode 100644 index 000000000..d84c6c69d --- /dev/null +++ b/docs/dev/issues/open/low_document-category-update-contract.md @@ -0,0 +1,20 @@ +# 11. Document Category `_update` Contract + +**Priority:** `[priority] low` + +**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. diff --git a/docs/dev/issues/open/low_document-fitresultbase-result-kind-default-rationale.md b/docs/dev/issues/open/low_document-fitresultbase-result-kind-default-rationale.md new file mode 100644 index 000000000..7a92e30df --- /dev/null +++ b/docs/dev/issues/open/low_document-fitresultbase-result-kind-default-rationale.md @@ -0,0 +1,22 @@ +# 106. Document `FitResultBase.result_kind` Default Rationale + +**Priority:** `[priority] low` + +**Type:** Code readability **Source:** `minimizer-input-output-split` +review 6. + +Most `FitResultBase` descriptors use `default=None, allow_none=True` so +pre-fit CIF output serializes unknown values as `?`. `result_kind` +intentionally keeps a valid enum default because it drives deterministic +versus Bayesian projection handling, but that exception is not +documented in code. + +**TODOs:** + +- [base.py](src/easydiffraction/analysis/categories/fit_result/base.py#L44) + +**Fix:** add a short code comment near the `result_kind` descriptor +explaining why it keeps a concrete default while unknown result values +use `None`. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_document-param-docstring-fix-and-notebook-prepare-workflow.md b/docs/dev/issues/open/low_document-param-docstring-fix-and-notebook-prepare-workflow.md new file mode 100644 index 000000000..203bd3493 --- /dev/null +++ b/docs/dev/issues/open/low_document-param-docstring-fix-and-notebook-prepare-workflow.md @@ -0,0 +1,16 @@ +# 82. Document `param-docstring-fix` and `notebook-prepare` Workflow + +**Priority:** `[priority] low` + +**Type:** Documentation + +Two manual workflow steps are required between releases/changes: + +1. `pixi run param-docstring-fix` — sync Parameter docstrings. +2. `pixi run notebook-prepare` — regenerate tutorial notebooks from + scripts. + +Document these in `CONTRIBUTING.md` or a relevant ADR so they are not +forgotten. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_draw-adp-ellipsoids-for-beta-tensor-atoms.md b/docs/dev/issues/open/low_draw-adp-ellipsoids-for-beta-tensor-atoms.md new file mode 100644 index 000000000..2c462a55a --- /dev/null +++ b/docs/dev/issues/open/low_draw-adp-ellipsoids-for-beta-tensor-atoms.md @@ -0,0 +1,17 @@ +# 136. Draw ADP Ellipsoids for Beta-Tensor Atoms + +**Priority:** `[priority] low` + +**Type:** Display / Visualization + +The 3D structure view +([builder.py](src/easydiffraction/display/structure/builder.py)) draws +anisotropic displacement ellipsoids only for the `Bani`/`Uani` ADP +types. Atoms stored as the dimensionless `beta` tensor currently fall +through to a plain sphere. Drawing their ellipsoids needs a β→U +conversion in the renderer (using the reciprocal cell), analogous to the +existing B→U step. The model-layer β↔U conversion already exists +(`AtomSite._convert_adp_values_beta`) and could be reused. + +**Depends on:** the β-tensor ADP support +([adp-beta-tensor plan](docs/dev/plans/adp-beta-tensor.md)). diff --git a/docs/dev/issues/open/low_drop-compute-and-ignore-result-kind-validation-in-cif-restore.md b/docs/dev/issues/open/low_drop-compute-and-ignore-result-kind-validation-in-cif-restore.md new file mode 100644 index 000000000..f462a5481 --- /dev/null +++ b/docs/dev/issues/open/low_drop-compute-and-ignore-result-kind-validation-in-cif-restore.md @@ -0,0 +1,20 @@ +# 102. Drop Compute-and-Ignore `result_kind` Validation in CIF Restore + +**Priority:** `[priority] low` + +**Type:** Dead code / clarity **Source:** Review 8 finding F7. +**Recommended:** fold into the emcee-minimizer plan. + +`_restore_persisted_fit_state` +([serialize.py:595-611](../../../src/easydiffraction/io/cif/serialize.py)) +calls `FitResultKindEnum(result_kind_value)` purely for the warning side +effect; the result is discarded. After P1.10 absorbed the +Bayesian-specific categories there is nothing else to do per +`result_kind`. + +**Fix:** replace with a validator helper that takes a string and logs +the warning, or move the warning into `fit_result.result_kind` setter so +invalid values are caught on read. Either removes the "compute and +ignore" pattern. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_expand-cross-engine-verification-coverage.md b/docs/dev/issues/open/low_expand-cross-engine-verification-coverage.md new file mode 100644 index 000000000..de705251e --- /dev/null +++ b/docs/dev/issues/open/low_expand-cross-engine-verification-coverage.md @@ -0,0 +1,16 @@ +# 115. Expand Cross-Engine Verification Coverage + +**Priority:** `[priority] low` + +**Type:** Test coverage / Documentation + +The Verification docs section ships with the framework and the first +cross-engine comparison page (constant-wavelength powder, cryspy ↔ +crysfml). Extend it to the remaining supported combinations declared by +the calculator support matrix — time-of-flight powder (cryspy ↔ crysfml) +and single crystal — so every valid experiment/instrument combination is +documented and regression-checked at least once. Each new page is a +calculation-only `.py` under `docs/docs/verification/` wired into +`script-tests` and `notebook-tests`, with explicit metric tolerances. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_external-link-checking-in-the-docs-gate.md b/docs/dev/issues/open/low_external-link-checking-in-the-docs-gate.md new file mode 100644 index 000000000..de45cc239 --- /dev/null +++ b/docs/dev/issues/open/low_external-link-checking-in-the-docs-gate.md @@ -0,0 +1,16 @@ +# 114. External Link Checking in the Docs Gate + +**Priority:** `[priority] low` + +**Type:** CI / Documentation + +The fast docs gate (`docs-build` + `link-check` + `spell-check`) catches +broken nav/internal links and typos on every push, but does not yet +check external URLs. Add a `lychee` link checker (with an allowlist for +rate-limited/unstable domains), coordinated with the +[Documentation CI and Build Verification](../adrs/accepted/documentation-ci-build.md) +ADR. Run it nightly or on pull requests to avoid flakiness from external +sites. Also covers link-checking of URLs that appear only inside +executed notebook output cells (a feature that does not exist yet). + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_finer-grained-parameter-change-tracking.md b/docs/dev/issues/open/low_finer-grained-parameter-change-tracking.md new file mode 100644 index 000000000..3fcf145b5 --- /dev/null +++ b/docs/dev/issues/open/low_finer-grained-parameter-change-tracking.md @@ -0,0 +1,14 @@ +# 14. Finer-Grained Parameter Change Tracking + +**Priority:** `[priority] low` + +**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. diff --git a/docs/dev/issues/open/low_fix-calculator-calculate-pattern-signature-type.md b/docs/dev/issues/open/low_fix-calculator-calculate-pattern-signature-type.md new file mode 100644 index 000000000..e39556f84 --- /dev/null +++ b/docs/dev/issues/open/low_fix-calculator-calculate-pattern-signature-type.md @@ -0,0 +1,14 @@ +# 63. Fix Calculator `calculate_pattern` Signature Type + +**Priority:** `[priority] low` + +**Type:** Design + +`CalculatorBase.calculate_pattern` takes `structure: Structures` but the +TODO asks whether it should be `Structure` (singular). + +**TODOs:** + +- [base.py](src/easydiffraction/analysis/calculators/base.py#L40) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_fix-cryspy-tof-instrument-default.md b/docs/dev/issues/open/low_fix-cryspy-tof-instrument-default.md new file mode 100644 index 000000000..22525e3f7 --- /dev/null +++ b/docs/dev/issues/open/low_fix-cryspy-tof-instrument-default.md @@ -0,0 +1,14 @@ +# 48. Fix CrysPy TOF Instrument Default + +**Priority:** `[priority] low` + +**Type:** Bug workaround + +`TofInstrument.calib_d_to_tof_quad` defaults to `-0.00001` because +CrysPy does not accept `0`. + +**TODOs:** + +- [tof.py](src/easydiffraction/datablocks/experiment/categories/instrument/tof.py#L95) + +**Depends on:** upstream CrysPy fix. diff --git a/docs/dev/issues/open/low_fix-gitignore-gaps-and-remove-the-stale-absorption-package.md b/docs/dev/issues/open/low_fix-gitignore-gaps-and-remove-the-stale-absorption-package.md new file mode 100644 index 000000000..6ed867c11 --- /dev/null +++ b/docs/dev/issues/open/low_fix-gitignore-gaps-and-remove-the-stale-absorption-package.md @@ -0,0 +1,24 @@ +# 163. Fix `.gitignore` Gaps and Remove the Stale `absorption/` Package + +**Priority:** `[priority] low` + +**Type:** Hygiene + +Several small repository-hygiene gaps: + +- `.gitignore` line `.pyc` ignores only a file literally named `.pyc`, + not `*.pyc` (intended). `__pycache__/` covers the common case, so the + rule as written does nothing. +- The `benchmarks` pixi task writes `benchmark.json` to the repo root, + but `.gitignore` has no entry for it, so it can be staged + accidentally. +- `src/easydiffraction/datablocks/experiment/categories/absorption/` + contains **no tracked source** (only a stale `__pycache__/`); the + category has no implementation yet (see issue 119). Remove the empty + package directory until the absorption category is implemented. + +**Fix:** correct the `.pyc` pattern to `*.pyc`, add `benchmark.json`, +and delete the empty `absorption/` package. + +**Depends on:** nothing (absorption implementation tracked by issue +119). diff --git a/docs/dev/issues/open/low_fix-jupyter-scroll-disabling-for-mkdocs.md b/docs/dev/issues/open/low_fix-jupyter-scroll-disabling-for-mkdocs.md new file mode 100644 index 000000000..ec7d885bd --- /dev/null +++ b/docs/dev/issues/open/low_fix-jupyter-scroll-disabling-for-mkdocs.md @@ -0,0 +1,14 @@ +# 55. Fix Jupyter Scroll Disabling for MkDocs + +**Priority:** `[priority] low` + +**Type:** Docs / UX + +`display/__init__.py` has disabled `JupyterScrollManager` because it +breaks MkDocs builds. + +**TODOs:** + +- [**init**.py](src/easydiffraction/display/__init__.py#L15) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_fix-reversed-abstract-sync-result-to-parameters-signature.md b/docs/dev/issues/open/low_fix-reversed-abstract-sync-result-to-parameters-signature.md new file mode 100644 index 000000000..8d430104e --- /dev/null +++ b/docs/dev/issues/open/low_fix-reversed-abstract-sync-result-to-parameters-signature.md @@ -0,0 +1,21 @@ +# 155. Fix Reversed Abstract `_sync_result_to_parameters` Signature + +**Priority:** `[priority] low` + +**Type:** Maintainability + +The abstract declaration is +`_sync_result_to_parameters(self, raw_result, parameters)`, but the base +caller (`_finalize_fit`) and every concrete override use +`(parameters, raw_result)`. Calls are positional so runtime is correct, +but the abstract signature and docstring are misleading and would trip +up a new minimizer author. + +**Fix:** correct the abstract signature/docstring to +`(parameters, raw_result)`. + +**TODOs / locations:** + +- [base.py](src/easydiffraction/analysis/minimizers/base.py#L169) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_handle-zero-uncertainty-in-bragg-pd-data.md b/docs/dev/issues/open/low_handle-zero-uncertainty-in-bragg-pd-data.md new file mode 100644 index 000000000..417fa708e --- /dev/null +++ b/docs/dev/issues/open/low_handle-zero-uncertainty-in-bragg-pd-data.md @@ -0,0 +1,19 @@ +# 27. Handle Zero Uncertainty in Bragg PD Data + +**Priority:** `[priority] low` + +**Type:** Correctness + +A temporary workaround exists for zero uncertainties in measured data. + +**Superseded by issue 140**, which broadens this into a single +finite-positive uncertainty-floor policy applied uniformly across Bragg +powder, single-crystal, and PDF data (the Bragg PD guard does not catch +NaN/negative values, and SC/PDF have no guard at all). Keep this entry +as the original narrow note; act on it through issue 140. + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L442) + +**Depends on:** see issue 140. diff --git a/docs/dev/issues/open/low_help-mislabels-boolean-descriptors-as-numeric.md b/docs/dev/issues/open/low_help-mislabels-boolean-descriptors-as-numeric.md new file mode 100644 index 000000000..dce0f0d3d --- /dev/null +++ b/docs/dev/issues/open/low_help-mislabels-boolean-descriptors-as-numeric.md @@ -0,0 +1,20 @@ +# 152. `help()` Mislabels Boolean Descriptors as "numeric" + +**Priority:** `[priority] low` + +**Type:** API safety / UX + +In `CategoryItem.help()` the type column is computed as +`'string' if isinstance(val, GenericStringDescriptor) else 'numeric'`, +so any non-string descriptor (including a `BoolDescriptor`) is shown to +the user as type "numeric". For a scientist reading `help()` to learn +what to type, this is a misleading hint. + +**Fix:** map the descriptor families (string / numeric / bool) +explicitly. + +**TODOs / locations:** + +- [category.py](src/easydiffraction/core/category.py#L108) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_improve-error-handling-in-crystallography-utilities.md b/docs/dev/issues/open/low_improve-error-handling-in-crystallography-utilities.md new file mode 100644 index 000000000..199fa9374 --- /dev/null +++ b/docs/dev/issues/open/low_improve-error-handling-in-crystallography-utilities.md @@ -0,0 +1,16 @@ +# 47. Improve Error Handling in Crystallography Utilities + +**Priority:** `[priority] low` + +**Type:** Diagnostics + +`crystallography.py` logs errors with a TODO asking whether these should +raise `ValueError` or provide better diagnostics. + +**TODOs:** + +- [crystallography.py](src/easydiffraction/crystallography/crystallography.py#L39) +- [crystallography.py](src/easydiffraction/crystallography/crystallography.py#L45) +- [crystallography.py](src/easydiffraction/crystallography/crystallography.py#L84) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_improve-jointfititem-descriptions.md b/docs/dev/issues/open/low_improve-jointfititem-descriptions.md new file mode 100644 index 000000000..ba5eb5f54 --- /dev/null +++ b/docs/dev/issues/open/low_improve-jointfititem-descriptions.md @@ -0,0 +1,16 @@ +# 46. Improve `JointFitItem` Descriptions + +**Priority:** `[priority] low` + +**Type:** Naming + +`JointFitItem` uses `name='experiment_id'`, but two description fields +are still incomplete. + +**TODOs:** + +- [default.py](src/easydiffraction/analysis/categories/joint_fit/default.py#L31) +- [default.py](src/easydiffraction/analysis/categories/joint_fit/default.py#L32) +- [default.py](src/easydiffraction/analysis/categories/joint_fit/default.py#L41) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_improve-update-priority-handling-in-categories.md b/docs/dev/issues/open/low_improve-update-priority-handling-in-categories.md new file mode 100644 index 000000000..1248901c1 --- /dev/null +++ b/docs/dev/issues/open/low_improve-update-priority-handling-in-categories.md @@ -0,0 +1,20 @@ +# 39. Improve `_update_priority` Handling in Categories + +**Priority:** `[priority] low` + +**Type:** Design + +`CategoryItem` and `CategoryCollection` both define +`_update_priority = 10` with a TODO to set different defaults and use +them during CIF serialisation. The duplicated `_update` no-op methods +are also marked. + +**TODOs:** + +- [category.py](src/easydiffraction/core/category.py#L21) +- [category.py](src/easydiffraction/core/category.py#L23) +- [category.py](src/easydiffraction/core/category.py#L32) +- [category.py](src/easydiffraction/core/category.py#L174) +- [category.py](src/easydiffraction/core/category.py#L199) + +**Depends on:** related to issues 10, 11. diff --git a/docs/dev/issues/open/low_investigate-pycrysfml-pattern-length-discrepancy.md b/docs/dev/issues/open/low_investigate-pycrysfml-pattern-length-discrepancy.md new file mode 100644 index 000000000..b340a9c70 --- /dev/null +++ b/docs/dev/issues/open/low_investigate-pycrysfml-pattern-length-discrepancy.md @@ -0,0 +1,16 @@ +# 23. Investigate PyCrysFML Pattern Length Discrepancy + +**Priority:** `[priority] low` + +**Type:** Correctness + +CrysFML calculator adjusts pattern length post-calculation with a TODO +asking to investigate the origin of the off-by-one discrepancy. The same +epsilon workaround appears in the dict builder. + +**TODOs:** + +- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L124) +- [crysfml.py](src/easydiffraction/analysis/calculators/crysfml.py#L253) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_let-more-tables-adapt-to-terminal-width.md b/docs/dev/issues/open/low_let-more-tables-adapt-to-terminal-width.md new file mode 100644 index 000000000..52ae209f4 --- /dev/null +++ b/docs/dev/issues/open/low_let-more-tables-adapt-to-terminal-width.md @@ -0,0 +1,23 @@ +# 109. Let More Tables Adapt to Terminal Width + +**Priority:** `[priority] low` + +**Type:** UX / Display + +`list_tutorials` now renders its table at the real terminal width via a +new optional `width` parameter threaded through the table render path +(`render_table` → `TableRenderer.render` → backend `render`; Rich +applies it, the HTML backend ignores it). Every other table and all log +output still go through the shared Rich console, whose width is floored +at `ConsoleManager._MIN_CONSOLE_WIDTH = 130` ("to avoid cramped +layouts"). On a standard ~80-column terminal that floor makes wide +tables overflow and soft-wrap badly. + +**Fix:** decide on a global policy — either have `_detect_width` trust +the detected terminal width (keeping 130 only as a fallback when +detection fails), or pass the terminal width into more table call sites +the way `list_tutorials` now does. A global change affects every table +(fit results, parameters, ...) and all logs, so weigh it against the +deliberate minimum-width choice. + +**Depends on:** related to issue 62. diff --git a/docs/dev/issues/open/low_make-refinement-status-default-an-enum.md b/docs/dev/issues/open/low_make-refinement-status-default-an-enum.md new file mode 100644 index 000000000..2f68c9994 --- /dev/null +++ b/docs/dev/issues/open/low_make-refinement-status-default-an-enum.md @@ -0,0 +1,23 @@ +# 30. Make `refinement_status` Default an Enum + +**Priority:** `[priority] low` + +**Type:** Design + +`bragg_pd.py` uses `default='incl'` as a raw string with a TODO to make +it an Enum. + +**Update:** the rename half is done — the property is now `calc_status` +(in both `bragg_pd.py` and `total_pd.py`), but the value set +(`'incl'`/`'excl'`) is still raw strings enforced by a +`MembershipValidator` and compared against string literals, contrary to +the `(str, Enum)` convention. The enum should be shared by both data +families and the excluded-regions mask logic. See also issue 151 (a dead +`else` branch in the same `_set_calc_status` setter). + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L156) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L116) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_make-save-respect-verbosity-settings.md b/docs/dev/issues/open/low_make-save-respect-verbosity-settings.md new file mode 100644 index 000000000..bf1016b56 --- /dev/null +++ b/docs/dev/issues/open/low_make-save-respect-verbosity-settings.md @@ -0,0 +1,11 @@ +# 92. Make `save()` Respect Verbosity Settings + +**Priority:** `[priority] low` + +**Type:** UX + +`Project.save()` unconditionally prints progress via `console.print()`. +It should respect the logger's verbosity mode so that silent/quiet +operation is possible (e.g. in automated pipelines or tests). + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_merge-parameter-record-construction-in-analysis.md b/docs/dev/issues/open/low_merge-parameter-record-construction-in-analysis.md new file mode 100644 index 000000000..1175d45f5 --- /dev/null +++ b/docs/dev/issues/open/low_merge-parameter-record-construction-in-analysis.md @@ -0,0 +1,16 @@ +# 44. Merge Parameter Record Construction in Analysis + +**Priority:** `[priority] low` + +**Type:** Cleanup + +`Analysis._params_to_dataframe` has TODOs to merge record construction +for `StringDescriptor`/`NumericDescriptor`/`Parameter` and to use `repr` +formatting for `StringDescriptor` values. + +**TODOs:** + +- [analysis.py](src/easydiffraction/analysis/analysis.py#L461) +- [analysis.py](src/easydiffraction/analysis/analysis.py#L462) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_mkdocs-doesn-t-unpack-types-in-validation-module.md b/docs/dev/issues/open/low_mkdocs-doesn-t-unpack-types-in-validation-module.md new file mode 100644 index 000000000..a7643adb6 --- /dev/null +++ b/docs/dev/issues/open/low_mkdocs-doesn-t-unpack-types-in-validation-module.md @@ -0,0 +1,14 @@ +# 42. MkDocs Doesn't Unpack Types in Validation Module + +**Priority:** `[priority] low` + +**Type:** Docs + +A TODO in `validation.py` notes that MkDocs doesn't unpack types +properly. + +**TODOs:** + +- [validation.py](src/easydiffraction/core/validation.py#L25) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_move-cif-v2-v1-conversion-out-of-calculator.md b/docs/dev/issues/open/low_move-cif-v2-v1-conversion-out-of-calculator.md new file mode 100644 index 000000000..03bbe152a --- /dev/null +++ b/docs/dev/issues/open/low_move-cif-v2-v1-conversion-out-of-calculator.md @@ -0,0 +1,15 @@ +# 18. Move CIF v2→v1 Conversion Out of Calculator + +**Priority:** `[priority] low` + +**Type:** Maintainability + +`PdffitCalculator.calculate_pattern` contains inline CIF v2→v1 +conversion (dot-to-underscore rewriting). This should live in a shared +`io` module. + +**TODOs:** + +- [pdffit.py](src/easydiffraction/analysis/calculators/pdffit.py#L118) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_move-show-to-categorycollection-base-class.md b/docs/dev/issues/open/low_move-show-to-categorycollection-base-class.md new file mode 100644 index 000000000..78a9cdcea --- /dev/null +++ b/docs/dev/issues/open/low_move-show-to-categorycollection-base-class.md @@ -0,0 +1,15 @@ +# 53. Move `show()` to `CategoryCollection` Base Class + +**Priority:** `[priority] low` + +**Type:** Maintainability + +`ExcludedRegions.show()` and `BackgroundBase.show()` duplicate table- +rendering logic. The TODO suggests moving it to the base class. + +**TODOs:** + +- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L166) +- [base.py](src/easydiffraction/datablocks/experiment/categories/background/base.py#L19) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_narrow-defensive-getattr-chain-in-aniso-adp-unit-resolution.md b/docs/dev/issues/open/low_narrow-defensive-getattr-chain-in-aniso-adp-unit-resolution.md new file mode 100644 index 000000000..0670e153f --- /dev/null +++ b/docs/dev/issues/open/low_narrow-defensive-getattr-chain-in-aniso-adp-unit-resolution.md @@ -0,0 +1,23 @@ +# 159. Narrow Defensive getattr-Chain in Aniso ADP Unit Resolution + +**Priority:** `[priority] low` + +**Type:** Maintainability + +`_owning_adp_type` walks +`param → aniso item → collection → structure → atom_sites → atom.adp_type` +entirely through `getattr(..., None)` plus a `try/except`, which is the +kind of defensive padding for internal states `AGENTS.md` discourages. +The docstring justifies it as a display path that can resolve before +wiring completes, so it is borderline-acceptable, but the broad +tolerance could mask a genuine wiring bug (silently returning declared +units). + +**Fix:** assert the chain in non-display contexts, or narrow the +tolerated cases. + +**TODOs / locations:** + +- [default.py](src/easydiffraction/datablocks/structure/categories/atom_site_aniso/default.py#L62) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_redesign-tutorial-grouping-and-categorisation.md b/docs/dev/issues/open/low_redesign-tutorial-grouping-and-categorisation.md new file mode 100644 index 000000000..06d6c3459 --- /dev/null +++ b/docs/dev/issues/open/low_redesign-tutorial-grouping-and-categorisation.md @@ -0,0 +1,14 @@ +# 87. Redesign Tutorial Grouping and Categorisation + +**Priority:** `[priority] low` + +**Type:** Documentation / UX + +The current tutorial index uses a flat `"level": "advanced"` tag. A +richer categorisation system is needed: + +- Group by topic (matching the docs structure). +- Multiple difficulty levels. +- Multiple Python-knowledge levels. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_redirect-or-suppress-cryspy-stderr-warnings.md b/docs/dev/issues/open/low_redirect-or-suppress-cryspy-stderr-warnings.md new file mode 100644 index 000000000..5c984e34c --- /dev/null +++ b/docs/dev/issues/open/low_redirect-or-suppress-cryspy-stderr-warnings.md @@ -0,0 +1,15 @@ +# 20. Redirect or Suppress CrysPy stderr Warnings + +**Priority:** `[priority] low` + +**Type:** UX + +CrysPy emits warnings to stderr during pattern calculation. The code has +TODO markers to redirect these. + +**TODOs:** + +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L112) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L184) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_remove-dead-auto-populate-branch-in-run-joint.md b/docs/dev/issues/open/low_remove-dead-auto-populate-branch-in-run-joint.md new file mode 100644 index 000000000..17e107bc5 --- /dev/null +++ b/docs/dev/issues/open/low_remove-dead-auto-populate-branch-in-run-joint.md @@ -0,0 +1,21 @@ +# 154. Remove Dead Auto-Populate Branch in `_run_joint` + +**Priority:** `[priority] low` + +**Type:** Dead code + +`_run_fit_mode` always calls `_prepare_joint_fit()` (which populates all +`joint_fit` rows with `weight=1.0`) immediately before `_run_joint()`, +so by the time `_run_joint` runs `len(self._joint_fit)` is never 0 and +its `if not len(self._joint_fit): ... create(weight=0.5)` block is +unreachable. It also disagrees with the live default weight (0.5 vs +1.0). + +**Fix:** remove the dead block (or, if it must stay as a guard, align +the default weight to 1.0). + +**TODOs / locations:** + +- [analysis.py](src/easydiffraction/analysis/analysis.py#L2761) + +**Depends on:** related to issues 3, 15. diff --git a/docs/dev/issues/open/low_remove-redundant-parameter-listing-from-parameter-itself.md b/docs/dev/issues/open/low_remove-redundant-parameter-listing-from-parameter-itself.md new file mode 100644 index 000000000..796135c17 --- /dev/null +++ b/docs/dev/issues/open/low_remove-redundant-parameter-listing-from-parameter-itself.md @@ -0,0 +1,11 @@ +# 83. Remove Redundant Parameter Listing from Parameter Itself + +**Priority:** `[priority] low` + +**Type:** Cleanup + +Parameters currently carry some form of self-listing metadata that is +redundant with the category/collection level. Remove it to keep the +single-responsibility principle. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_remove-stale-commented-out-dead-code-in-core-and-io.md b/docs/dev/issues/open/low_remove-stale-commented-out-dead-code-in-core-and-io.md new file mode 100644 index 000000000..b8106435f --- /dev/null +++ b/docs/dev/issues/open/low_remove-stale-commented-out-dead-code-in-core-and-io.md @@ -0,0 +1,18 @@ +# 158. Remove Stale Commented-Out / Dead Code in `core/` and `io/` + +**Priority:** `[priority] low` + +**Type:** Dead code + +The CIF parse module keeps a commented-out `experiment_type_from_block` +helper, and `core/variable.py` carries a dead `_value_spec.validated` +block plus a "Check if it is actually in use?" TODO. These are stale and +removable. + +**TODOs / locations:** + +- [parse.py](src/easydiffraction/io/cif/parse.py#L65) +- [variable.py](src/easydiffraction/core/variable.py#L99) +- [variable.py](src/easydiffraction/core/variable.py#L167) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_rename-beammodeenum-members-to-cwl-tof.md b/docs/dev/issues/open/low_rename-beammodeenum-members-to-cwl-tof.md new file mode 100644 index 000000000..1902104cb --- /dev/null +++ b/docs/dev/issues/open/low_rename-beammodeenum-members-to-cwl-tof.md @@ -0,0 +1,14 @@ +# 35. Rename `BeamModeEnum` Members to CWL/TOF + +**Priority:** `[priority] low` + +**Type:** Naming + +`BeamModeEnum.CONSTANT_WAVELENGTH` and `TIME_OF_FLIGHT` have a TODO to +be renamed to `CWL` and `TOF`. + +**TODOs:** + +- [enums.py](src/easydiffraction/datablocks/experiment/item/enums.py#L113) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_rename-experiment-type-property.md b/docs/dev/issues/open/low_rename-experiment-type-property.md new file mode 100644 index 000000000..6a2a7e1c8 --- /dev/null +++ b/docs/dev/issues/open/low_rename-experiment-type-property.md @@ -0,0 +1,14 @@ +# 37. Rename Experiment `.type` Property + +**Priority:** `[priority] low` + +**Type:** Naming + +`ExperimentBase.type` returns experimental metadata but the name shadows +the built-in `type`. A TODO suggests finding a better name. + +**TODOs:** + +- [base.py](src/easydiffraction/datablocks/experiment/item/base.py#L75) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_rename-line-segment-background-y-to-intensity.md b/docs/dev/issues/open/low_rename-line-segment-background-y-to-intensity.md new file mode 100644 index 000000000..629e27f5a --- /dev/null +++ b/docs/dev/issues/open/low_rename-line-segment-background-y-to-intensity.md @@ -0,0 +1,14 @@ +# 52. Rename Line-Segment Background `y` to `intensity` + +**Priority:** `[priority] low` + +**Type:** Naming + +`LineSegmentBackgroundPoint.y` has TODOs to rename to `intensity`. + +**TODOs:** + +- [line_segment.py](src/easydiffraction/datablocks/experiment/categories/background/line_segment.py#L67) +- [line_segment.py](src/easydiffraction/datablocks/experiment/categories/background/line_segment.py#L72) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_rename-pd-data-point-mixins.md b/docs/dev/issues/open/low_rename-pd-data-point-mixins.md new file mode 100644 index 000000000..d46850e37 --- /dev/null +++ b/docs/dev/issues/open/low_rename-pd-data-point-mixins.md @@ -0,0 +1,17 @@ +# 31. Rename PD Data Point Mixins + +**Priority:** `[priority] low` + +**Type:** Naming + +Mixin classes `PdDataPointBaseMixin` and `PdCwlDataPointMixin` have TODO +markers suggesting a rename to `BasePdDataPointMixin` and +`CwlPdDataPointMixin` for consistency. + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L268) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L269) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L271) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_render-styled-multi-line-table-cells-in-the-html-backend.md b/docs/dev/issues/open/low_render-styled-multi-line-table-cells-in-the-html-backend.md new file mode 100644 index 000000000..e727d696c --- /dev/null +++ b/docs/dev/issues/open/low_render-styled-multi-line-table-cells-in-the-html-backend.md @@ -0,0 +1,21 @@ +# 110. Render Styled Multi-Line Table Cells in the HTML Backend + +**Priority:** `[priority] low` + +**Type:** Display / Notebook parity + +`list_tutorials` shows a two-line cell in the terminal — a colored title +on the first line and a dimmed description on the second — using Rich +markup and an embedded newline. The Jupyter table backend +(`PandasTableBackend`) cannot render this: `_strip_rich_markup` only +matches a single full-cell `[color]text[/color]`, and HTML collapses the +newline, so the markup would show as literal text. `list_tutorials` is +therefore gated via `in_jupyter()` to show only the plain title in +notebooks, which drops the description and the color there. + +**Fix:** teach the HTML backend to render the same styling — translate +embedded newlines to `<br>`, map `[dim]` to reduced opacity, and accept +multiple/mixed markup tags per cell — then remove the terminal-only gate +in `list_tutorials` so notebooks also get the styled two-line entry. + +**Depends on:** related to issue 62. diff --git a/docs/dev/issues/open/low_replace-no-op-assert-true-in-test-logging-py.md b/docs/dev/issues/open/low_replace-no-op-assert-true-in-test-logging-py.md new file mode 100644 index 000000000..5cc5b4f92 --- /dev/null +++ b/docs/dev/issues/open/low_replace-no-op-assert-true-in-test-logging-py.md @@ -0,0 +1,20 @@ +# 160. Replace No-Op `assert True` in `test_logging.py` + +**Priority:** `[priority] low` + +**Type:** Test coverage + +`test_logger_configure_and_warn_reaction` exercises logger +configure/level/mode calls but ends with `assert True` ("absence of +exception is success"), so it asserts nothing about behaviour — a +regression that changed log routing or level handling would not be +caught. + +**Fix:** assert on captured log records/levels (e.g. via `caplog`) +instead of `assert True`. + +**TODOs / locations:** + +- [test_logging.py](tests/unit/easydiffraction/utils/test_logging.py#L13) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_resolve-any-vs-object-type-annotation-policy.md b/docs/dev/issues/open/low_resolve-any-vs-object-type-annotation-policy.md new file mode 100644 index 000000000..27309ab03 --- /dev/null +++ b/docs/dev/issues/open/low_resolve-any-vs-object-type-annotation-policy.md @@ -0,0 +1,15 @@ +# 80. Resolve `Any` vs `object` Type Annotation Policy + +**Priority:** `[priority] low` + +**Type:** Code style + +Both `Any` and `object` are used as generic parameter types. Current +pattern: `Any` inside containers (`dict[str, Any]`), `object` for +standalone params (often to avoid circular imports). Decide on a policy: + +- Use protocol types / `TYPE_CHECKING` imports instead of `object`. +- Reserve `Any` for genuinely unknown types. +- Document when each is appropriate. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_revisit-powder-refln-phase-labels-and-row-ids.md b/docs/dev/issues/open/low_revisit-powder-refln-phase-labels-and-row-ids.md new file mode 100644 index 000000000..9d4a6c318 --- /dev/null +++ b/docs/dev/issues/open/low_revisit-powder-refln-phase-labels-and-row-ids.md @@ -0,0 +1,29 @@ +# 94. Revisit Powder `refln` Phase Labels and Row IDs + +**Priority:** `[priority] low` + +**Type:** Naming / CIF UX + +The implemented powder reflection category uses `phase_id` throughout +(`experiment.refln`, `PowderReflnRecord`, Bragg tick labels) and assigns +global sequential row ids. This matches the current implementation, but +the archived planning notes left two follow-up questions open: + +1. whether `structure_id` would be clearer than `phase_id` in the public + API / CIF output, and +2. whether phase-prefixed row ids would make CIF inspection and + debugging easier than simple `1`, `2`, `3`, ... + +**TODOs** (paths updated — reflection categories moved from +`categories/data/refln_pd.py` to `categories/refln/`): + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/refln/bragg_pd.py) +- [base.py](src/easydiffraction/display/plotters/base.py#L24) + +**Note:** the Edi persistence ADR proposes renaming powder +`refln.phase_id` → `structure_id` +([`edstar-project-persistence.md`](../../adrs/accepted/edstar-project-persistence.md)), +which resolves follow-up question 1; keep this issue scoped to the +row-id question (2) once that ADR lands. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_shorter-public-api-names-via-init-py-re-exports.md b/docs/dev/issues/open/low_shorter-public-api-names-via-init-py-re-exports.md new file mode 100644 index 000000000..3b90debca --- /dev/null +++ b/docs/dev/issues/open/low_shorter-public-api-names-via-init-py-re-exports.md @@ -0,0 +1,12 @@ +# 69. Shorter Public API Names via `__init__.py` Re-Exports + +**Priority:** `[priority] low` + +**Type:** API ergonomics + +Classes are imported in `__init__.py` files but users still need deep +paths to reach them. Consider whether top-level re-exports (e.g. +`from easydiffraction import Project, Structure, Experiment`) should +provide shorter access, and document the policy. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_show-experiment-number-total-during-sequential-fitting.md b/docs/dev/issues/open/low_show-experiment-number-total-during-sequential-fitting.md new file mode 100644 index 000000000..b3cbb5d51 --- /dev/null +++ b/docs/dev/issues/open/low_show-experiment-number-total-during-sequential-fitting.md @@ -0,0 +1,12 @@ +# 90. Show Experiment Number/Total During Sequential Fitting + +**Priority:** `[priority] low` + +**Type:** UX + +Currently prints: `Using experiment 🔬 'd20_30' for 'single' fitting` + +Should print: +`Using experiment 🔬 'd20_30' (No. 30 of 47) for 'single' fitting` + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_size-chapter-divider-from-the-live-console-width.md b/docs/dev/issues/open/low_size-chapter-divider-from-the-live-console-width.md new file mode 100644 index 000000000..ea9e335ce --- /dev/null +++ b/docs/dev/issues/open/low_size-chapter-divider-from-the-live-console-width.md @@ -0,0 +1,21 @@ +# 156. Size `chapter()` Divider From the Live Console Width + +**Priority:** `[priority] low` + +**Type:** Robustness / Display + +`ConsoleManager.get()` constructs the shared `Console` once with the +width detected at first use and caches it; `chapter()` calls +`_detect_width()` fresh on each invocation to size its divider line. If +the terminal is resized between first console creation and a `chapter()` +call, the computed padding no longer matches the console's fixed render +width, producing a mis-aligned or wrapped header rule. + +**Fix:** size `chapter()` from the live console's `width` +(`cls._console.width`) instead of re-detecting. + +**TODOs / locations:** + +- [logging.py](src/easydiffraction/utils/logging.py#L784) + +**Depends on:** related to issue 109 (table/terminal width policy). diff --git a/docs/dev/issues/open/low_smarter-automatic-bond-detection-near-neighbour-analysis.md b/docs/dev/issues/open/low_smarter-automatic-bond-detection-near-neighbour-analysis.md new file mode 100644 index 000000000..e8a4429a6 --- /dev/null +++ b/docs/dev/issues/open/low_smarter-automatic-bond-detection-near-neighbour-analysis.md @@ -0,0 +1,29 @@ +# 108. Smarter Automatic Bond Detection (Near-Neighbour Analysis) + +**Priority:** `[priority] low` + +**Type:** UX / Visualization + +crysview generates bonds with the cif_core distance rule +(`min_bond_distance_cutoff ≤ d ≤ r_bond(A) + r_bond(B) + bond_distance_incr`), +then prunes to the first coordination shell — a contact survives only if +it is within `1.3×` the nearer atom's nearest-neighbour distance +(`COORDINATION_SHELL_FACTOR` in `display/structure/builder.py`). This +stop-gap handles the common cases (e.g. LBCO renders just the Co–O +octahedron) without a new dependency, but the fixed factor is still a +heuristic: it can over-prune strongly distorted shells (e.g. elongated +Jahn–Teller octahedra) or under-prune others, and it is not yet +user-configurable. + +**Fix:** consider a robust, configurable near-neighbour algorithm for +automatic "reasonable" bonding — e.g. a Voronoi / solid-angle method +such as pymatgen's `CrystalNN` or `VoronoiNN`, which weights neighbours +by solid angle instead of a single relative cutoff. The Voronoi route is +the most robust across arbitrary structures but introduces a heavyweight +dependency (pymatgen), so it needs a dependency decision; an +ASE/Jmol-style multiplicative covalent tolerance is lighter but, like +the current factor, cannot separate shells when ionic-cation covalent +radii are large. + +**Depends on:** dependency decision for pymatgen (if the Voronoi route +is chosen). diff --git a/docs/dev/issues/open/low_suppress-redundant-dirty-flag-sets-in-symmetry-constraints.md b/docs/dev/issues/open/low_suppress-redundant-dirty-flag-sets-in-symmetry-constraints.md new file mode 100644 index 000000000..7df6993ea --- /dev/null +++ b/docs/dev/issues/open/low_suppress-redundant-dirty-flag-sets-in-symmetry-constraints.md @@ -0,0 +1,20 @@ +# 13. Suppress Redundant Dirty-Flag Sets in Symmetry Constraints + +**Priority:** `[priority] low` + +**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. diff --git a/docs/dev/issues/open/low_suppress-the-redundant-row-index-column-in-tables.md b/docs/dev/issues/open/low_suppress-the-redundant-row-index-column-in-tables.md new file mode 100644 index 000000000..bb06f70f0 --- /dev/null +++ b/docs/dev/issues/open/low_suppress-the-redundant-row-index-column-in-tables.md @@ -0,0 +1,17 @@ +# 112. Suppress the Redundant Row-Index Column in Tables + +**Priority:** `[priority] low` + +**Type:** Display / UX + +`TableRenderer._prepare_dataframe` bumps the DataFrame index to 1-based, +and both the Rich and pandas backends always render it as the first +column. For tables that already carry an explicit identifier — e.g. +`list_tutorials`, whose `id` column duplicates that 1-based counter — +the leading index column is redundant and reads as a duplicate. + +**Fix:** add an opt-out (e.g. a `show_index` flag on the render path) so +callers with their own id column can hide the auto-generated index, or +only render the index column when no explicit id column is present. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_tighten-fitparameteritem-posterior-summary-nan-behaviour.md b/docs/dev/issues/open/low_tighten-fitparameteritem-posterior-summary-nan-behaviour.md new file mode 100644 index 000000000..34741ad64 --- /dev/null +++ b/docs/dev/issues/open/low_tighten-fitparameteritem-posterior-summary-nan-behaviour.md @@ -0,0 +1,25 @@ +# 104. Tighten `FitParameterItem.posterior_summary` NaN Behaviour + +**Priority:** `[priority] low` + +**Type:** Robustness / partial-data edge case **Source:** Review 8 +finding F9. + +`FitParameterItem.has_posterior_summary` returns `True` if any posterior +field is set, and `posterior_summary` then builds a +`PosteriorParameterSummary` whose missing floats become `NaN`. A +hand-edited or partially-written CIF row with only +`posterior_gelman_rubin = 1.02` and the rest unset produces a summary +whose `median`, `standard_deviation`, and both interval bounds are +`NaN`. Downstream plotting and the `display.fit_results` table render +NaN intervals — harder to debug than a clean "no posterior" outcome. + +The deterministic-fit case is fine: deterministic fits set all required +fields to `None`, so `has_posterior_summary()` returns `False`. + +**Fix:** tighten `has_posterior_summary` to require the core stats (at +least `posterior_median` and one interval bound) before emitting a +summary, or split the dataclass into required-statistics and +optional-diagnostics components. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_unify-mkdir-usage-across-the-codebase.md b/docs/dev/issues/open/low_unify-mkdir-usage-across-the-codebase.md new file mode 100644 index 000000000..538c60433 --- /dev/null +++ b/docs/dev/issues/open/low_unify-mkdir-usage-across-the-codebase.md @@ -0,0 +1,13 @@ +# 60. Unify `mkdir` Usage Across the Codebase + +**Priority:** `[priority] low` + +**Type:** Cleanup + +`io/ascii.py` has a TODO to unify directory creation with other uses. + +**TODOs:** + +- [ascii.py](src/easydiffraction/io/ascii.py#L118) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_unify-project-level-update-orchestration.md b/docs/dev/issues/open/low_unify-project-level-update-orchestration.md new file mode 100644 index 000000000..111fa478c --- /dev/null +++ b/docs/dev/issues/open/low_unify-project-level-update-orchestration.md @@ -0,0 +1,17 @@ +# 10. Unify Project-Level Update Orchestration + +**Priority:** `[priority] low` + +**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 the CategoryOwner migration. diff --git a/docs/dev/issues/open/low_unify-setter-parameter-naming-convention.md b/docs/dev/issues/open/low_unify-setter-parameter-naming-convention.md new file mode 100644 index 000000000..33f53b479 --- /dev/null +++ b/docs/dev/issues/open/low_unify-setter-parameter-naming-convention.md @@ -0,0 +1,19 @@ +# 73. Unify Setter Parameter Naming Convention + +**Priority:** `[priority] low` + +**Type:** Code style + +Some setters use `new`, others use `value`, others use the attribute +name. For example: + +```python +@id.setter +def id(self, new): + self._id.value = new +``` + +Agree on a single convention (e.g. always `value`) and apply +consistently. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_use-pdf-specific-cif-names-for-total-scattering.md b/docs/dev/issues/open/low_use-pdf-specific-cif-names-for-total-scattering.md new file mode 100644 index 000000000..60dc3df73 --- /dev/null +++ b/docs/dev/issues/open/low_use-pdf-specific-cif-names-for-total-scattering.md @@ -0,0 +1,27 @@ +# 17. Use PDF-Specific CIF Names for Total Scattering + +**Priority:** `[priority] low` + +**Type:** Naming + +The `TotalPdDataPoint` class reuses Bragg powder CIF tag names (e.g. +`_pd_data.point_id`, `_pd_proc.r`, `_pd_meas.intensity_total`) as +placeholders. These should be replaced with proper total-scattering / +PDF-specific CIF names. + +**TODOs:** + +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L48) + — `_pd_data.point_id` +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L62) + — `_pd_proc.r` +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L74) + — `_pd_meas.intensity_total` +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L87) + — `_pd_meas.intensity_total_su` +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L99) + — `_pd_calc.intensity_total` +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L112) + — `_pd_data.refinement_status` + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/low_value-setter-re-validates-on-every-nan-assignment.md b/docs/dev/issues/open/low_value-setter-re-validates-on-every-nan-assignment.md new file mode 100644 index 000000000..56ac4f259 --- /dev/null +++ b/docs/dev/issues/open/low_value-setter-re-validates-on-every-nan-assignment.md @@ -0,0 +1,22 @@ +# 153. `value` Setter Re-Validates on Every NaN Assignment + +**Priority:** `[priority] low` + +**Type:** Performance + +The early-out `if self._value == v: return` never fires when the current +value is `NaN` (`nan == nan` is False), so re-assigning the same `NaN` +to a sentinel field (e.g. NaN data-range bounds) always re-runs +validation and marks the owner dirty. Harmless for correctness, but it +defeats the no-op optimization exactly for the sentinel fields the +codebase relies on. + +**Fix:** use a `NaN`-aware equality, or document that NaN fields always +re-validate. + +**TODOs / locations:** + +- [variable.py](src/easydiffraction/core/variable.py#L152) + +**Depends on:** related to issue 13 (suppress redundant dirty-flag +sets). diff --git a/docs/dev/issues/open/low_verify-completeness-of-analysis-cif-serialisation.md b/docs/dev/issues/open/low_verify-completeness-of-analysis-cif-serialisation.md new file mode 100644 index 000000000..e5d19de3f --- /dev/null +++ b/docs/dev/issues/open/low_verify-completeness-of-analysis-cif-serialisation.md @@ -0,0 +1,12 @@ +# 79. Verify Completeness of Analysis CIF Serialisation + +**Priority:** `[priority] low` + +**Type:** Correctness + +`analysis_to_cif()` and `analysis_from_cif()` exist, but audit whether +**all** analysis state is persisted: aliases, constraints, fit mode, +joint-fit weights, minimiser type, calculator assignments. Any missing +fields means a loaded project silently differs from the saved one. + +**Depends on:** related to issue 121. diff --git a/docs/dev/issues/open/lowest_disable-todo-comment-checks-in-codefactor-prs.md b/docs/dev/issues/open/lowest_disable-todo-comment-checks-in-codefactor-prs.md new file mode 100644 index 000000000..62f3ec8d6 --- /dev/null +++ b/docs/dev/issues/open/lowest_disable-todo-comment-checks-in-codefactor-prs.md @@ -0,0 +1,15 @@ +# 91. Disable TODO Comment Checks in CodeFactor PRs + +**Priority:** `[priority] lowest` + +**Type:** CI / Tooling + +CodeFactor flags TODO comments as unresolved issues (rule C100) in PRs. +Since TODOs are tracked in `issues/open.md`, the CodeFactor check adds +noise. Disable the C100 rule or configure CodeFactor to ignore TODO +comments. + +**Depends on:** nothing. + +**Recommended-priority note:** Marked **lowest**: CI-tooling noise +reduction (CodeFactor TODO rule), not a product defect. diff --git a/docs/dev/issues/open/lowest_fix-dataset-26-description-47-files-not-57.md b/docs/dev/issues/open/lowest_fix-dataset-26-description-47-files-not-57.md new file mode 100644 index 000000000..ad18c6626 --- /dev/null +++ b/docs/dev/issues/open/lowest_fix-dataset-26-description-47-files-not-57.md @@ -0,0 +1,14 @@ +# 88. Fix Dataset 26 Description (47 Files, Not 57) + +**Priority:** `[priority] lowest` + +**Type:** Data + +Dataset 26 description says "57 files" but should say "47 files": +`"Co2SiO4, D20 (ILL), 57 files, T from ~50K to ~500K"` → +`"Co2SiO4, D20 (ILL), 47 files, T from ~50K to ~500K"`. + +**Depends on:** nothing. + +**Recommended-priority note:** Marked **lowest**: a data-description +typo (57 vs 47 files) with no functional impact. diff --git a/docs/dev/issues/open/lowest_live-notebook-plotly-delivery-loader-vs-native-mimetype.md b/docs/dev/issues/open/lowest_live-notebook-plotly-delivery-loader-vs-native-mimetype.md new file mode 100644 index 000000000..7841edf48 --- /dev/null +++ b/docs/dev/issues/open/lowest_live-notebook-plotly-delivery-loader-vs-native-mimetype.md @@ -0,0 +1,44 @@ +# 117. Live-Notebook Plotly Delivery: Loader vs Native Mimetype + +**Priority:** `[priority] lowest` + +**Type:** Display / Architecture + +Records the two viable strategies for rendering interactive Plotly +figures in live notebooks, so the trade-off is not re-litigated. See +[`plotting-docs-performance.md`](../adrs/accepted/plotting-docs-performance.md). + +**Background.** Live notebooks historically rendered via +`display(HTML(pio.to_html(..., include_plotlyjs='cdn')))`, which caused +an empty first plot after kernel restart (the CDN `<script src>` loaded +asynchronously while `Plotly.newPlot` ran immediately) and a loading +gap. Two ways to fix it: + +- **Option 1 — Native mimetype renderer (`fig.show()`).** Emit the + `application/vnd.plotly.v1+json` mime bundle and let the JupyterLab + Plotly extension render it. Pros: simplest, lowest maintenance, + officially supported, no CDN, no script-in-output artifacts. Cons: + live notebooks lose the three custom post-script behaviours (dynamic + theme-sync on JupyterLab light/dark toggle, hidden-tab resize, the + modebar legend-toggle button); a saved `.ipynb`'s plot output is the + spec JSON, not self-contained HTML. + +- **Option 2 — Self-hosted loader (current).** Ship the vendored Plotly + bundle + the shared `ed-figures.js` loader in the wheel; the first + figure injects them once per kernel session and each figure renders + via `window.edFigures.renderSpec(id, spec)` delivered as a + `display(Javascript(...))` output, so the HTML output is just the plot + div (no `<script>` tags some hosts render as empty rows). Pros: keeps + all three custom behaviours; CDN-free/archival; unified with the docs + delivery. Cons: more moving parts; depends on the notebook being + **trusted** for a reopened (not re-run) notebook to re-render. + +**Current choice:** Option 2 (keeps the live-notebook extras). Revisit +Option 1 if the loader proves fragile across notebook frontends or the +maintenance cost outweighs the three behaviours. + +**Depends on:** nothing. + +**Recommended-priority note:** Marked **lowest**: records a settled +delivery decision (Option 2); no action required unless the loader +proves fragile. diff --git a/docs/dev/issues/open/lowest_plotly-figures-show-empty-rows-in-the-visa-jupyterlab.md b/docs/dev/issues/open/lowest_plotly-figures-show-empty-rows-in-the-visa-jupyterlab.md new file mode 100644 index 000000000..3f25ba2cb --- /dev/null +++ b/docs/dev/issues/open/lowest_plotly-figures-show-empty-rows-in-the-visa-jupyterlab.md @@ -0,0 +1,65 @@ +# 118. Plotly Figures Show Empty Rows in the VISA JupyterLab + +**Priority:** `[priority] lowest` + +**Type:** Display / Environment + +**Symptom.** In the **VISA-hosted, iframe-embedded** JupyterLab +(`visa.ess.eu/.../jupyter/.../lab`), every interactive Plotly figure is +preceded by several empty rows that fill in over ~1–2 s, and the plot +often renders collapsed. On a **standard local JupyterLab the branch is +fine** — the only issue there is the CDN race (issue addressed by the +self-hosted runtime; see below). So this is **specific to the VISA +environment**, not the library. + +**Root cause (from a DOM inspection in VISA).** The plot element renders +at **height 0**, nested under +`div.jp-WindowedPanel-viewport.jp-content-visibility-mode`. VISA's +JupyterLab runs notebooks in **windowed mode** (`content-visibility` +virtualization). The output container is **0×0 at the moment Plotly +draws**, and because the figure config is `responsive: true`, Plotly +sizes to that 0×0 container and renders a zero-size plot that does not +recover. The "empty rows" are that collapsed zero-height output as the +viewport re-measures. This is a known Plotly ✕ JupyterLab-windowing +interaction. + +**Workaround (user side).** JupyterLab → Settings → Notebook → +**Windowing mode = `defer`/`none`** disables the virtualization. (The +user reported this alone did **not** resolve it in VISA, so VISA may +force or wrap the setting — needs confirmation.) + +**Attempts that did NOT resolve it in VISA** (all reverted to keep the +code minimal; recorded so they are not retried blindly): + +- Self-hosted runtime instead of CDN, delivered as a single HTML output, + as a `display(Javascript(...))` output, and as multiple/one inline + `<script>` tags. (The self-hosting itself **is** kept — it fixes the + real CDN race — but none of the delivery variants changed the VISA + empty rows.) +- Removing the loading skeleton; removing then restoring the + `min-height` reservation. +- Trimming the figure top margin + `title.automargin` (chasing a misread + "default margin" theory). +- Preloading the runtime on `import easydiffraction` (regressed: added a + visible empty output line on the import cell). +- Skipping the resize `ResizeObserver` for live figures. +- Reserving an explicit container height for windowing. +- Deferring the render until the container reports a non-zero size + (`requestAnimationFrame` poll). This **did** get the plot to render at + full height in VISA (DOM showed `h=565` instead of `0`), but the user + still reported empty rows visually — so it is necessary-but-not- + sufficient there. + +**Promising future directions.** Set `responsive: false` with an +explicit width/height for live figures so Plotly never depends on the +0×0 container; or render the figure off-screen and swap it in once +sized; or detect the VISA/windowed environment and special-case it. +Needs to be developed and tested **inside VISA**, since it does not +reproduce on a standard JupyterLab. + +**Depends on:** nothing. Lower priority — affects only the VISA +deployment, and a user-side windowing-mode change may suffice. + +**Recommended-priority note:** Marked **lowest**: affects only the +VISA-hosted JupyterLab, and a user-side windowing-mode change may +suffice. diff --git a/docs/dev/issues/open/medium_add-sycos-sysin-systematic-peak-position-corrections.md b/docs/dev/issues/open/medium_add-sycos-sysin-systematic-peak-position-corrections.md new file mode 100644 index 000000000..63715b615 --- /dev/null +++ b/docs/dev/issues/open/medium_add-sycos-sysin-systematic-peak-position-corrections.md @@ -0,0 +1,26 @@ +# 131. Add SyCos/SySin Systematic Peak-Position Corrections + +**Priority:** `[priority] medium` + +**Type:** Feature / Experiment model + +FullProf models systematic peak-position aberrations with `SyCos` +(sample displacement) and `SySin` (transparency), shifting peaks as a +function of angle on top of the `Zero` offset. EasyDiffraction has no +category for these, so it cannot reproduce datasets that use them. The +cryspy side is implemented in +[cryspy PR #46](https://github.com/ikibalin/cryspy/pull/46) (see +[issue #38](https://github.com/ikibalin/cryspy/issues/38)); the +EasyDiffraction side — an instrument-category parameter pair plus the +calculator wiring — is still to do. + +A prepared verification page, +`docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.py`, uses the issue #38 +LaB6 dataset and is skipped via `ci_skip.txt`. Finishing it also needs a +custom ¹¹B scattering length, the Thompson–Cox–Hastings profile, and a +FullProf-style polynomial background, which that dataset relies on. + +**Fix:** add `SyCos`/`SySin` to the CWL instrument category, pass them +to the calculators, then un-skip the LaB6 page. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_atom-site-phase-id-label-regex-rejects-valid-cif-labels.md b/docs/dev/issues/open/medium_atom-site-phase-id-label-regex-rejects-valid-cif-labels.md new file mode 100644 index 000000000..5f6893932 --- /dev/null +++ b/docs/dev/issues/open/medium_atom-site-phase-id-label-regex-rejects-valid-cif-labels.md @@ -0,0 +1,26 @@ +# 148. Atom-Site / Phase-ID Label Regex Rejects Valid CIF Labels + +**Priority:** `[priority] medium` + +**Type:** Robustness + +`_atom_site.label` and `_pd_phase_block.id` use +`RegexValidator(r'^[A-Za-z_][A-Za-z0-9_]*$')`, which rejects valid CIF +identifiers that begin with a digit or contain `+`/`-`/`'` (e.g. ion +labels like `O1-`, `Tb3+`, or block names starting with a digit). +Loading a real-world third-party CIF with such labels fails at the parse +boundary. The excluded-regions id regex differs again +(`^[A-Za-z0-9_]*$`) — an inconsistency among the three. + +**Fix:** relax the CIF-facing validator to match CIF label rules (or +decouple the internal dict key from the CIF label), and standardise the +pattern across categories. + +**TODOs / locations:** + +- [default.py](src/easydiffraction/datablocks/structure/categories/atom_sites/default.py#L71) +- [default.py](src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py#L38) +- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L45) + +**Depends on:** subsumes part of issue 29 (standardise CIF ID validator +pattern) with the concrete invalid-label consequence. diff --git a/docs/dev/issues/open/medium_bragg-powder-ascii-loader-returns-zero-points-instead-of-raising.md b/docs/dev/issues/open/medium_bragg-powder-ascii-loader-returns-zero-points-instead-of-raising.md new file mode 100644 index 000000000..94f6057ec --- /dev/null +++ b/docs/dev/issues/open/medium_bragg-powder-ascii-loader-returns-zero-points-instead-of-raising.md @@ -0,0 +1,23 @@ +# 150. Bragg Powder ASCII Loader Returns Zero Points Instead of Raising + +**Priority:** `[priority] medium` + +**Type:** Robustness + +When a data file has fewer than two columns, the Bragg powder loader +calls `log.error(..., exc_type=ValueError); return 0`. If the Logger is +in WARN reaction mode (it can be, per the `AGENTS.md` leaked-mode note), +this silently returns a zero-point experiment instead of raising — +whereas the total-scattering loader unconditionally `raise ValueError`. +Boundary input (a bad file) should fail consistently and loudly across +families. + +**Fix:** make the Bragg loader raise directly, like the total_pd loader. + +**TODOs / locations:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/item/bragg_pd.py#L138) +- [total_pd.py](src/easydiffraction/datablocks/experiment/item/total_pd.py#L85) + — the consistent (raising) reference + +**Depends on:** related to issue 66 (`log.error` vs `raise` strategy). diff --git a/docs/dev/issues/open/medium_bumps-drops-uncertainties-silently-on-singular-covariance.md b/docs/dev/issues/open/medium_bumps-drops-uncertainties-silently-on-singular-covariance.md new file mode 100644 index 000000000..235f02e70 --- /dev/null +++ b/docs/dev/issues/open/medium_bumps-drops-uncertainties-silently-on-singular-covariance.md @@ -0,0 +1,23 @@ +# 141. BUMPS Drops Uncertainties Silently on Singular Covariance + +**Priority:** `[priority] medium` + +**Type:** Correctness / Silent failure + +`_compute_covariance` catches `np.linalg.LinAlgError` and returns +`(None, None)`; `_sync_result_to_parameters` then sets every parameter +`uncertainty = None`. A successful BUMPS fit whose Jacobian is +rank-deficient therefore reports parameter values with blank +uncertainties and no message explaining why, so a non-programmer +scientist cannot distinguish "no uncertainty computed" from a bug. + +**Fix:** emit a deferred warning (via `_warn_after_tracking`) when +covariance computation fails, so the missing uncertainties are +explained. + +**TODOs / locations:** + +- [bumps.py](src/easydiffraction/analysis/minimizers/bumps.py#L433) +- [bumps.py](src/easydiffraction/analysis/minimizers/bumps.py#L466) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_clarify-cryspy-tof-background-cif-tag-names.md b/docs/dev/issues/open/medium_clarify-cryspy-tof-background-cif-tag-names.md new file mode 100644 index 000000000..7b492b849 --- /dev/null +++ b/docs/dev/issues/open/medium_clarify-cryspy-tof-background-cif-tag-names.md @@ -0,0 +1,19 @@ +# 21. Clarify CrysPy TOF Background CIF Tag Names + +**Priority:** `[priority] medium` + +**Type:** Correctness / Naming + +The CrysPy calculator uses TOF background CIF tags +(`_tof_backgroundpoint_time`, `_tof_backgroundpoint_intensity`) and +hardcoded `0.0` intensity values marked with `TODO: !!!!????`. The +mapping and the hardcoded defaults need verification. + +**TODOs:** + +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L734) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L735) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L738) +- [cryspy.py](src/easydiffraction/analysis/calculators/cryspy.py#L739) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_clarify-joint-fit-lifecycle-outside-execution.md b/docs/dev/issues/open/medium_clarify-joint-fit-lifecycle-outside-execution.md new file mode 100644 index 000000000..a93de08a6 --- /dev/null +++ b/docs/dev/issues/open/medium_clarify-joint-fit-lifecycle-outside-execution.md @@ -0,0 +1,13 @@ +# 121. Clarify `joint_fit` Lifecycle Outside Execution + +**Priority:** `[priority] medium` + +**Type:** Fragility + +`joint_fit` is validated and auto-populated at `fit()` time, but it does +not react when experiments are later renamed or removed. + +**Fix:** decide whether `joint_fit` should stay passive until execution, +or listen for experiment lifecycle changes and prune or warn earlier. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_cross-repository-validation-harness-nightly.md b/docs/dev/issues/open/medium_cross-repository-validation-harness-nightly.md new file mode 100644 index 000000000..4dc417263 --- /dev/null +++ b/docs/dev/issues/open/medium_cross-repository-validation-harness-nightly.md @@ -0,0 +1,36 @@ +# 113. Cross-Repository Validation Harness (nightly) + +**Priority:** `[priority] medium` + +**Type:** Test infrastructure + +Deferred cross-repository work for the +[Test Suite and Validation Strategy](../adrs/accepted/test-suite-and-validation.md) +ADR (§7, §8). The harness code lives in `diffraction-lib`; the corpus, +results database, and benchmark/reference history live in the +`diffraction` data repository, fetched at runtime and written back by a +nightly job that installs easydiffraction from PyPI (acceptance-style). + +**Items:** + +- **COD corpus check.** Download ~100–200 CIF files from the + Crystallography Open Database, load each, and record per-file status + (`ok` / `partial` + missing fields / `fail`) in a git-diffable CSV + keyed and ordered by COD id. Add a `--recheck-failed` flag to re-run + only the failed/partial entries after fixes (instead of new random + files). Extend with per-engine calculation results on the corpus. +- **Generative fuzzing.** Randomly generate ~100–200 structures (random + space group, cell, 1–10 atoms with random coordinates/ADP/occupancy), + compute patterns across engines, and record disagreements in the same + database. +- **Benchmark history + gate.** Commit `pytest-benchmark` baseline JSON + to the data repository and add a regression threshold once timing + variance is characterised on a controlled runner. (The serial + benchmark task itself now exists — see issue 16 — this is the + history/gating remainder.) +- **External-software comparison.** Add FullProf (then GSAS-II/TOPAS) + pre-calculated profiles as zipped projects in the data repository so + the Verification pages can overlay them against easydiffraction. + +**Depends on:** the `diffraction` data repository; cross-repo +coordination. diff --git a/docs/dev/issues/open/medium_custom-validation-for-parameter-descriptor-and-category-types.md b/docs/dev/issues/open/medium_custom-validation-for-parameter-descriptor-and-category-types.md new file mode 100644 index 000000000..ffaa46b49 --- /dev/null +++ b/docs/dev/issues/open/medium_custom-validation-for-parameter-descriptor-and-category-types.md @@ -0,0 +1,16 @@ +# 67. Custom Validation for Parameter/Descriptor and Category Types + +**Priority:** `[priority] medium` + +**Type:** Design + +Parameters and Descriptors use `RangeValidator`, `RegexValidator`, +`MembershipValidator` for values but rely on `@typechecked` (only in +some places) for type checking. Category switchable types use different +validation paths. Decide whether to: + +- Use custom validators for both types and values on Parameters. +- Use custom validators for category type setters. +- Standardise the approach across the codebase. + +**Depends on:** issue 38 (`@typechecked` / gemmi interaction). diff --git a/docs/dev/issues/open/medium_decide-sequential-extraction-failure-policy.md b/docs/dev/issues/open/medium_decide-sequential-extraction-failure-policy.md new file mode 100644 index 000000000..caeed5cf9 --- /dev/null +++ b/docs/dev/issues/open/medium_decide-sequential-extraction-failure-policy.md @@ -0,0 +1,14 @@ +# 124. Decide Sequential Extraction Failure Policy + +**Priority:** `[priority] medium` + +**Type:** Runtime behaviour + +Today a failed required extract rule marks that file as failed and the +run continues. The overall aggregation policy is still undefined. + +**Fix:** decide whether one failed file should abort the whole run, +remain an isolated row-level failure, or count toward a configurable +failure threshold. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_decide-whether-inactive-fit-mode-categories-stay-lenient.md b/docs/dev/issues/open/medium_decide-whether-inactive-fit-mode-categories-stay-lenient.md new file mode 100644 index 000000000..3e198a742 --- /dev/null +++ b/docs/dev/issues/open/medium_decide-whether-inactive-fit-mode-categories-stay-lenient.md @@ -0,0 +1,16 @@ +# 120. Decide Whether Inactive Fit-Mode Categories Stay Lenient + +**Priority:** `[priority] medium` + +**Type:** API design + +`Analysis` currently allows direct access to inactive mode-specific +categories such as `joint_fit` or `sequential_fit`. The values remain +editable, but inactive sections are hidden from help and dropped during +serialization. + +**Fix:** confirm whether this lenient access is the long-term contract, +or replace it with a dedicated mode error to prevent silent state loss +on save. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_define-joint-fit-weight-bounds.md b/docs/dev/issues/open/medium_define-joint-fit-weight-bounds.md new file mode 100644 index 000000000..2a340d08d --- /dev/null +++ b/docs/dev/issues/open/medium_define-joint-fit-weight-bounds.md @@ -0,0 +1,14 @@ +# 122. Define `joint_fit.weight` Bounds + +**Priority:** `[priority] medium` + +**Type:** Data model + +Joint-fit rows currently allow any non-negative weight, but the public +contract is still unclear about whether `0` means exclusion and whether +an upper bound should exist. + +**Fix:** define the supported range and validator semantics for +`joint_fit.weight`. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_define-sequential-fit-extract-target-scope.md b/docs/dev/issues/open/medium_define-sequential-fit-extract-target-scope.md new file mode 100644 index 000000000..9c28d10bd --- /dev/null +++ b/docs/dev/issues/open/medium_define-sequential-fit-extract-target-scope.md @@ -0,0 +1,15 @@ +# 123. Define `sequential_fit_extract` Target Scope + +**Priority:** `[priority] medium` + +**Type:** Data model + +Sequential extract rules currently target one numeric descriptor under +`experiment.diffrn`. Open questions remain around nested targets, +duplicate rules writing the same target, and how additional supported +prefixes should be introduced when new environment categories appear. + +**Fix:** pin the allowed target grammar and duplicate-target behaviour +in an ADR and validation rules. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_don-t-report-a-pdf-report-as-written-when-no-tex-engine-exists.md b/docs/dev/issues/open/medium_don-t-report-a-pdf-report-as-written-when-no-tex-engine-exists.md new file mode 100644 index 000000000..d45d86ffc --- /dev/null +++ b/docs/dev/issues/open/medium_don-t-report-a-pdf-report-as-written-when-no-tex-engine-exists.md @@ -0,0 +1,23 @@ +# 147. Don't Report a PDF Report as Written When No TeX Engine Exists + +**Priority:** `[priority] medium` + +**Type:** UX + +`compile_pdf_report` returns the intended `pdf_path` even when +`_find_engines()` is empty (it only logs a warning). `_save_configured` +appends that path to `report_paths`, and `project.save()` prints it +under "reports/" as if the file were created — but the PDF does not +exist on disk. The scientist sees a PDF listed in the save tree that is +not there. + +**Fix:** include only report paths that exist in the printed save tree, +or distinguish "skipped" from "written". + +**TODOs / locations:** + +- [pdf_compiler.py](src/easydiffraction/report/pdf_compiler.py#L81) +- [default.py](src/easydiffraction/project/categories/report/default.py#L274) +- [project.py](src/easydiffraction/project/project.py#L556) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_eliminate-flicker-in-live-progress-tables.md b/docs/dev/issues/open/medium_eliminate-flicker-in-live-progress-tables.md new file mode 100644 index 000000000..611a9a140 --- /dev/null +++ b/docs/dev/issues/open/medium_eliminate-flicker-in-live-progress-tables.md @@ -0,0 +1,65 @@ +# 93. Eliminate Flicker in Live Progress Tables + +**Priority:** `[priority] medium` + +**Type:** UX + +The shared `ActivityIndicator` / Rich `Live` region used by single fit, +sequential fit, and DREAM sampling visibly flickers in terminals +whenever the live renderable grows (new rows appended) or is updated at +a moderate rate. The effect is most pronounced in sequential fit because +rows are added more frequently than in single fit. + +**Findings from current investigation:** + +- Both single fit (`FitProgressTracker._refresh_activity_indicator`) and + sequential fit (`_report_chunk_progress`) push a fresh + `build_table_renderable(...)` into + `ActivityIndicator.update(content=...)` on each progress event. The + Rich `Table` instance is rebuilt from scratch every time. +- `_TerminalLiveHandle` / `ActivityIndicator` start `rich.live.Live` + with `auto_refresh=True`, + `refresh_per_second=1/_SPINNER_FRAME_SECONDS` (≈10 Hz), and + `vertical_overflow='visible'`. At every refresh tick, Rich re-renders + the full multi-line region (table + spinner line), which on many + terminals causes a visible flicker that scales with row count. +- Earlier attempts to mitigate this in sequential fit by switching to a + single-line spinner-only `Live` and printing rows above it (so Rich's + print-above-live mechanism handled them) removed flicker entirely, but + produced a different visual style from single fit and could not show + the closing border during the run. That approach was reverted for + consistency with single fit; flicker came back with it. +- `vertical_overflow='visible'` is required so the growing table is not + clipped, but it also forces Rich to repaint the whole region rather + than scroll/append. +- The spinner animation itself drives the refresh rate; lowering + `refresh_per_second` reduces flicker frequency but makes the spinner + feel sluggish. +- Single fit appears smoother in practice mainly because content changes + are throttled (`FIT_PROGRESS_UPDATE_SECONDS = 5.0`) and rows grow + slowly; the underlying mechanism is the same and it still flickers + when many iterations are appended quickly. + +**Possible directions (not yet evaluated):** + +- Decouple spinner refresh from content refresh: drive `Live` at a low + `refresh_per_second` (e.g. 2–4 Hz) and update content explicitly only + when a new row arrives, while animating the spinner via the label + string rather than Rich's renderable diff. +- Render the table once as static `console.print(...)` above a + single-line spinner-only `Live`, and re-print only the _new_ row(s) on + each update — restore the streaming approach but emit the bottom + border at the end (accept the trade-off that the closing border is not + visible during the run, or print it as part of every update with ANSI + cursor movement). +- Use `rich.live.Live(transient=False, auto_refresh=False)` and call + `live.refresh()` manually only when content changes; let the spinner + animate via a separate background timer or label updates. +- Investigate `rich.progress.Progress` with custom columns and a table + panel — Rich has optimised diff rendering there. +- Evaluate the actual cause on macOS Terminal / iTerm2 / VS Code + terminal separately — flicker behaviour differs across emulators. + +**Depends on:** nothing. Affects single fit, sequential fit, and DREAM +sampler progress displays — any fix should keep their visuals consistent +(issue #93 should be solved for all three at once). diff --git a/docs/dev/issues/open/medium_enforce-docstrings-on-all-public-methods.md b/docs/dev/issues/open/medium_enforce-docstrings-on-all-public-methods.md new file mode 100644 index 000000000..130ab5069 --- /dev/null +++ b/docs/dev/issues/open/medium_enforce-docstrings-on-all-public-methods.md @@ -0,0 +1,15 @@ +# 81. Enforce Docstrings on All Public Methods + +**Priority:** `[priority] medium` + +**Type:** Code quality + +Some public methods (e.g. `plot_meas_vs_calc`, others) lack docstrings. +Decide: + +- All public methods **must** have numpy-style docstrings. +- Private helpers: minimal one-liner docstring or none? Choose a policy. +- Enable a ruff rule (e.g. `D103`, `D102`) or add a custom check to + enforce. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_escape-user-names-before-rich-markup-rendering.md b/docs/dev/issues/open/medium_escape-user-names-before-rich-markup-rendering.md new file mode 100644 index 000000000..f83796177 --- /dev/null +++ b/docs/dev/issues/open/medium_escape-user-names-before-rich-markup-rendering.md @@ -0,0 +1,25 @@ +# 145. Escape User Names Before Rich Markup Rendering + +**Priority:** `[priority] medium` + +**Type:** Robustness + +`ConsolePrinter.paragraph()` builds a `Text`, then re-emits +`text.markup` and prints it through Rich, which re-parses markup. +`_validate_name` only rejects `/` and `\`, so a project or experiment +name containing Rich markup characters (e.g. `[red]`, or a bare `[`) is +interpreted as markup or raises `MarkupError` on `save()`. This is a +boundary-input path a user reaches simply by naming a project. + +**Fix:** escape user-supplied substrings (`rich.markup.escape`) before +constructing the paragraph, or append the name as a literal `Text` +segment rather than round-tripping through `.markup`. + +**TODOs / locations:** + +- [logging.py](src/easydiffraction/utils/logging.py#L710) +- [project.py](src/easydiffraction/project/project.py#L501) +- [default.py](src/easydiffraction/project/categories/info/default.py#L80) + — `_validate_name` + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_find-loop-for-category-missing-none-guard.md b/docs/dev/issues/open/medium_find-loop-for-category-missing-none-guard.md new file mode 100644 index 000000000..ba21b8e6c --- /dev/null +++ b/docs/dev/issues/open/medium_find-loop-for-category-missing-none-guard.md @@ -0,0 +1,24 @@ +# 144. `_find_loop_for_category` Missing None Guard + +**Priority:** `[priority] medium` + +**Type:** Robustness + +`_find_loop_for_category` does `block.find_loop(name).get_loop()` +directly, while the sibling helper `_has_cif_loop` defensively checks +for `None` and `hasattr(..., 'get_loop')` before calling. If gemmi's +`find_loop` returns a falsy/None-like reference for a tag that exists as +a non-loop scalar (a real hand-edited-CIF possibility — e.g. an +`_atom_site.label` written as a key-value instead of inside a `loop_`), +this path raises `AttributeError` instead of returning `None`. + +**Fix:** make the two helpers consistent; guard `find_loop(...)` for +`None` before `.get_loop()`. + +**TODOs / locations:** + +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L1160) +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L991) — + `_has_cif_loop` (the correct pattern) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_fix-typechecked-gemmi-interaction-in-factories.md b/docs/dev/issues/open/medium_fix-typechecked-gemmi-interaction-in-factories.md new file mode 100644 index 000000000..239639cf8 --- /dev/null +++ b/docs/dev/issues/open/medium_fix-typechecked-gemmi-interaction-in-factories.md @@ -0,0 +1,23 @@ +# 38. Fix `@typechecked` / gemmi Interaction in Factories + +**Priority:** `[priority] medium` + +**Type:** Bug + +Both `StructureFactory` and `ExperimentFactory` have `from_cif_str` +methods where `@typechecked` is commented out because it "fails to find +gemmi". They also share TODOs about adding minimal default configuration +for missing parameters and reading content from files. + +**TODOs:** + +- [factory.py](src/easydiffraction/datablocks/structure/item/factory.py#L41) +- [factory.py](src/easydiffraction/datablocks/structure/item/factory.py#L91) +- [factory.py](src/easydiffraction/datablocks/structure/item/factory.py#L115) +- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L59) + — `Add to core/factory.py?` +- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L108) +- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L177) +- [factory.py](src/easydiffraction/datablocks/experiment/item/factory.py#L201) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_guard-hand-edited-project-timestamps-on-restore.md b/docs/dev/issues/open/medium_guard-hand-edited-project-timestamps-on-restore.md new file mode 100644 index 000000000..d01b1871d --- /dev/null +++ b/docs/dev/issues/open/medium_guard-hand-edited-project-timestamps-on-restore.md @@ -0,0 +1,24 @@ +# 146. Guard Hand-Edited Project Timestamps on Restore + +**Priority:** `[priority] medium` + +**Type:** Robustness + +`created` and `last_modified` parse the stored CIF string with +`strptime` using a fixed `'%d %b %Y %H:%M:%S'` format every time the +property is read. If a user hand-edits the project file and changes the +timestamp to any other format, reading these properties (or any +report/display path that touches them) raises a bare `ValueError` with +no actionable message. + +**Fix:** validate on load and emit a descriptive error, or store the raw +string and parse lazily with a guarded message. + +**TODOs / locations:** + +- [default.py](src/easydiffraction/project/categories/info/default.py#L89) + — `_parse_timestamp` +- [default.py](src/easydiffraction/project/categories/info/default.py#L152) + — `created` / `last_modified` getters + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_implement-resetting-user-constrained-to-false.md b/docs/dev/issues/open/medium_implement-resetting-user-constrained-to-false.md new file mode 100644 index 000000000..c8e3d9d63 --- /dev/null +++ b/docs/dev/issues/open/medium_implement-resetting-user-constrained-to-false.md @@ -0,0 +1,24 @@ +# 40. Implement Resetting `.user_constrained` to `False` + +**Priority:** `[priority] medium` + +**Type:** Correctness + +`ConstraintsHandler` has a TODO to implement changing the +`.user_constrained` attribute back to `False` when constraints are +removed. + +**Concrete consequence (raises this above cosmetic):** because +`_user_constrained` is never reset on constraint/alias removal, a +parameter that was once user-constrained stays excluded from +`fittable_parameters` (`datablock.py` `fittable_parameters` filter) for +the rest of the session even after the alias/constraint is deleted — so +a parameter the user expects to refine again is silently held fixed. +This is a persisted/live-state correctness gap, not just a missing +feature. + +**TODOs:** + +- [singleton.py](src/easydiffraction/core/singleton.py#L37) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_make-datablockitem-update-categories-abstract.md b/docs/dev/issues/open/medium_make-datablockitem-update-categories-abstract.md new file mode 100644 index 000000000..cc55c3c37 --- /dev/null +++ b/docs/dev/issues/open/medium_make-datablockitem-update-categories-abstract.md @@ -0,0 +1,19 @@ +# 33. Make `DatablockItem._update_categories` Abstract + +**Priority:** `[priority] medium` + +**Type:** Design + +`DatablockItem._update_categories` has a TODO to make it abstract and +implement it in subclasses for structures (symmetry + constraints) and +experiments (calculation updates). Currently it is a concrete no-op. + +**TODOs:** + +- [datablock.py](src/easydiffraction/core/datablock.py#L39) + +**Depends on:** related to issue 11. + +**Recommended-priority note:** Part of the data `_update` refactor +cluster (with #25 / #32): make `_update_categories` abstract. **Tier 4 +(maintainability).** diff --git a/docs/dev/issues/open/medium_more-intuitive-adp-creation-api-type-aware-kwargs.md b/docs/dev/issues/open/medium_more-intuitive-adp-creation-api-type-aware-kwargs.md new file mode 100644 index 000000000..9c6e0a35a --- /dev/null +++ b/docs/dev/issues/open/medium_more-intuitive-adp-creation-api-type-aware-kwargs.md @@ -0,0 +1,21 @@ +# 135. More Intuitive ADP Creation API (type-aware kwargs) + +**Priority:** `[priority] medium` + +**Type:** API design + +Creating an atom currently requires setting `adp_type` and the +type-neutral `adp_iso`/`adp_11`… values separately (for example +`add(..., adp_type='Biso', adp_iso=0.5)`). For scientists this is less +discoverable than naming the displacement convention directly. Options +to explore: a richer creation surface with type-aware convenience +keywords (`b_iso=`/`u_iso=`/`beta=`) that set `adp_type` automatically, +and/or CIF-style auto-attachment of the sibling isotropic/anisotropic +values when `adp_type` is set. This revisits the accepted +[type-neutral-adp-parameters](docs/dev/adrs/accepted/type-neutral-adp-parameters.md) +ADR — which deliberately chose type-neutral storage to keep parameter +identity stable across switches — so it needs its own ADR + plan and is +independent of the β-tensor work that surfaced it. + +**Depends on:** the β-tensor ADP support +([adp-beta-tensor plan](docs/dev/plans/adp-beta-tensor.md)). diff --git a/docs/dev/issues/open/medium_move-common-methods-to-datablockcollection-base-class.md b/docs/dev/issues/open/medium_move-common-methods-to-datablockcollection-base-class.md new file mode 100644 index 000000000..68204d680 --- /dev/null +++ b/docs/dev/issues/open/medium_move-common-methods-to-datablockcollection-base-class.md @@ -0,0 +1,30 @@ +# 32. Move Common Methods to `DatablockCollection` Base Class + +**Priority:** `[priority] medium` + +**Type:** Maintainability + +Both `Experiments` and `Structures` collections duplicate methods +(`from_cif_str`, `from_cif_file`, `show`, `show_as_text`, etc.) that +could live in the base `DatablockCollection`. + +**TODOs:** + +- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L29) + — `Make abstract in DatablockCollection?` +- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L65) + — `Move to DatablockCollection?` +- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L82) +- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L145) +- [collection.py](src/easydiffraction/datablocks/experiment/collection.py#L151) +- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L30) +- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L48) +- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L65) +- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L82) +- [collection.py](src/easydiffraction/datablocks/structure/collection.py#L88) + +**Depends on:** nothing. + +**Recommended-priority note:** Part of the data `_update` refactor +cluster (with #25 / #33): lift duplicated collection methods to the +base. **Tier 4 (maintainability).** diff --git a/docs/dev/issues/open/medium_numeric-cif-parse-failure-silently-stores-none.md b/docs/dev/issues/open/medium_numeric-cif-parse-failure-silently-stores-none.md new file mode 100644 index 000000000..1001c8ba4 --- /dev/null +++ b/docs/dev/issues/open/medium_numeric-cif-parse-failure-silently-stores-none.md @@ -0,0 +1,24 @@ +# 142. Numeric CIF Parse Failure Silently Stores `None` + +**Priority:** `[priority] medium` + +**Type:** Robustness + +For a NUMERIC field, `_set_param_from_raw_cif_value` calls +`str_to_ufloat(raw).n` and assigns it with no parse-success check. On a +hand-edited/garbled numeric token, `str_to_ufloat` falls back to +`ufloat(default, nan)` where `default` is `None`, so +`param.value = None` runs through the validator with no clear "could not +parse numeric CIF value" diagnostic — unlike the INTEGER branch, which +warns on non-integers. + +**Fix:** emit an explicit warning/error naming the field and raw token +when numeric parsing fails, mirroring the INTEGER branch. + +**TODOs / locations:** + +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L1121) +- [utils.py](src/easydiffraction/utils/utils.py#L1094) — `str_to_ufloat` + fallback + +**Depends on:** related to issue 59 (CIF parse validation). diff --git a/docs/dev/issues/open/medium_parallel-independent-fits-for-single-independent-fit-mode.md b/docs/dev/issues/open/medium_parallel-independent-fits-for-single-independent-fit-mode.md new file mode 100644 index 000000000..5747aeb5f --- /dev/null +++ b/docs/dev/issues/open/medium_parallel-independent-fits-for-single-independent-fit-mode.md @@ -0,0 +1,12 @@ +# 89. Parallel Independent Fits for Single/Independent Fit Mode + +**Priority:** `[priority] medium` + +**Type:** Performance + +In `single` (independent) fit mode, each experiment has its own +structure parameters and is completely independent. These fits could run +in parallel threads. Sequential mode, by contrast, must remain single- +threaded because each step's output is the next step's input. + +**Depends on:** nothing (issue 78 resolved). diff --git a/docs/dev/issues/open/medium_re-enable-dream-multiprocessing-in-direct-python-scripts.md b/docs/dev/issues/open/medium_re-enable-dream-multiprocessing-in-direct-python-scripts.md new file mode 100644 index 000000000..33a41bd67 --- /dev/null +++ b/docs/dev/issues/open/medium_re-enable-dream-multiprocessing-in-direct-python-scripts.md @@ -0,0 +1,33 @@ +# 95. Re-Enable DREAM Multiprocessing in Direct Python Scripts + +**Priority:** `[priority] medium` + +**Type:** Performance / Script runtime + +On macOS and other spawn-based platforms, direct Bayesian tutorial +execution via `python script.py` or wrappers such as +`pixi run tutorial docs/docs/tutorials/ed-21.py` can fail during BUMPS +`MPMapper` startup because worker processes re-import `__main__` and +re-execute top-level tutorial code. The current defensive workaround is +to fall back to serial execution for these direct-script entry points, +which avoids the crash but disables DREAM multiprocessing and causes a +large performance drop. + +Observed behavior for `ed-21` today: + +- Jupyter execution and `easydiffraction PROJECT_DIR fit` both appear to + use working parallel DREAM and complete `361/361` in about 40 seconds. +- Direct Python-script execution of the same tutorial runs `361/361` in + about 220 seconds, consistent with the serial fallback path. + +**Possible solution:** keep the existing tracker-state cleanup before +pickling and mapper startup, but replace the blanket serial fallback +with an EasyDiffraction-controlled multiprocessing context policy. For +direct Python script entry points, prefer a `fork` context when +available so workers do not re-import the tutorial top level. Keep the +existing behavior for import-safe module entry points such as +`easydiffraction PROJECT_DIR fit` and for platforms where `fork` is +unavailable. Document the tradeoff clearly because `fork` on macOS is +less conservative than `spawn`. + +**Depends on:** related to issue 89, but independent. diff --git a/docs/dev/issues/open/medium_refactor-data-update-methods-split-and-unify.md b/docs/dev/issues/open/medium_refactor-data-update-methods-split-and-unify.md new file mode 100644 index 000000000..6f7825607 --- /dev/null +++ b/docs/dev/issues/open/medium_refactor-data-update-methods-split-and-unify.md @@ -0,0 +1,29 @@ +# 25. Refactor Data `_update` Methods (Split and Unify) + +**Priority:** `[priority] medium` + +**Type:** Maintainability + +Multiple `_update` helpers in Bragg PD, Bragg SC, and Total PD data +classes have `TODO: split into multiple methods` or +`TODO: refactor _get_valid_linked_phases` markers. The update logic +should be decomposed and the `_get_valid_linked_phases` responsibility +should be narrowed. The Total PD and Bragg PD classes should also adapt +the pattern from `bragg_sc.py`. + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L386) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L389) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L506) +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L585) +- [bragg_sc.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py#L271) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L254) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L257) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L349) + +**Depends on:** nothing. + +**Recommended-priority note:** Part of the data `_update` refactor +cluster (with #32 / #33): decompose `_update`. **Tier 4 +(maintainability).** diff --git a/docs/dev/issues/open/medium_replace-all-bare-print-calls-with-logging.md b/docs/dev/issues/open/medium_replace-all-bare-print-calls-with-logging.md new file mode 100644 index 000000000..76744ed6a --- /dev/null +++ b/docs/dev/issues/open/medium_replace-all-bare-print-calls-with-logging.md @@ -0,0 +1,21 @@ +# 65. Replace All Bare `print()` Calls with Logging + +**Priority:** `[priority] medium` + +**Type:** Code quality + +A few bare `print()` calls remain in `src/` (not `console.print()`, not +commented out, excluding vendored code). All output should go through +`log` or `console` so that verbosity is controllable. Current offenders: + +- [ascii.py](src/easydiffraction/display/plotters/ascii.py#L211) +- [ascii.py](src/easydiffraction/display/plotters/ascii.py#L354) +- [display.py](src/easydiffraction/project/display.py#L687) + +Most earlier offenders are already resolved; the remaining calculator +import prints are commented out and tracked separately under issue 19. + +**Depends on:** nothing. + +**Recommended-priority note:** Bare `print()` → logging; now only 3 real +call sites. **Tier 4 (maintainability).** diff --git a/docs/dev/issues/open/medium_replace-dead-else-branch-in-set-calc-status-with-a-real-boolean-check.md b/docs/dev/issues/open/medium_replace-dead-else-branch-in-set-calc-status-with-a-real-boolean-check.md new file mode 100644 index 000000000..2471463c3 --- /dev/null +++ b/docs/dev/issues/open/medium_replace-dead-else-branch-in-set-calc-status-with-a-real-boolean-check.md @@ -0,0 +1,22 @@ +# 151. Replace Dead `else` Branch in `_set_calc_status` With a Real Boolean Check + +**Priority:** `[priority] medium` + +**Type:** Correctness / Dead code + +The pattern +`if v: ... elif not v: ... else: raise ValueError('Expected boolean')` +has an unreachable `else` — every value is truthy or falsy, so the +validation `raise` never fires. A non-boolean (e.g. the string `'0'`, +which is truthy) is silently coerced to `'incl'` rather than rejected. +Duplicated verbatim in both data classes. + +**Fix:** replace the truthiness test with an explicit +`isinstance(v, (bool, np.bool_))` check so the guard actually runs. + +**TODOs / locations:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L407) +- [total_pd.py](src/easydiffraction/datablocks/experiment/categories/data/total_pd.py#L224) + +**Depends on:** related to issue 30 (make `calc_status` an enum). diff --git a/docs/dev/issues/open/medium_standardise-cif-id-validator-pattern-across-categories.md b/docs/dev/issues/open/medium_standardise-cif-id-validator-pattern-across-categories.md new file mode 100644 index 000000000..03952e7e8 --- /dev/null +++ b/docs/dev/issues/open/medium_standardise-cif-id-validator-pattern-across-categories.md @@ -0,0 +1,20 @@ +# 29. Standardise CIF ID Validator Pattern Across Categories + +**Priority:** `[priority] medium` + +**Type:** Consistency + +Multiple category item classes use the same regex `r'^[A-Za-z0-9_]*$'` +for their id/label validators with an identical TODO about CIF label vs. +internal label conversion. + +**TODOs:** + +- [bragg_pd.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py#L43) +- [bragg_sc.py](src/easydiffraction/datablocks/experiment/categories/data/bragg_sc.py#L39) +- [chebyshev.py](src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py#L52) +- [line_segment.py](src/easydiffraction/datablocks/experiment/categories/background/line_segment.py#L45) +- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L39) +- [default.py](src/easydiffraction/datablocks/structure/categories/atom_sites/default.py#L45) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_standardise-class-member-ordering-and-visual-section-headers.md b/docs/dev/issues/open/medium_standardise-class-member-ordering-and-visual-section-headers.md new file mode 100644 index 000000000..930c65b2e --- /dev/null +++ b/docs/dev/issues/open/medium_standardise-class-member-ordering-and-visual-section-headers.md @@ -0,0 +1,19 @@ +# 70. Standardise Class Member Ordering and Visual Section Headers + +**Priority:** `[priority] medium` + +**Type:** Code style + +Agree on and enforce a consistent ordering within every class: + +1. Class-level attributes / metadata +2. `__init__` +3. Private helper methods +4. Public properties (getters/setters) +5. Public methods + +Each group should have a comment header (e.g. +`# --- Public properties ---`) for visual separation. Some classes +already use this pattern; apply it uniformly. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_sync-property-type-hints-with-private-attributes-custom-lint.md b/docs/dev/issues/open/medium_sync-property-type-hints-with-private-attributes-custom-lint.md new file mode 100644 index 000000000..d7c9fac80 --- /dev/null +++ b/docs/dev/issues/open/medium_sync-property-type-hints-with-private-attributes-custom-lint.md @@ -0,0 +1,17 @@ +# 74. Sync Property Type Hints with Private Attributes + Custom Lint + +**Priority:** `[priority] medium` + +**Type:** Tooling / Correctness + +Public property getters return `Parameter` / `StringDescriptor` etc., +and setters accept `float` / `str` etc. These annotations must stay in +sync with the private `_attr` type. Currently there is no automated +check. Options: + +- A custom script (like `param_consistency.py`) to verify sync. +- A ruff plugin or post-ruff check step. +- Also covers: enforcing `Base` suffix (not prefix), checking missing + docstrings (issue 81), and other project-specific conventions. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_untrack-generated-tutorial-benchmark-csvs.md b/docs/dev/issues/open/medium_untrack-generated-tutorial-benchmark-csvs.md new file mode 100644 index 000000000..f274fce88 --- /dev/null +++ b/docs/dev/issues/open/medium_untrack-generated-tutorial-benchmark-csvs.md @@ -0,0 +1,17 @@ +# 162. Untrack Generated Tutorial-Benchmark CSVs + +**Priority:** `[priority] medium` + +**Type:** Hygiene + +Six `*_tutorial-benchmarks.csv` files are tracked under +`docs/dev/benchmarking/`, but `AGENTS.md` (§Workflow) states these are +"generated verification artifacts, not source changes" that should be +left untracked. They are machine/date/Python-specific snapshots that +accrue indefinitely and create noisy diffs. + +**Fix:** `git rm --cached` the generated CSVs and add +`docs/dev/benchmarking/*.csv` to `.gitignore`, or document an explicit +exception if a curated benchmark history is intentional. + +**Depends on:** related to issues 16, 113 (benchmark history/gating). diff --git a/docs/dev/issues/open/medium_validate-generated-cif-report-against-official-iucr-dictionaries.md b/docs/dev/issues/open/medium_validate-generated-cif-report-against-official-iucr-dictionaries.md new file mode 100644 index 000000000..7d9420fa1 --- /dev/null +++ b/docs/dev/issues/open/medium_validate-generated-cif-report-against-official-iucr-dictionaries.md @@ -0,0 +1,31 @@ +# 107. Validate Generated CIF Report Against Official IUCr Dictionaries + +**Priority:** `[priority] medium` + +**Type:** Test coverage + +The runtime gemmi self-check in the IUCr CIF writer was removed (it +validated our own deterministic output at write time and depended on +dictionaries under `tmp/iucr-dicts/`; see the §2.5 amendment in +[`iucr-cif-tag-alignment.md`](../adrs/accepted/iucr-cif-tag-alignment.md)). +That spec-compliance guarantee now needs to live in a dev-time test +instead. + +**Fix:** add a unit or functional test that renders both a powder and a +single-crystal IUCr report CIF and validates every emitted tag against +the latest official COMCIFS `cif_core.dic` and `cif_pow.dic`. +Requirements: + +- Cover both powder (`_pd_*`, profile/reflection loops) and + single-crystal report outputs. +- Do **not** read `tmp/` at runtime — pass the dictionaries explicitly + as a committed test fixture (or fetch them in test setup and pass the + path in). The check belongs in the test suite, not in the user's write + path. +- Parse the DDLm/CIF2 form correctly: the current dictionaries use + `save_<name>` frames with `_definition.id`, which the removed helper's + `save__tag` regex and a plain `gemmi.cif.read_file` could not handle. + Use gemmi's DDL reader or a scan adapted to the DDLm layout. +- Allow the project's private `_easydiffraction_*` extension namespace. + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_validate-inverted-excluded-regions-start-end.md b/docs/dev/issues/open/medium_validate-inverted-excluded-regions-start-end.md new file mode 100644 index 000000000..6c8cb15c6 --- /dev/null +++ b/docs/dev/issues/open/medium_validate-inverted-excluded-regions-start-end.md @@ -0,0 +1,21 @@ +# 149. Validate Inverted Excluded Regions (start > end) + +**Priority:** `[priority] medium` + +**Type:** API safety + +`start`/`end` on an excluded region are independent `NumericDescriptor`s +with no cross-field validation; `_update` builds +`region_mask = (x >= start) & (x <= end)`, so a region with +`start > end` produces an all-False mask and is silently ignored. A +scientist who enters the bounds in the wrong order gets no exclusion and +no feedback. + +**Fix:** validate `start <= end` (warn or raise) when both are set. + +**TODOs / locations:** + +- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L52) +- [default.py](src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py#L163) + +**Depends on:** nothing. diff --git a/docs/dev/issues/open/medium_verify-string-field-cif-round-trip-strips-text-delimiters.md b/docs/dev/issues/open/medium_verify-string-field-cif-round-trip-strips-text-delimiters.md new file mode 100644 index 000000000..26935fb30 --- /dev/null +++ b/docs/dev/issues/open/medium_verify-string-field-cif-round-trip-strips-text-delimiters.md @@ -0,0 +1,25 @@ +# 143. Verify String-Field CIF Round-Trip Strips `;` Text Delimiters + +**Priority:** `[priority] medium` + +**Type:** Correctness + +`_set_param_from_raw_cif_value` strips text-field delimiters once at the +top, but loop cells passed from `category_collection_from_cif` carry +already-split raw tokens, and the STRING branch only calls +`_strip_optional_quotes`. A multi-line semicolon-delimited string value +(e.g. a long `description` written as a CIF `;`-text-field) loaded via +the descriptor `from_cif` path — not the dedicated `ProjectInfo` reader +— can retain its `;\n … \n;` framing. + +**Fix:** confirm round-trip of long descriptions/strings through the +descriptor `from_cif` path and route both paths through one shared +text-field-aware reader (see issue 57). + +**TODOs / locations:** + +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L1104) +- [serialize.py](src/easydiffraction/io/cif/serialize.py#L1131) + +**Depends on:** related to issue 57 (CIF deserialisation helper +cleanup). diff --git a/docs/dev/issues/recommended-priorities.md b/docs/dev/issues/recommended-priorities.md deleted file mode 100644 index 9c8457024..000000000 --- a/docs/dev/issues/recommended-priorities.md +++ /dev/null @@ -1,126 +0,0 @@ -# EasyDiffraction — Recommended Work (Prioritised Proposal) - -**Date:** 2026-06-10 - -A curated, re-tiered recommendation of what to work on next, produced -from a whole-project analysis (ADRs, plans, `issues/open.md`, -`issues/closed.md`, roadmap, calculator backends, and the CI-skipped -verification pages). - -This is a **priority view**, not a second tracker. [`open.md`](open.md) -remains the canonical issue list; the roadmap and ADRs remain -authoritative for features and decisions. Issue numbers below reference -`open.md` **after** the 2026-06-10 renumber that removed its duplicate -numbers. - -## Meta-finding: triage has drifted - -`open.md` currently labels **every** item 🟡 Medium or 🟢 Low — there -are **no 🔴 High** items — yet several genuine high-severity correctness -problems sit in the Medium tier or only in -`docs/docs/verification/ci_skip.txt`. The recommendation below re-tiers -by real impact (silent-wrong-results and crashes first). Consider -promoting the Tier 1 items to 🔴 in `open.md`. - ---- - -## Tier 1 — Correctness / wrong science (do first) - -1. **Joint-fit weight safety** — issues **3** (rebuild stale weights - when experiments change) + **15** (validate weights before residual - normalisation). Invalid or all-zero weights currently produce `nan` / - division-by-zero straight into the minimiser. Crash + silent - corruption; self-contained. -2. **Retain per-experiment fitted parameters for plotting** — issue - **85**. In `single` mode only the last experiment's results survive, - so earlier experiments plot incorrectly after fitting. -3. **TOF profile divergences** — issue **130** (cryspy TOF Jorgensen–Von - Dreele Lorentzian, ~22% off) + issue **134** (crysfml TOF Jorgensen - ~8.5% off). Both have CI-skipped verification pages. -4. **Sample absorption (Debye–Scherrer μR)** — issue **119**. Accounts - for the entire intensity residual on the LaB₆ verification page; - well-specified (Hewat formula). See the architecture note below. -5. **Serialise `None` as `.`/`?` in CIF** — issue **84**. Spec - correctness and round-trip fidelity. - -## Tier 2 — Tooling that prevents whole bug classes - -6. **Add a static type checker to the gate** — issue **116** - (mypy/pyright/ty). A real wrong-arity `TypeError` already shipped - because nothing catches it. High leverage; land as its own - baseline-cleanup effort. -7. **Explicit `create()` signatures on collections** — issue **8**. - Typos in `create(**kwargs)` are silently dropped today. -8. **Pin the error-handling strategy** — issues **66** + **61** - (`log.error` vs `raise`, and the logger default reaction mode). - -## Tier 3 — User-visible roadmap features - -9. **Plot Bragg peaks in the library** (roadmap 🗓`high`) and **basic - preferred-orientation model via CrysPy** (roadmap 🗓`high`). -10. **Finish the BUMPS minimizer** (roadmap 🚧). -11. **Physical FCJ asymmetry model + rename `asym_empir_*`** — issue - **133**. Unblocks further CI-skipped asymmetry pages. - -## Tier 4 — Maintainability / housekeeping - -- **Data `_update` refactor cluster** — issues **25** / **32** / **33** - (decompose `_update`, lift duplicated collection methods to the base, - make `_update_categories` abstract). -- **Bare `print()` → logging** — issue **65** (now only 3 real call - sites). - ---- - -## Pending design decisions (decide before/with implementation) - -Four suggestion ADRs are still **Proposed**, plus one drafted this -session: - -- **[`lazy-pattern-recalculation.md`](../adrs/suggestions/lazy-pattern-recalculation.md)** - — highest-value/highest-risk: a missed dirty-flag setter yields - silently stale refinement results. -- **[`cif-numeric-precision.md`](../adrs/suggestions/cif-numeric-precision.md)** - — s.u.-aware CIF serialization (file size + meaningful precision). -- **[`fit-output-files-and-data-exports.md`](../adrs/suggestions/fit-output-files-and-data-exports.md)**. -- **[`documentation-ci-build.md`](../adrs/accepted/documentation-ci-build.md)**. -- **[`in-house-calculation-engine.md`](../adrs/suggestions/in-house-calculation-engine.md)** - — drafted 2026-06-10 (in review): own the core (neutron powder - Rietveld) in-repo, keep cryspy/crysfml/pdffit for the frontier. - -## Architecture note — absorption correction (issue 119) - -Both backends return only a finished, convolved profile to the -EasyDiffraction layer (`cryspy.calculate_pattern` → -`signal_plus + signal_minus`; `crysfml.calculate_pattern` → -`np.asarray(y)`), and neither exposes a CW absorption knob. So: - -- An **in-project point-wise** `A(2θ)` correction is feasible now — - multiply the summed structure profile by `A` in `bragg_pd.py` _before_ - adding the background (`_set_intensity_calc(calc + intensity_bkg)`), - reusing the per-phase scale-factor precedent. Backend-agnostic, ~small - change + a `μR` parameter (follows the `calib_sample_displacement` - SyCos precedent). -- The **physically-exact per-reflection** `A(θ_hkl)`-before-convolution - is **not** possible in our layer (both engines convolve internally); - it requires owning the engine — which is exactly the - in-house-calculation-engine ADR's motivation. - -## Ready-to-execute / status corrections - -- **β-tensor anisotropic ADP** — implementation is **in progress** as of - 2026-06-10. The plan - [`adp-beta-tensor.md`](../plans/adp-beta-tensor.md) has entered Phase - 1 (reciprocal-cell helper and `AdpTypeEnum.BETA` committed; commit - `678bd3c5` had earlier added only the ADR + plan). -- **Calculation without measured data** — shipped (#198). - -## Housekeeping completed 2026-06-10 - -For the record, these were done in the session that produced this -proposal: removed `open.md` duplicate issue numbers (15–24, 93, 116–117, -119 → reassigned 120–134) and reconciled its Summary table; refreshed -the stale issue **65** bare-`print()` metadata; added issue **134** for -the previously-untracked `pd-neut-tof_j_si` page; and marked the -SyCos/SySin LIB roadmap cell 🚧 (code landed in #197, blocked on the -unreleased cryspy PR #46). diff --git a/docs/dev/package-structure/full.md b/docs/dev/package-structure/full.md index 29d80d81f..ae33ba78c 100644 --- a/docs/dev/package-structure/full.md +++ b/docs/dev/package-structure/full.md @@ -191,7 +191,8 @@ │ ├── 📄 enums.py │ │ ├── 🏷️ class FitModeEnum │ │ ├── 🏷️ class FitResultKindEnum -│ │ └── 🏷️ class FitCorrelationSourceEnum +│ │ ├── 🏷️ class FitCorrelationSourceEnum +│ │ └── 🏷️ class SoftwareRoleEnum │ ├── 📄 fitting.py │ │ ├── 🏷️ class FitterFitOptions │ │ └── 🏷️ class Fitter @@ -378,18 +379,20 @@ │ │ │ │ ├── 🏷️ class TofScInstrument │ │ │ │ └── 🏷️ class TofPdInstrument │ │ │ ├── 📁 linked_crystal +│ │ │ ├── 📁 linked_phases +│ │ │ ├── 📁 linked_structure │ │ │ │ ├── 📄 __init__.py │ │ │ │ ├── 📄 default.py -│ │ │ │ │ └── 🏷️ class LinkedCrystal +│ │ │ │ │ └── 🏷️ class LinkedStructure │ │ │ │ └── 📄 factory.py -│ │ │ │ └── 🏷️ class LinkedCrystalFactory -│ │ │ ├── 📁 linked_phases +│ │ │ │ └── 🏷️ class LinkedStructureFactory +│ │ │ ├── 📁 linked_structures │ │ │ │ ├── 📄 __init__.py │ │ │ │ ├── 📄 default.py -│ │ │ │ │ ├── 🏷️ class LinkedPhase -│ │ │ │ │ └── 🏷️ class LinkedPhases +│ │ │ │ │ ├── 🏷️ class LinkedStructure +│ │ │ │ │ └── 🏷️ class LinkedStructures │ │ │ │ └── 📄 factory.py -│ │ │ │ └── 🏷️ class LinkedPhasesFactory +│ │ │ │ └── 🏷️ class LinkedStructuresFactory │ │ │ ├── 📁 peak │ │ │ │ ├── 📄 __init__.py │ │ │ │ ├── 📄 base.py @@ -596,6 +599,8 @@ │ ├── 📄 base.py │ │ ├── 🏷️ class RendererBase │ │ └── 🏷️ class RendererFactoryBase +│ ├── 📄 links.py +│ │ └── 🏷️ class TableLink │ ├── 📄 plotting.py │ │ ├── 🏷️ class PlotterEngineEnum │ │ ├── 🏷️ class PosteriorPairPlotStyleEnum @@ -624,7 +629,7 @@ │ ├── 📁 cif │ │ ├── 📄 __init__.py │ │ ├── 📄 handler.py -│ │ │ └── 🏷️ class CifHandler +│ │ │ └── 🏷️ class TagSpec │ │ ├── 📄 iucr_transformers.py │ │ │ ├── 🏷️ class IucrItem │ │ │ ├── 🏷️ class IucrLoop @@ -640,17 +645,21 @@ │ │ │ └── 🏷️ class _PowderPattern │ │ ├── 📄 parse.py │ │ └── 📄 serialize.py +│ ├── 📁 edi +│ │ ├── 📄 __init__.py +│ │ └── 📄 serialize.py │ ├── 📄 __init__.py │ ├── 📄 ascii.py │ └── 📄 results_sidecar.py ├── 📁 project │ ├── 📁 categories │ │ ├── 📁 info +│ │ ├── 📁 metadata │ │ │ ├── 📄 __init__.py │ │ │ ├── 📄 default.py -│ │ │ │ └── 🏷️ class ProjectInfo +│ │ │ │ └── 🏷️ class ProjectMetadata │ │ │ └── 📄 factory.py -│ │ │ └── 🏷️ class ProjectInfoFactory +│ │ │ └── 🏷️ class ProjectMetadataFactory │ │ ├── 📁 rendering_plot │ │ │ ├── 📄 __init__.py │ │ │ ├── 📄 default.py @@ -705,7 +714,7 @@ │ │ └── 🏷️ class Project │ ├── 📄 project_config.py │ │ └── 🏷️ class ProjectConfig -│ └── 📄 project_info.py +│ └── 📄 project_metadata.py ├── 📁 report │ ├── 📁 templates │ │ ├── 📁 html @@ -735,6 +744,8 @@ │ │ └── 🏷️ class ConsolePrinter │ ├── 📄 matplotlib_config.py │ └── 📄 utils.py +│ ├── 🏷️ class DataCategoryEnum +│ └── 🏷️ class TutorialFormat ├── 📄 __init__.py └── 📄 __main__.py ``` diff --git a/docs/dev/package-structure/short.md b/docs/dev/package-structure/short.md index f5f603314..e038ef6e7 100644 --- a/docs/dev/package-structure/short.md +++ b/docs/dev/package-structure/short.md @@ -183,10 +183,12 @@ │ │ │ │ ├── 📄 factory.py │ │ │ │ └── 📄 tof.py │ │ │ ├── 📁 linked_crystal +│ │ │ ├── 📁 linked_phases +│ │ │ ├── 📁 linked_structure │ │ │ │ ├── 📄 __init__.py │ │ │ │ ├── 📄 default.py │ │ │ │ └── 📄 factory.py -│ │ │ ├── 📁 linked_phases +│ │ │ ├── 📁 linked_structures │ │ │ │ ├── 📄 __init__.py │ │ │ │ ├── 📄 default.py │ │ │ │ └── 📄 factory.py @@ -287,6 +289,7 @@ │ │ └── 📄 rich.py │ ├── 📄 __init__.py │ ├── 📄 base.py +│ ├── 📄 links.py │ ├── 📄 plotting.py │ ├── 📄 progress.py │ ├── 📄 tables.py @@ -300,12 +303,16 @@ │ │ ├── 📄 iucr_writer.py │ │ ├── 📄 parse.py │ │ └── 📄 serialize.py +│ ├── 📁 edi +│ │ ├── 📄 __init__.py +│ │ └── 📄 serialize.py │ ├── 📄 __init__.py │ ├── 📄 ascii.py │ └── 📄 results_sidecar.py ├── 📁 project │ ├── 📁 categories │ │ ├── 📁 info +│ │ ├── 📁 metadata │ │ │ ├── 📄 __init__.py │ │ │ ├── 📄 default.py │ │ │ └── 📄 factory.py @@ -342,7 +349,7 @@ │ ├── 📄 display.py │ ├── 📄 project.py │ ├── 📄 project_config.py -│ └── 📄 project_info.py +│ └── 📄 project_metadata.py ├── 📁 report │ ├── 📁 templates │ │ ├── 📁 html diff --git a/docs/dev/plans/adp-beta-tensor.md b/docs/dev/plans/adp-beta-tensor.md index da2f9b8a8..003763450 100644 --- a/docs/dev/plans/adp-beta-tensor.md +++ b/docs/dev/plans/adp-beta-tensor.md @@ -254,7 +254,7 @@ they are recorded here for an accurate Phase 2 scope. CIF-style auto-attach of the sibling iso/aniso values) would improve discoverability but **revisits the accepted type-neutral ADP ADR** and is independent of the β math. It is **out of scope** for this PR; - tracked as a follow-up issue in `docs/dev/issues/open.md` and to be + tracked as a follow-up issue in `docs/dev/issues/open/` and to be designed in its own ADR + plan. This β work stays strictly inside the type-neutral model (β is a fourth `adp_type`). - **Q6 (β ADP-ellipsoid display). RESOLVED — deferred** (confirmed @@ -263,9 +263,9 @@ they are recorded here for an accurate Phase 2 scope. reciprocal-length math. Drawing β ellipsoids would need a β→U conversion in the renderer. That is **out of scope**: β atoms render as spheres for now (no crash, no wrong ellipsoid), tracked as a - follow-up issue in `docs/dev/issues/open.md` (step P1.9). The - duplicate `_reciprocal_lengths` helper is still consolidated onto the - shared helper (step P1.9); only the ellipsoid math is deferred. + follow-up issue in `docs/dev/issues/open/` (step P1.9). The duplicate + `_reciprocal_lengths` helper is still consolidated onto the shared + helper (step P1.9); only the ellipsoid math is deferred. ## Concrete files likely to change @@ -305,8 +305,8 @@ Docs / ADR: - `docs/dev/adrs/accepted/type-neutral-adp-parameters.md` — Extension section (step P1.1). -- `docs/dev/issues/open.md` — add a follow-up row for the ADP - creation-API UX (resolved Q5; step P1.1). +- `docs/dev/issues/open/` — add a follow-up row for the ADP creation-API + UX (resolved Q5; step P1.1). Tests (Phase 2): @@ -333,7 +333,7 @@ Each step is one atomic commit. Stage only the files the step touches and the new CIF tags. Verify it states that off-diagonal negatives are _already_ permitted (a statement of existing behaviour), not a newly introduced relaxation. Also add the ADP creation-API UX - follow-up row to `docs/dev/issues/open.md` (resolved Q5). Keep the + follow-up row to `docs/dev/issues/open/` (resolved Q5). Keep the original Decision/Consequences intact. Stage the ADR and `open.md`. Commit: `Extend type-neutral ADP ADR with beta tensor` - [x] **P1.2 — Reciprocal-cell helper.** Add a pure-geometry helper @@ -385,9 +385,8 @@ Each step is one atomic commit. Stage only the files the step touches call `crystallography.reciprocal_cell_lengths` (remove the duplicate math; keep the `np.ndarray` return shape its callers expect). β atoms keep rendering as spheres; add a follow-up issue - to `docs/dev/issues/open.md` for β→U ADP-ellipsoid display - (resolved Q6). Commit: - `Reuse shared reciprocal helper in structure builder` + to `docs/dev/issues/open/` for β→U ADP-ellipsoid display (resolved + Q6). Commit: `Reuse shared reciprocal helper in structure builder` - [x] **P1.10 — Phase 1 review gate.** No-code step. Mark complete, commit the checklist update alone. Commit: `Reach Phase 1 review gate` diff --git a/docs/dev/plans/calculation-without-measured-data.md b/docs/dev/plans/calculation-without-measured-data.md index b4cdf344b..0a1471f82 100644 --- a/docs/dev/plans/calculation-without-measured-data.md +++ b/docs/dev/plans/calculation-without-measured-data.md @@ -240,9 +240,8 @@ Existing files: the experiment categories or the "experiment without measured data" state to mention `data_range` (no tutorial regeneration in Phase 1; tutorial/notebook updates, if any, are handled in Phase 2 - with `pixi run notebook-prepare`). Update - `docs/dev/issues/open.md` → `closed.md` if an existing issue - tracks this. Commit: + with `pixi run notebook-prepare`). Update `docs/dev/issues/open/` + → `closed/` if an existing issue tracks this. Commit: `Document data_range and calculated-only workflow` - [x] **P1.10 — Phase 1 review gate (no code).** Mark this step `[x]` diff --git a/docs/dev/plans/documentation-snippet-tests.md b/docs/dev/plans/documentation-snippet-tests.md index 63d80ec77..367aa2423 100644 --- a/docs/dev/plans/documentation-snippet-tests.md +++ b/docs/dev/plans/documentation-snippet-tests.md @@ -58,7 +58,7 @@ shape would catch it before merge. unaffected. - **One always-on shape check, independent of markers.** In addition to marked-snippet execution, a parametrised test scans the doc set for - `from easydiffraction import <name>` and `ed.<name>` references and + `from easydiffraction import <name>` and `edi.<name>` references and asserts each resolves against the installed package. This alone would have caught the `first-steps.md` regression and needs no per-snippet curation. @@ -113,7 +113,7 @@ verification. - [ ] **P1.1 — Add the import-shape test (always-on).** Create `tests/functional/test_docs_snippets.py` with the doc-page list and a parametrised test that extracts every - `from easydiffraction import <name>` and `ed.<name>` reference + `from easydiffraction import <name>` and `edi.<name>` reference from the listed pages and asserts each name resolves on the installed `easydiffraction` package. Files: `tests/functional/test_docs_snippets.py`. Commit: diff --git a/docs/dev/plans/edstar-project-persistence.md b/docs/dev/plans/edstar-project-persistence.md new file mode 100644 index 000000000..532c2cc73 --- /dev/null +++ b/docs/dev/plans/edstar-project-persistence.md @@ -0,0 +1,525 @@ +# Edi Project Persistence Plan + +This plan follows `AGENTS.md`. There are no deliberate exceptions. + +## Status + +- [x] Draft implementation plan from the accepted local instructions and + current repository context. +- [x] Review and accept this plan. +- [x] Phase 1 - implementation commits complete. +- [x] Phase 1 review complete. +- [x] Phase 2 - tests and verification complete. +- [x] Phase 2 review complete. + +When an AI agent follows this plan, every completed Phase 1 +implementation step must be staged with explicit paths and committed +locally before moving to the next implementation step or the Phase 1 +review gate. Commits must be atomic, single-purpose, and use the commit +message rules from `AGENTS.md`. + +Phase 1 must not add or run tests. Phase 2 adds and updates tests, then +runs the verification commands listed below. + +## Related ADR + +- ADR: `docs/dev/adrs/accepted/edstar-project-persistence.md` +- Implementation branch: `edstar-project-persistence` +- Pull request target: `develop` + +This change implements one ADR. As required by `AGENTS.md`, Phase 1 must +promote the ADR from `suggestions/` to `accepted/` before opening a pull +request, update `docs/dev/adrs/index.md`, and fix links that still point +to the old suggestions path. + +## Decisions + +- Edi becomes the project persistence format: `project.edi`, + `structures/<structure>.edi`, `experiments/<experiment>.edi`, and + `analysis/analysis.edi`. +- `analysis/results.csv`, `analysis/results.h5`, and + `reports/<project>.cif` keep their current locations and purposes. +- Report CIF generation stays strict IUCr/pdCIF export. Regular project + save/load must not treat report CIF as round-trippable project state. +- Saved Edi files include the schema marker `_edi.schema_version 1`. +- Project restore accepts only Edi project files. Legacy beta + EasyDiffraction CIF project files fail with an explicit migration + error. Official CIF import names remain supported by explicit CIF + import paths where supported, and read aliases remain recorded on + handlers. +- Edi files take precedence over stale CIF siblings. If both exist, load + Edi and ignore CIF for the same project section. +- Ordinary `save()` writes Edi files. It does not need to delete stale + CIF files; precedence and clear console output handle stale siblings. +- Public Python names move to the ADR's API-oriented names with no + transitional Python properties: `project.metadata`, + `experiment.experiment_type`, `linked_structures`, `linked_structure`, + `structure_id`, atom/alias `id`, `parameter_unique_name`, and the + other explicit field renames from the ADR. +- `analysis.software` becomes a role-keyed loop with + `software[role].{name, version, url}` and a closed `(str, Enum)` role + set. Fit timestamp moves to `project.metadata.timestamp`. +- Runtime descriptors expose a read-only `url` derived from Edi names + and docs version. Static docs tables use stable relative anchors, not + runtime `param.url`. +- Parameter docs remain hand-maintained. A generated CifHandler/Edi + inventory is an audit artifact, not the source used to generate docs + tables. +- No new dependency is planned. + +## Open Questions + +- Whether Edi needs an explicit public CLI format flag. This plan + assumes no new `--format` flag: `.edi` is the default project + persistence format, and CLI help/docs name it because users see the + files. +- Whether future analysis-reference fields should adopt a broader + `<target>_id` naming convention. This plan keeps the ADR's current + decision: `parameter_unique_name` remains the persisted analysis + parameter reference field. + +## Repository Context + +Current persistence is centered in: + +- `src/easydiffraction/project/project.py` +- `src/easydiffraction/io/cif/handler.py` +- `src/easydiffraction/io/cif/serialize.py` +- `src/easydiffraction/io/cif/parse.py` +- `src/easydiffraction/datablocks/structure/collection.py` +- `src/easydiffraction/datablocks/experiment/collection.py` +- `src/easydiffraction/datablocks/experiment/item/factory.py` +- `src/easydiffraction/io/ascii.py` +- `src/easydiffraction/__main__.py` + +The main public-name changes cross these areas: + +- Structure categories: `atom_sites`, `atom_site_aniso`, and `geom`. +- Experiment categories: `experiment_type`, `linked_phases`, + `linked_crystal`, `refln`, `pref_orient`, `instrument`, `background`, + and `data`. +- Analysis categories: `aliases`, `fit_parameters`, + `fit_parameter_correlations`, and `software`. +- Project metadata: `project.metadata`, `project.project_metadata`, and + `project/categories/metadata/`. +- User-facing docs and generated notebooks under `docs/docs/`. + +The existing docs reference is a two-tab code/CIF table in +`docs/docs/user-guide/parameters.md` with per-category pages under +`docs/docs/user-guide/parameters/`. It must become the ADR's three-tab +code/Edi/CIF reference. + +## Concrete Files Likely To Change + +- ADRs and plan: `docs/dev/adrs/accepted/edstar-project-persistence.md`, + `docs/dev/adrs/index.md`, accepted ADRs that explicitly describe the + superseded CIF project layout. +- Persistence handlers: `src/easydiffraction/io/cif/handler.py`, + `src/easydiffraction/io/cif/serialize.py`, + `src/easydiffraction/io/cif/parse.py`, + `src/easydiffraction/io/cif/iucr_writer.py`, + `src/easydiffraction/io/cif/iucr_transformers.py`, plus a new + `src/easydiffraction/io/edi/` package if the implementation needs a + format-specific boundary. +- Project facade/config: `src/easydiffraction/project/project.py`, + `src/easydiffraction/project/project_config.py`, + `src/easydiffraction/project/project_metadata.py`, + `src/easydiffraction/project/categories/metadata/`, and matching + `__init__.py` files. +- Structure model: + `src/easydiffraction/datablocks/structure/categories/atom_sites/`, + `src/easydiffraction/datablocks/structure/categories/atom_site_aniso/`, + `src/easydiffraction/datablocks/structure/categories/geom/`, structure + factories/collections, and report/data-context callers that read + atom-site labels. +- Experiment model: `src/easydiffraction/datablocks/experiment/item/`, + `src/easydiffraction/datablocks/experiment/categories/experiment_type/`, + `linked_phases/`, `linked_crystal/`, `refln/`, `pref_orient/`, + `instrument/`, `background/`, `data/`, and package `__init__.py` + files. +- Analysis model: `src/easydiffraction/analysis/analysis.py`, + `src/easydiffraction/analysis/categories/aliases/`, + `src/easydiffraction/analysis/categories/fit_parameters/`, + `src/easydiffraction/analysis/categories/fit_parameter_correlations/`, + `src/easydiffraction/analysis/categories/software/`, + `src/easydiffraction/analysis/sequential.py`, and display/plotting + code that reads persisted parameter-reference names. +- Documentation and CLI: `docs/docs/user-guide/parameters.md`, + `docs/docs/user-guide/parameters/`, + `docs/docs/user-guide/analysis-workflow/`, + `docs/docs/quick-reference/index.md`, `docs/docs/tutorials/*.py`, + regenerated `docs/docs/tutorials/*.ipynb`, `docs/mkdocs.yml`, + `src/easydiffraction/__main__.py`, and + `src/easydiffraction/io/ascii.py`. +- New or updated tools: an Edi handler inventory/audit tool under + `tools/`, and a docs anchor verification tool if it is not folded into + an existing docs check. +- Tests in Phase 2: matching unit test files under + `tests/unit/easydiffraction/`, project save/load tests, CLI tests, + integration tests, script tests, and notebook regeneration checks. + +## Implementation Steps (Phase 1) + +- [x] P1.1 - Promote the ADR and mark superseded layout text. + + Move the ADR into `docs/dev/adrs/accepted/`, set status to Accepted, + update `docs/dev/adrs/index.md`, and add narrow cross-reference notes + to older accepted ADRs whose examples still describe CIF as the + regular project persistence format. Do not rewrite those historical + ADRs wholesale. + + Commit: + + ```text + Accept Edi project persistence ADR + ``` + +- [x] P1.2 - Make handler names explicit before changing tags. + + Extend `CifHandler` so each descriptor can declare: `project_name` for + Edi write tags, `import_names` for accepted read aliases, `iucr_name` + for report export, and enough category metadata for inventory/docs URL + generation. Preserve current CIF behavior while this step lands. + + Update descriptor construction only where needed to keep current CIF + output unchanged. Add the versioned docs URL resolver and a read-only + descriptor `url` property, but keep docs content changes for P1.11. + + Commit: + + ```text + Add explicit persistence names to handlers + ``` + +- [x] P1.3 - Add the generated handler inventory audit. + + Add a tool that imports the registered concrete categories and emits a + deterministic inventory of descriptor paths, Edi names, legacy CIF + import names, IUCr names, docs anchors, and ownership context. + Generate the initial inventory before any write-side tag renames so + later commits have a reviewable baseline. + + Keep this as an audit artifact. Do not use it to generate user docs + tables in this plan. + + Commit: + + ```text + Add Edi persistence inventory audit + ``` + +- [x] P1.4 - Introduce Edi project file save/load. + + Add Edi serialization and parsing boundaries, including schema marker + writing/validation and selector/body consistency checks. Update + `Project.save()` to write: + + ```text + project.edi + structures/<structure>.edi + experiments/<experiment>.edi + analysis/analysis.edi + analysis/results.csv + analysis/results.h5 + reports/<project>.cif + ``` + + Update `Project.load()` and structure/experiment/analysis loaders so + Edi wins over same-section CIF files, while legacy-only beta CIF + project files fail with explicit migration errors. Keep + `project.report.save_cif()` and the IUCr writer on the CIF path. + + Commit: + + ```text + Save and load Edi project files + ``` + +- [x] P1.5 - Rename project info to project metadata. + + Move the public facade from `project.info` to `project.metadata`, + rename the project metadata module/category paths as appropriate, and + move the project timestamp field to `project.metadata.timestamp`. Keep + runtime saved path state available through the renamed metadata + surface. Do not add a `project.info` compatibility property. + + Update save/load, CLI dry-run handling, report path helpers, display + context, and docs snippets that access project info. + + Commit: + + ```text + Rename project info facade to metadata + ``` + +- [x] P1.6 - Convert analysis software to a role loop. + + Replace the wide `_software.framework_name`, + `_software.calculator_name`, and `_software.minimizer_name` style with + a role-keyed collection such as `analysis.software['framework'].name`. + Add a closed role enum for framework, calculator, and minimizer. + + Update fit-time provenance stamping, report data context, IUCr report + rendering, HTML/TeX templates, and restore logic. Move fit timestamp + reads/writes to `project.metadata.timestamp`. + + Commit: + + ```text + Persist software provenance as role rows + ``` + +- [x] P1.7 - Rename structure identity fields. + + Rename atom-site and anisotropic ADP public row identity from `label` + to `id`, and rename alias row identity from `label` to `id` where it + belongs with analysis aliases. Update collection key declarations, + constructors, display/report code, symmetry/ADP lookup code, and + examples. Keep official CIF import aliases for `_atom_site.label` and + `_atom_site_aniso.label`; write Edi with the new `id` names. + + Commit: + + ```text + Rename structure row identities to id + ``` + +- [x] P1.8 - Rename experiment type and linked-structure surfaces. + + Rename `experiment.type` to `experiment.experiment_type`. Rename + powder `linked_phases` to `linked_structures`, single-crystal + `linked_crystal` to `linked_structure`, and linked item `id` to + `structure_id`. + + Move package/module names where the public category name changes, and + update factories, owner attachment, supported-filter logic, + calculators, display/report code, docs snippets, tutorials, and + imports. Do not add stale Python aliases. + + Commit: + + ```text + Rename experiment structure link categories + ``` + +- [x] P1.9 - Rename experiment data and instrument fields. + + Apply the remaining experiment-side API/Edi renames from the ADR: + powder `refln.phase_id` to `structure_id`, preferred-orientation + `phase_id` to `structure_id`, powder data `point_id` to `id`, TOF + calibration `quad`/`recip` to `quadratic`/`reciprocal`, and + line-segment background `x`/`y` to `position`/`intensity`. + + Update Edi write names, legacy CIF import aliases, calculators, report + writers, plotting code, docs, and tutorials. Preserve strict report + CIF output names through `iucr_name`/transformers. + + Commit: + + ```text + Rename experiment Edi fields + ``` + +- [x] P1.10 - Rename analysis parameter-reference fields. + + Rename `param_unique_name` to `parameter_unique_name` for aliases and + fit-parameter state, rename fit-parameter correlation pair fields from + `param_unique_name_i`/`param_unique_name_j` to + `parameter_unique_name_i`/`parameter_unique_name_j`, and rename + `fit_bounds_uncertainty_multiplier` to + `bounds_uncertainty_multiplier`. If the ADR's open question is + resolved differently before implementation, apply that explicit + decision consistently to aliases, fit-parameter state, correlations, + docs, and tests. + + Update analysis restore, undo, sequential fitting, plotting, + results-sidecar consumers, and display code. + + Commit: + + ```text + Rename analysis parameter reference fields + ``` + +- [x] P1.11 - Rework parameter docs and runtime links. + + Update `docs/docs/user-guide/parameters.md` to use three tabs: "How to + access in the code", "Keys in Edi", and "Keys in CIF". Rename + per-category pages under `docs/docs/user-guide/parameters/` to Edi + category names, give them Edi titles and EasyDiffraction descriptions, + and keep IUCr icon links for official dictionary tags. + + Add stable anchors that match the runtime `param.url` resolver. Static + docs tables should use relative links to the same anchors rather than + embedding runtime `param.url`. + + Update `docs/mkdocs.yml`, quick reference, workflow docs, tutorials, + ZIP/project docs, and CLI help text from CIF project persistence to + Edi project persistence. Regenerate notebooks only from edited + tutorial `.py` files during the implementation step that changes + tutorials. + + Commit: + + ```text + Document Edi parameter keys + ``` + +- [x] P1.12 - Refresh report CIF boundaries and stale-name errors. + + Ensure report CIF generation still emits strict IUCr/pdCIF and does + not accidentally use Edi project names. Remove or update regular + project-save CIF assumptions in display/report helpers while keeping + `reports/<project>.cif` intact. + + Add clear boundary errors for schema marker mismatches and legacy-only + beta CIF project directories. Do not add compatibility parameters or + properties for removed public names. + + Commit: + + ```text + Keep report CIF separate from Edi persistence + ``` + +- [x] P1.13 - Phase 1 review gate. + + Confirm all Phase 1 implementation commits are present, the plan + checklist is updated, and no tests or verification commands have been + run as part of Phase 1. Stop for review before Phase 2. + + Phase 1 review gate reached on branch `edstar-project-persistence`. + All Phase 1 implementation commits are present and no Phase 2 + verification commands were run during Phase 1. + + Commit: + + ```text + Reach Edi Phase 1 review gate + ``` + +- [x] P1.14 - Rename the space-group coordinate-system parameter. + + Record the post-review Phase 1 scope addition requested after the + original review gate: rename + `structure.space_group.it_coordinate_system_code` to + `structure.space_group.coord_system_code` and write Edi with + `_space_group.coord_system_code`. Keep official CIF import/report + names as `_space_group.IT_coordinate_system_code`, and do not preserve + the pre-release lowercase Edi spelling as a legacy alias. + + Update the accepted ADR, live ADR references, handler inventory, + source call sites, user docs, tutorials, and regenerated notebooks. + This reopened the Phase 1 review cycle after the original review-3 + sentinel; Phase 1 review is complete only after the follow-up review + accepts this additional step. + + Commit: + + ```text + Rename space-group coordinate-system parameter + ``` + +## Verification Steps (Phase 2) + +- [x] P2.1 - Add and update focused tests. + + Add or update unit tests for: + + - Edi schema marker write/read validation. + - Edi-over-CIF precedence for project, structures, experiments, and + analysis. + - Beta-window CIF import aliases for each renamed field. + - Public stale-name failures for removed Python names. + - Role-keyed `analysis.software` persistence and timestamp relocation. + - Fit-parameter and fit-parameter-correlation reference-field renames. + - `param.url` generation and docs-anchor validation. + - Report CIF continuing to use strict IUCr/pdCIF names. + - ZIP extraction and CLI help accepting Edi project archives. + + Add integration/script coverage for save/load round trips, tutorial + projects, report export, and sequential/Bayesian sidecars where those + paths are affected. + + Commit: + + ```text + Test Edi project persistence + ``` + +- [x] P2.2 - Run structure and formatting checks. + + ```bash + pixi run test-structure-check > /tmp/easydiffraction-test-structure-check.log 2>&1; test_structure_check_exit_code=$?; tail -n 200 /tmp/easydiffraction-test-structure-check.log; exit $test_structure_check_exit_code + pixi run fix > /tmp/easydiffraction-fix.log 2>&1; fix_exit_code=$?; tail -n 200 /tmp/easydiffraction-fix.log; exit $fix_exit_code + ``` + + Include regenerated `docs/dev/package-structure/full.md` and + `docs/dev/package-structure/short.md` if `pixi run fix` changes them. + + Commit: + + ```text + Apply Edi verification formatting + ``` + +- [x] P2.3 - Run static checks. + + ```bash + pixi run check > /tmp/easydiffraction-check.log 2>&1; check_exit_code=$?; tail -n 200 /tmp/easydiffraction-check.log; exit $check_exit_code + ``` + + Commit fixes only if this command identifies code or docs issues. + +- [x] P2.4 - Run unit tests. + + ```bash + pixi run unit-tests > /tmp/easydiffraction-unit-tests.log 2>&1; unit_tests_exit_code=$?; tail -n 200 /tmp/easydiffraction-unit-tests.log; exit $unit_tests_exit_code + ``` + + Commit fixes only if this command identifies unit-level issues. + +- [x] P2.5 - Run integration tests. + + ```bash + pixi run integration-tests > /tmp/easydiffraction-integration-tests.log 2>&1; integration_tests_exit_code=$?; tail -n 200 /tmp/easydiffraction-integration-tests.log; exit $integration_tests_exit_code + ``` + + If the failure is a sandbox-only multiprocessing or permission symptom + rather than a code assertion, rerun the same command with the approved + escalated permission path before changing code. + + Commit fixes only if this command identifies integration-level issues. + +- [x] P2.6 - Run script tests. + + ```bash + pixi run script-tests > /tmp/easydiffraction-script-tests.log 2>&1; script_tests_exit_code=$?; tail -n 200 /tmp/easydiffraction-script-tests.log; exit $script_tests_exit_code + ``` + + If two tutorials write to the same project directory under + `tmp/tutorials/projects/`, fix the tutorial source path, run + `pixi run notebook-prepare`, and commit the source plus regenerated + notebook. Do not include benchmark CSV files under + `docs/dev/benchmarking/` unless the user explicitly asks to update + benchmark history. + + Commit fixes only if this command identifies script-level issues. + +## Suggested Pull Request + +Title: + +```text +Add Edi project persistence +``` + +Description: + +```text +Save EasyDiffraction projects in the new Edi format while keeping +report CIF output strict for publication and exchange. The change makes +saved project files easier to read and edit, clarifies project metadata +and linked-structure names, and gives clear migration errors for older +beta CIF project directories. +``` diff --git a/docs/dev/plans/edstar-project-persistence_edi-reply-1.md b/docs/dev/plans/edstar-project-persistence_edi-reply-1.md new file mode 100644 index 000000000..ef93d3e27 --- /dev/null +++ b/docs/dev/plans/edstar-project-persistence_edi-reply-1.md @@ -0,0 +1,62 @@ +# Edi Rename Reply 1: Edstar Project Persistence + +Reply to +[`edstar-project-persistence_edi-review-1.md`](edstar-project-persistence_edi-review-1.md). +All three findings agreed and fixed. + +## Finding 1 [high] — Stale `_edi.schema_name` in test and docs — Agree, fixed + +Verdict: agree. The marker is version-only now, so both the checked +assertion and the user-facing sample had to follow. + +Action: + +- The test assertion was already corrected before this review landed: + `test_save_writes_experiment_edi_files` now asserts + `_edi.schema_version 1` + (`tests/unit/easydiffraction/project/test_project_coverage.py:427`, + commit `56037465`). +- The user-guide project-layout sample no longer shows the + `_edi.schema_name EasyDiffraction` line; it now shows only + `_edi.schema_version 1` + (`docs/docs/user-guide/analysis-workflow/project.md:120`, commit + `117dbf7c`). + +## Finding 2 [medium] — Legacy `.edifa` files silently ignored on restore — Agree, fixed + +Verdict: agree. A stale `.edifa` section in a project directory is a +boundary input and must fail loudly, like the existing `.cif` rejection, +not be skipped. + +Action (commit `4c26f0a7`): added `_raise_legacy_edifa_error()` and +`.edifa` detection at every restore site in +`src/easydiffraction/project/project.py`: + +- `_load_edi_directory()` rejects any `*.edifa` in a section directory + before loading `*.edi` (covers `structures/` and `experiments/`). +- `_load_project_metadata()` rejects `project.edifa`. +- `_resolved_analysis_path()` rejects `analysis/analysis.edifa` and a + root `analysis.edifa`. + +In each case the `.edifa` check runs _before_ the matching `.edi` +lookup, so a directory mixing a stale `.edifa` with a valid `.edi` still +fails loudly rather than silently preferring one. The error mirrors the +`.cif` migration message and names `.edi` as the replacement. Coverage +added in `tests/unit/easydiffraction/project/test_project_coverage.py` +(`test_load_rejects_legacy_edifa_section_file`, +`test_load_rejects_legacy_edifa_analysis_file`). + +## Finding 3 [low] — `pixi.toml` comment drift — Agree, fixed + +Verdict: agree. The task reads `analysis.edi`; the comment lagged. + +Action (commit `f0217275`): updated the tutorial-output task comment in +`pixi.toml:128` from `analysis.edifa` to `analysis.edi`. + +## Verification + +`pixi run check` clean (11/11). Unit suite green (3551 passed) including +the two new `.edifa`-rejection tests; integration green (192 passed) +against the regenerated `.edi` external data. Script/functional/ +tutorial-baseline gates are running as part of the final pre-push +verification. diff --git a/docs/dev/plans/edstar-project-persistence_edi-reply-2.md b/docs/dev/plans/edstar-project-persistence_edi-reply-2.md new file mode 100644 index 000000000..d9f72e036 --- /dev/null +++ b/docs/dev/plans/edstar-project-persistence_edi-reply-2.md @@ -0,0 +1,31 @@ +# Edi Rename Reply 2: Edstar Project Persistence + +Reply to +[`edstar-project-persistence_edi-review-2.md`](edstar-project-persistence_edi-review-2.md). +The single finding is agreed and fixed. + +## Finding 1 [low] — `project.edifa` rejection branch untested — Agree, fixed + +Verdict: agree. Restore is a boundary input and the project-level +`project.edifa` branch (`src/easydiffraction/project/project.py`, +`_load_project_metadata()`) was the one rejection path without a test; +only the section and analysis branches were covered. + +Action (commit `f392f51b`): added +`test_load_rejects_legacy_project_edifa_metadata` in +`tests/unit/easydiffraction/project/test_project_coverage.py`. It saves +a valid project, drops a stale `project.edifa` beside the new +`project.edi`, and asserts `Project.load()` raises the legacy `.edifa` +migration error. Because the `.edifa` check runs before the +`project.edi` read, the mixed-directory case fails loudly as intended. + +All three project-restore rejection paths — section files +(`structures/`, `experiments/`), `analysis.edifa`, and `project.edifa` — +now have explicit coverage. + +## Verification + +`pixi run check` clean (11/11). The new test passes; the full +script-tests (34 passed, 7 skipped) and tutorial-baseline (24 passed, 1 +platform skip) gates were green on the pushed branch before this +finding, and this change is test-only. diff --git a/docs/dev/plans/edstar-project-persistence_edi-review-1.md b/docs/dev/plans/edstar-project-persistence_edi-review-1.md new file mode 100644 index 000000000..498da28ac --- /dev/null +++ b/docs/dev/plans/edstar-project-persistence_edi-review-1.md @@ -0,0 +1,46 @@ +# Edi Rename Review 1: Edstar Project Persistence + +## Findings + +1. [high] The schema-marker simplification left stale `_edi.schema_name` + expectations behind. `section_to_edi()` now writes only + `_edi.schema_version 1` (`src/easydiffraction/io/edi/serialize.py:9`, + `src/easydiffraction/io/edi/serialize.py:44`, + `src/easydiffraction/io/edi/serialize.py:47`), which matches the ADR + and plan, but `test_save_writes_experiment_edi_files` still asserts + `_edi.schema_name EasyDiffraction` + (`tests/unit/easydiffraction/project/test_project_coverage.py:427`). + The user guide project-layout sample also still shows + `_edi.schema_name` + (`docs/docs/user-guide/analysis-workflow/project.md:120`). Please + update the stale test and documentation sample to the version-only + marker so the checked behavior and user-facing docs match the new Edi + schema. + +2. [medium] Old `.edifa` section files are silently ignored in mixed + project directories. `_load_edi_directory()` loads `*.edi` files and + explicitly rejects legacy `*.cif` files + (`src/easydiffraction/project/project.py:152`, + `src/easydiffraction/project/project.py:158`), but it does not + recognize `*.edifa` as an unsupported previous project-state suffix. + A directory containing `project.edi` plus stale + `structures/lbco.edifa` or `experiments/hrpt.edifa` would restore + without those structures/experiments instead of failing loudly. The + same gap exists for root or nested `analysis.edifa`, since + `_resolved_analysis_path()` checks only `.edi` and legacy `.cif` + paths (`src/easydiffraction/project/project.py:199`, + `src/easydiffraction/project/project.py:206`). Given persisted-state + restore is a boundary input, please reject `.edifa` files explicitly + with the same migration-style error rather than silently skipping + them. + +3. [low] The tutorial-output task comment in `pixi.toml` still says it + parses `analysis.edifa` (`pixi.toml:128`). The task itself now reads + `analysis.edi`, so this is documentation drift in the developer task + list. Please update the comment to avoid sending the next verifier + looking for the old extension. + +## Checks Skipped + +Static review only. I did not run tests, lint, formatters, builds, or +any `pixi` commands. diff --git a/docs/dev/plans/edstar-project-persistence_edi-review-2.md b/docs/dev/plans/edstar-project-persistence_edi-review-2.md new file mode 100644 index 000000000..e46ec7d06 --- /dev/null +++ b/docs/dev/plans/edstar-project-persistence_edi-review-2.md @@ -0,0 +1,20 @@ +# Edi Rename Review 2: Edstar Project Persistence + +## Findings + +1. [low] The new `project.edifa` rejection branch is untested. The + follow-up fix added explicit project-level detection before reading + `project.edi` (`src/easydiffraction/project/project.py:200`), but the + new coverage only exercises stale section and analysis files + (`tests/unit/easydiffraction/project/test_project_coverage.py:587`, + `tests/unit/easydiffraction/project/test_project_coverage.py:605`). + Persisted-state restore is a boundary input, and the local test rule + asks for every new code path to be covered. Please add a small test + that drops `project.edifa` next to, or instead of, `project.edi` and + asserts `Project.load()` fails with the legacy `.edifa` migration + error. + +## Checks Skipped + +Static review only. I did not run tests, lint, formatters, builds, or +any `pixi` commands. diff --git a/docs/dev/plans/edstar-project-persistence_edi-review-3.md b/docs/dev/plans/edstar-project-persistence_edi-review-3.md new file mode 100644 index 000000000..bde3084d2 --- /dev/null +++ b/docs/dev/plans/edstar-project-persistence_edi-review-3.md @@ -0,0 +1,12 @@ +# Edi Rename Review 3: Edstar Project Persistence + +**No findings. Ready to commit.** + +The review-2 finding is addressed by `f392f51b`, which adds explicit +coverage for the project-level `project.edifa` rejection path. I re-read +the reply and inspected the follow-up patch statically; the stale +marker, legacy `.edifa`, and `pixi.toml` findings from earlier Edi +rename reviews are closed. + +Static review only. I did not run tests, lint, formatters, builds, or +any `pixi` commands. diff --git a/docs/dev/roadmap/ROADMAP.md b/docs/dev/roadmap/ROADMAP.md index 74166fcce..4a5d3450d 100644 --- a/docs/dev/roadmap/ROADMAP.md +++ b/docs/dev/roadmap/ROADMAP.md @@ -53,9 +53,10 @@ Legend: | Feature | LIB | APP | | --------------------------------------------------- | --- | --- | | Isotropic _Biso_ | ✅ | 🗓 | -| Isotropic _Uiso_ | 🚧 | ✅ | -| Anisotropic _Bani_ (_B11, B22, B33, B12, B13, B23_) | 🚧 | 🗓 | -| Anisotropic _Uani_ (_U11, U22, U33, U12, U13, U23_) | 🚧 | 🗓 | +| Isotropic _Uiso_ | ✅ | ✅ | +| Anisotropic _Bani_ (_B11, B22, B33, B12, B13, B23_) | ✅ | 🗓 | +| Anisotropic _Uani_ (_U11, U22, U33, U12, U13, U23_) | ✅ | 🗓 | +| Anisotropic _β_ (_β11, β22, β33, β12, β13, β23_) | ✅ | 🗓 | --- @@ -117,16 +118,17 @@ Legend: ### Background -| Feature | LIB | APP | -| ------------------------------------------------- | --- | --- | -| Line segments type<br>_x, y_ | ✅ | ✅ | -| Chebyshev polynomial type<br>_order, coefficient_ | ✅ | 🗓 | +| Feature | LIB | APP | +| -------------------------------------------------------------------------------- | --- | --- | +| Line segments type<br>_x, y_ | ✅ | ✅ | +| Chebyshev polynomial type<br>_order, coefficient_ | ✅ | 🗓 | +| Automatic background estimation<br>_one-call baseline (arpls/fabc), auto method_ | ✅ | 🗓 | ### Preferred Orientation -| Feature | LIB | APP | -| ------------------------------------------ | -------- | --- | -| Basic preferred orientation model (CrysPy) | 🗓`high` | 🗓 | +| Feature | LIB | APP | +| ------------------------------------------------- | --- | --- | +| Basic preferred orientation model (March–Dollase) | ✅ | 🗓 | ### Instrument — Constant Wavelength diff --git a/docs/docs/api-reference/index.md b/docs/docs/api-reference/index.md index 6f1f30354..e6b8d7b94 100644 --- a/docs/docs/api-reference/index.md +++ b/docs/docs/api-reference/index.md @@ -1,4 +1,5 @@ --- +title: API Reference icon: material/code-braces-box --- diff --git a/docs/docs/assets/stylesheets/extra.css b/docs/docs/assets/stylesheets/extra.css index 2ab67a91f..827b2fe08 100644 --- a/docs/docs/assets/stylesheets/extra.css +++ b/docs/docs/assets/stylesheets/extra.css @@ -185,7 +185,9 @@ label.md-nav__title[for="__drawer"] { /* Change line height of the table cells */ .md-typeset td, .md-typeset th { - line-height: 1.25 !important; + line-height: 2.35 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; } /* Change vertical alignment of the icon inside the table cells */ @@ -207,7 +209,7 @@ label.md-nav__title[for="__drawer"] { /* Keep prose line length stable when sidebars are hidden at narrower widths */ .md-main .md-content > .md-content__inner { - width: min(100%, 45em); + width: min(100%, 47em); margin-left: auto !important; margin-right: auto !important; } @@ -296,6 +298,22 @@ body[data-md-color-scheme="slate"] .jupyter-wrapper { padding-left: 0.85em !important; } +/* Force the docs site to honour the compact table spacing the tablers + set inline (see TABLE_CELL_* in display/tablers/base.py). The values + here MUST mirror those constants. This rule exists only because + mkdocs-Material's `.md-typeset table` base typography (line-height + inherited from `.md-typeset { line-height: 1.6 }` and Material's tall + default cell padding) otherwise inflates these rows on the site, + whereas JupyterLab renders the same inline markup compactly. Scoped + to rendered HTML *outputs* (.jp-RenderedHTML), so markdown prose + tables (.jp-RenderedMarkdown) and code-input cells are untouched. */ +.jp-RenderedHTML td, +.jp-RenderedHTML th { + padding-top: 0.15em !important; + padding-bottom: 0.15em !important; + line-height: 1.2 !important; +} + /* Keep inline Three.js structure outputs inside the notebook stacking context. Generated notebook HTML contains internal z-index values, so isolate the viewer to stop its controls from painting over the sticky MkDocs header. */ diff --git a/docs/docs/cli/index.md b/docs/docs/cli/index.md index d5e88388f..0ad0b7f43 100644 --- a/docs/docs/cli/index.md +++ b/docs/docs/cli/index.md @@ -1,4 +1,5 @@ --- +title: Command-Line Interface icon: material/console --- @@ -91,6 +92,14 @@ Download a specific tutorial by ID: python -m easydiffraction download-tutorial 1 ``` +By default this downloads the Jupyter notebook (`.ipynb`). Use `--py` +for the plain-Python script, or pass both flags to get both files: + +```bash +python -m easydiffraction download-tutorial 1 --py # script only +python -m easydiffraction download-tutorial 1 --ipynb --py # both +``` + Download all available tutorials: ```bash @@ -114,14 +123,14 @@ python -m easydiffraction PROJECT_DIR fit ``` `PROJECT_DIR` is the path to a project directory previously created by -`project.save_as()`. It must contain a `project.cif` file along with the +`project.save_as()`. It must contain a `project.edi` file along with the `structures/`, `experiments/`, and `analysis/` subdirectories. After fitting, the command displays the fit results and a project summary. By default, updated parameter values are **saved back** to the project directory. -If `project.cif` enables any `_report.*` output flags, the same save +If `project.edi` enables any `_report.*` output flags, the same save also writes those reports. For example, `_report.html true` writes the HTML report after the fit, and `_report.tex true` plus `_report.pdf true` writes the TeX bundle and PDF when a TeX engine is diff --git a/docs/docs/installation-and-setup/index.md b/docs/docs/installation-and-setup/index.md index ee86679ba..8e07ddf50 100644 --- a/docs/docs/installation-and-setup/index.md +++ b/docs/docs/installation-and-setup/index.md @@ -1,4 +1,5 @@ --- +title: Installation & Setup icon: material/cog-box --- diff --git a/docs/docs/introduction/index.md b/docs/docs/introduction/index.md index 35d4656b4..e73b08e23 100644 --- a/docs/docs/introduction/index.md +++ b/docs/docs/introduction/index.md @@ -1,4 +1,5 @@ --- +title: Introduction icon: material/information-slab-circle --- diff --git a/docs/docs/quick-reference/index.md b/docs/docs/quick-reference/index.md index b246586d1..57fd61bb9 100644 --- a/docs/docs/quick-reference/index.md +++ b/docs/docs/quick-reference/index.md @@ -1,4 +1,5 @@ --- +title: Quick Reference icon: material/clipboard-text-outline --- @@ -17,9 +18,9 @@ and [Tutorials](../tutorials/index.md). Import the package and create or load a project: ```python -import easydiffraction as ed +import easydiffraction as edi -project = ed.Project(name='lbco_hrpt') +project = edi.Project(name='lbco_hrpt') ``` ```python @@ -31,18 +32,18 @@ project = Project.load('lbco_hrpt') Check the installed version: ```python -ed.show_version() +edi.show_version() ``` ## Get Example Data -Download a dataset by ID into a local directory: +Download a dataset by its slug into a local directory: ```python -ed.list_data() +edi.list_data() -structure_path = ed.download_data(id=1, destination='data') -data_path = ed.download_data(id=3, destination='data') +structure_path = edi.download_data('struct-lbco', destination='data') +data_path = edi.download_data('meas-lbco-hrpt', destination='data') ``` Project archives are extracted automatically, and `download_data()` @@ -51,9 +52,9 @@ returns the extracted project directory path. For tutorial notebooks: ```python -ed.list_tutorials() -ed.download_tutorial(id=1, destination='tutorials') -ed.download_all_tutorials(destination='tutorials') +edi.list_tutorials() +edi.download_tutorial('refine-lbco-hrpt-from-cif', destination='tutorials') +edi.download_all_tutorials(destination='tutorials') ``` ## Build a Project @@ -74,7 +75,7 @@ project.structures.create(name='lbco') structure = project.structures['lbco'] structure.space_group.name_h_m = 'P m -3 m' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' structure.cell.length_a = 3.88 ``` @@ -82,7 +83,7 @@ Add an atom site: ```python structure.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -121,8 +122,8 @@ experiment.peak.broad_lorentz_y = 0.1 Add background points and excluded regions: ```python -experiment.background.create(id='1', x=10, y=170) -experiment.background.create(id='2', x=30, y=170) +experiment.background.create(id='1', position=10, intensity=170) +experiment.background.create(id='2', position=30, intensity=170) experiment.excluded_regions.create(id='1', start=0, end=5) experiment.excluded_regions.create(id='2', start=165, end=180) @@ -131,19 +132,19 @@ experiment.excluded_regions.create(id='2', start=165, end=180) Link a structure to an experiment: ```python -experiment.linked_phases.create(id='lbco', scale=10.0) +experiment.linked_structures.create(structure_id='lbco', scale=10.0) ``` ## Inspect the Project -Show names and CIF text: +Show names and serialized text: ```python project.structures.show_names() project.experiments.show_names() -structure.show_as_cif() -experiment.show_as_cif() +structure.show_as_text() +experiment.show_as_text() ``` Open the main display views: @@ -154,7 +155,9 @@ project.display.parameters.all() project.display.parameters.fittable() project.display.parameters.free() project.display.parameters.access() -project.display.parameters.cif_uids() +project.display.parameters.uid() +project.display.parameters.edi() +project.display.parameters.cif() ``` ## Show Tables and Select Types @@ -268,7 +271,7 @@ structure.cell.length_a.help() structure.atom_sites['O'].adp_iso.help() experiment.instrument.calib_twotheta_offset.help() -experiment.linked_phases['lbco'].scale.help() +experiment.linked_structures['lbco'].scale.help() ``` The usual navigation pattern is: @@ -287,8 +290,8 @@ structure.atom_sites['O'].adp_iso.free = True experiment.instrument.calib_twotheta_offset.free = True experiment.peak.broad_gauss_u.free = True -experiment.background['1'].y.free = True -experiment.linked_phases['lbco'].scale.free = True +experiment.background['1'].intensity.free = True +experiment.linked_structures['lbco'].scale.free = True ``` Choose calculators and minimizers: @@ -364,11 +367,11 @@ expression using those aliases: ```python project.analysis.aliases.create( - label='biso_la', + id='biso_la', param=project.structures['lbco'].atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_ba', + id='biso_ba', param=project.structures['lbco'].atom_sites['Ba'].adp_iso, ) @@ -400,7 +403,7 @@ project.save() Load it again: ```python -project = ed.Project.load('lbco_hrpt') +project = edi.Project.load('lbco_hrpt') ``` Run a saved project from the command line: @@ -413,15 +416,15 @@ python -m easydiffraction lbco_hrpt undo python -m easydiffraction lbco_hrpt undo --dry ``` -When `project.cif` enables `_report.cif`, `_report.html`, `_report.tex`, +When `project.edi` enables `_report.cif`, `_report.html`, `_report.tex`, or `_report.pdf`, the `fit` command writes those reports during the normal project save. Load a saved example project straight from `download_data()`: ```python -saved_project_dir = ed.download_data(id=30, destination='projects') -project = ed.Project.load(saved_project_dir) +saved_project_dir = edi.download_data('proj-lbco-hrpt', destination='projects') +project = edi.Project.load(saved_project_dir) ``` ## Command-Line Reminders diff --git a/docs/docs/tutorials/ed-24.ipynb b/docs/docs/tutorials/bayesian-dream-display-lbco-hrpt.ipynb similarity index 90% rename from docs/docs/tutorials/ed-24.ipynb rename to docs/docs/tutorials/bayesian-dream-display-lbco-hrpt.ipynb index b4dbb1fee..81676c79b 100644 --- a/docs/docs/tutorials/ed-24.ipynb +++ b/docs/docs/tutorials/bayesian-dream-display-lbco-hrpt.ipynb @@ -27,7 +27,7 @@ "# Bayesian Analysis Display (`bumps-dream`): LBCO, HRPT\n", "\n", "This tutorial shows how to reopen the Bayesian project created in\n", - "`ed-21.py` and inspect the saved fit results without rerunning DREAM.\n", + "`bayesian-dream-lbco-hrpt.py` and inspect the saved fit results without rerunning DREAM.\n", "\n", "The project already contains posterior samples together with cached\n", "posterior density, pair, and predictive data, so the plots below are\n", @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -65,11 +65,11 @@ "id": "5", "metadata": {}, "source": [ - "### Download Project\n", + "### Locate Project\n", "\n", - "The returned path points directly to the saved project directory with\n", - "the completed Bayesian fit and persisted posterior samples and plot\n", - "caches." + "Download and extract the saved Bayesian project, with the completed\n", + "fit, persisted posterior samples, and plot caches, from the\n", + "EasyDiffraction data repository." ] }, { @@ -79,7 +79,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_dir = ed.download_data(id=39, destination='projects')" + "project_dir = edi.download_data('proj-lbco-hrpt-dream', destination='projects')" ] }, { @@ -100,7 +100,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project.load(project_dir)" + "project = edi.Project.load(project_dir)" ] }, { @@ -261,7 +261,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_24_lbco_hrpt_bumps_dream')" + "project.save_as(dir_path='projects/bayesian-dream-display-lbco-hrpt')" ] } ], diff --git a/docs/docs/tutorials/ed-24.py b/docs/docs/tutorials/bayesian-dream-display-lbco-hrpt.py similarity index 80% rename from docs/docs/tutorials/ed-24.py rename to docs/docs/tutorials/bayesian-dream-display-lbco-hrpt.py index 984706474..da9252694 100644 --- a/docs/docs/tutorials/ed-24.py +++ b/docs/docs/tutorials/bayesian-dream-display-lbco-hrpt.py @@ -2,7 +2,7 @@ # # Bayesian Analysis Display (`bumps-dream`): LBCO, HRPT # # This tutorial shows how to reopen the Bayesian project created in -# `ed-21.py` and inspect the saved fit results without rerunning DREAM. +# `bayesian-dream-lbco-hrpt.py` and inspect the saved fit results without rerunning DREAM. # # The project already contains posterior samples together with cached # posterior density, pair, and predictive data, so the plots below are @@ -12,20 +12,20 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📂 Load Project # %% [markdown] -# ### Download Project +# ### Locate Project # -# The returned path points directly to the saved project directory with -# the completed Bayesian fit and persisted posterior samples and plot -# caches. +# Download and extract the saved Bayesian project, with the completed +# fit, persisted posterior samples, and plot caches, from the +# EasyDiffraction data repository. # %% -project_dir = ed.download_data(id=39, destination='projects') +project_dir = edi.download_data('proj-lbco-hrpt-dream', destination='projects') # %% [markdown] # ### Load Project @@ -34,7 +34,7 @@ # caches. No new fit is launched in this tutorial. # %% -project = ed.Project.load(project_dir) +project = edi.Project.load(project_dir) # %% [markdown] # ## 📊 Inspect Results @@ -99,4 +99,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_24_lbco_hrpt_bumps_dream') +project.save_as(dir_path='projects/bayesian-dream-display-lbco-hrpt') diff --git a/docs/docs/tutorials/ed-21.ipynb b/docs/docs/tutorials/bayesian-dream-lbco-hrpt.ipynb similarity index 95% rename from docs/docs/tutorials/ed-21.ipynb rename to docs/docs/tutorials/bayesian-dream-lbco-hrpt.ipynb index 0b4ab246e..5118d3721 100644 --- a/docs/docs/tutorials/ed-21.ipynb +++ b/docs/docs/tutorials/bayesian-dream-lbco-hrpt.ipynb @@ -62,7 +62,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -87,7 +87,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='lbco_hrpt_bumps_dream')" + "project = edi.Project(name='lbco_hrpt_bumps_dream')" ] }, { @@ -97,7 +97,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_21_lbco_hrpt_bumps_dream')" + "project.save_as(dir_path='projects/bayesian-dream-lbco-hrpt')" ] }, { @@ -140,7 +140,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'P m -3 m'\n", - "structure.space_group.it_coordinate_system_code = '1'" + "structure.space_group.coord_system_code = '1'" ] }, { @@ -171,7 +171,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='La',\n", + " id='La',\n", " type_symbol='La',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -182,7 +182,7 @@ " occupancy=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Ba',\n", + " id='Ba',\n", " type_symbol='Ba',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -193,7 +193,7 @@ " occupancy=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Co',\n", + " id='Co',\n", " type_symbol='Co',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -203,7 +203,7 @@ " adp_iso=0.2190,\n", ")\n", "structure.atom_sites.create(\n", - " label='O',\n", + " id='O',\n", " type_symbol='O',\n", " fract_x=0,\n", " fract_y=0.5,\n", @@ -262,7 +262,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=3, destination='data')" + "data_path = edi.download_data('meas-lbco-hrpt', destination='data')" ] }, { @@ -315,7 +315,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases.create(id='lbco', scale=9.1351)" + "experiment.linked_structures.create(structure_id='lbco', scale=9.1351)" ] }, { @@ -371,10 +371,10 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.background.create(id='1', x=10, y=168.5585)\n", - "experiment.background.create(id='2', x=30, y=164.3357)\n", - "experiment.background.create(id='3', x=50, y=166.8881)\n", - "experiment.background.create(id='4', x=110, y=175.4006)" + "experiment.background.create(id='1', position=10, intensity=168.5585)\n", + "experiment.background.create(id='2', position=30, intensity=164.3357)\n", + "experiment.background.create(id='3', position=50, intensity=166.8881)\n", + "experiment.background.create(id='4', position=110, intensity=175.4006)" ] }, { @@ -424,7 +424,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lbco'].scale.free = True\n", + "experiment.linked_structures['lbco'].scale.free = True\n", "experiment.peak.broad_gauss_u.free = True\n", "experiment.peak.broad_gauss_v.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True" diff --git a/docs/docs/tutorials/ed-21.py b/docs/docs/tutorials/bayesian-dream-lbco-hrpt.py similarity index 93% rename from docs/docs/tutorials/ed-21.py rename to docs/docs/tutorials/bayesian-dream-lbco-hrpt.py index 002f3ba4d..504d397cc 100644 --- a/docs/docs/tutorials/ed-21.py +++ b/docs/docs/tutorials/bayesian-dream-lbco-hrpt.py @@ -25,7 +25,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -38,10 +38,10 @@ # it later if needed. # %% -project = ed.Project(name='lbco_hrpt_bumps_dream') +project = edi.Project(name='lbco_hrpt_bumps_dream') # %% -project.save_as(dir_path='projects/ed_21_lbco_hrpt_bumps_dream') +project.save_as(dir_path='projects/bayesian-dream-lbco-hrpt') # %% [markdown] # ## 🧩 Define Structure @@ -58,7 +58,7 @@ # %% structure.space_group.name_h_m = 'P m -3 m' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' # %% structure.cell.length_a = 3.88 @@ -70,7 +70,7 @@ # %% structure.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -81,7 +81,7 @@ occupancy=0.5, ) structure.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -92,7 +92,7 @@ occupancy=0.5, ) structure.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -102,7 +102,7 @@ adp_iso=0.2190, ) structure.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -132,7 +132,7 @@ # downloading from the repository. # %% -data_path = ed.download_data(id=3, destination='data') +data_path = edi.download_data('meas-lbco-hrpt', destination='data') # %% [markdown] # Create the experiment object and specify the sample form, beam mode, @@ -154,7 +154,7 @@ # Link the structural phase to the experiment. # %% -experiment.linked_phases.create(id='lbco', scale=9.1351) +experiment.linked_structures.create(structure_id='lbco', scale=9.1351) # %% [markdown] # Set instrument and peak profile parameters. @@ -179,10 +179,10 @@ # exclude regions that are not intended to contribute to the fit. # %% -experiment.background.create(id='1', x=10, y=168.5585) -experiment.background.create(id='2', x=30, y=164.3357) -experiment.background.create(id='3', x=50, y=166.8881) -experiment.background.create(id='4', x=110, y=175.4006) +experiment.background.create(id='1', position=10, intensity=168.5585) +experiment.background.create(id='2', position=30, intensity=164.3357) +experiment.background.create(id='3', position=50, intensity=166.8881) +experiment.background.create(id='4', position=110, intensity=175.4006) # %% experiment.excluded_regions.create(id='1', start=0, end=10) @@ -206,7 +206,7 @@ structure.cell.length_a.free = True # %% -experiment.linked_phases['lbco'].scale.free = True +experiment.linked_structures['lbco'].scale.free = True experiment.peak.broad_gauss_u.free = True experiment.peak.broad_gauss_v.free = True experiment.instrument.calib_twotheta_offset.free = True diff --git a/docs/docs/tutorials/ed-25.ipynb b/docs/docs/tutorials/bayesian-emcee-lbco-hrpt.ipynb similarity index 95% rename from docs/docs/tutorials/ed-25.ipynb rename to docs/docs/tutorials/bayesian-emcee-lbco-hrpt.ipynb index b7757aa2f..a34779094 100644 --- a/docs/docs/tutorials/ed-25.ipynb +++ b/docs/docs/tutorials/bayesian-emcee-lbco-hrpt.ipynb @@ -62,7 +62,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -87,7 +87,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='lbco_hrpt_emcee')" + "project = edi.Project(name='lbco_hrpt_emcee')" ] }, { @@ -97,7 +97,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_25_lbco_hrpt_emcee')" + "project.save_as(dir_path='projects/bayesian-emcee-lbco-hrpt')" ] }, { @@ -140,7 +140,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'P m -3 m'\n", - "structure.space_group.it_coordinate_system_code = '1'" + "structure.space_group.coord_system_code = '1'" ] }, { @@ -171,7 +171,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='La',\n", + " id='La',\n", " type_symbol='La',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -182,7 +182,7 @@ " occupancy=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Ba',\n", + " id='Ba',\n", " type_symbol='Ba',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -193,7 +193,7 @@ " occupancy=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Co',\n", + " id='Co',\n", " type_symbol='Co',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -203,7 +203,7 @@ " adp_iso=0.2190,\n", ")\n", "structure.atom_sites.create(\n", - " label='O',\n", + " id='O',\n", " type_symbol='O',\n", " fract_x=0,\n", " fract_y=0.5,\n", @@ -262,7 +262,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=3, destination='data')" + "data_path = edi.download_data('meas-lbco-hrpt', destination='data')" ] }, { @@ -315,7 +315,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases.create(id='lbco', scale=9.1351)" + "experiment.linked_structures.create(structure_id='lbco', scale=9.1351)" ] }, { @@ -371,10 +371,10 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.background.create(id='1', x=10, y=168.5585)\n", - "experiment.background.create(id='2', x=30, y=164.3357)\n", - "experiment.background.create(id='3', x=50, y=166.8881)\n", - "experiment.background.create(id='4', x=110, y=175.4006)" + "experiment.background.create(id='1', position=10, intensity=168.5585)\n", + "experiment.background.create(id='2', position=30, intensity=164.3357)\n", + "experiment.background.create(id='3', position=50, intensity=166.8881)\n", + "experiment.background.create(id='4', position=110, intensity=175.4006)" ] }, { @@ -424,7 +424,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lbco'].scale.free = True\n", + "experiment.linked_structures['lbco'].scale.free = True\n", "experiment.peak.broad_gauss_u.free = True\n", "experiment.peak.broad_gauss_v.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True" diff --git a/docs/docs/tutorials/ed-25.py b/docs/docs/tutorials/bayesian-emcee-lbco-hrpt.py similarity index 93% rename from docs/docs/tutorials/ed-25.py rename to docs/docs/tutorials/bayesian-emcee-lbco-hrpt.py index 662615def..6d44aeaf5 100644 --- a/docs/docs/tutorials/ed-25.py +++ b/docs/docs/tutorials/bayesian-emcee-lbco-hrpt.py @@ -25,7 +25,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -38,10 +38,10 @@ # it later if needed. # %% -project = ed.Project(name='lbco_hrpt_emcee') +project = edi.Project(name='lbco_hrpt_emcee') # %% -project.save_as(dir_path='projects/ed_25_lbco_hrpt_emcee') +project.save_as(dir_path='projects/bayesian-emcee-lbco-hrpt') # %% [markdown] # ## 🧩 Define Structure @@ -58,7 +58,7 @@ # %% structure.space_group.name_h_m = 'P m -3 m' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' # %% structure.cell.length_a = 3.88 @@ -70,7 +70,7 @@ # %% structure.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -81,7 +81,7 @@ occupancy=0.5, ) structure.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -92,7 +92,7 @@ occupancy=0.5, ) structure.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -102,7 +102,7 @@ adp_iso=0.2190, ) structure.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -132,7 +132,7 @@ # downloading from the repository. # %% -data_path = ed.download_data(id=3, destination='data') +data_path = edi.download_data('meas-lbco-hrpt', destination='data') # %% [markdown] # Create the experiment object and specify the sample form, beam mode, @@ -154,7 +154,7 @@ # Link the structural phase to the experiment. # %% -experiment.linked_phases.create(id='lbco', scale=9.1351) +experiment.linked_structures.create(structure_id='lbco', scale=9.1351) # %% [markdown] # Set instrument and peak profile parameters. @@ -179,10 +179,10 @@ # exclude regions that are not intended to contribute to the fit. # %% -experiment.background.create(id='1', x=10, y=168.5585) -experiment.background.create(id='2', x=30, y=164.3357) -experiment.background.create(id='3', x=50, y=166.8881) -experiment.background.create(id='4', x=110, y=175.4006) +experiment.background.create(id='1', position=10, intensity=168.5585) +experiment.background.create(id='2', position=30, intensity=164.3357) +experiment.background.create(id='3', position=50, intensity=166.8881) +experiment.background.create(id='4', position=110, intensity=175.4006) # %% experiment.excluded_regions.create(id='1', start=0, end=10) @@ -206,7 +206,7 @@ structure.cell.length_a.free = True # %% -experiment.linked_phases['lbco'].scale.free = True +experiment.linked_structures['lbco'].scale.free = True experiment.peak.broad_gauss_u.free = True experiment.peak.broad_gauss_v.free = True experiment.instrument.calib_twotheta_offset.free = True diff --git a/docs/docs/tutorials/ed-26.ipynb b/docs/docs/tutorials/bayesian-emcee-resume-lbco-hrpt.ipynb similarity index 90% rename from docs/docs/tutorials/ed-26.ipynb rename to docs/docs/tutorials/bayesian-emcee-resume-lbco-hrpt.ipynb index 1e4460336..916a49785 100644 --- a/docs/docs/tutorials/ed-26.ipynb +++ b/docs/docs/tutorials/bayesian-emcee-resume-lbco-hrpt.ipynb @@ -66,7 +66,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -82,11 +82,10 @@ "id": "5", "metadata": {}, "source": [ - "### Download Project\n", + "### Locate Project\n", "\n", - "The returned path points directly to the saved project directory with\n", - "the completed Bayesian fit and persisted posterior samples and plot\n", - "caches." + "Download and extract the saved emcee project, with the persisted chain\n", + "and posterior caches, from the EasyDiffraction data repository." ] }, { @@ -96,7 +95,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_dir = ed.download_data(id=38, destination='projects')" + "project_dir = edi.download_data('proj-lbco-hrpt-emcee', destination='projects')" ] }, { @@ -117,20 +116,39 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project.load(project_dir)" + "project = edi.Project.load(project_dir)" ] }, { "cell_type": "markdown", "id": "9", "metadata": {}, + "source": [ + "Re-save the project to a fresh working directory so resuming the\n", + "chain below writes there instead of the bundled read-only copy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='projects/bayesian-emcee-resume-lbco-hrpt')" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, "source": [ "## 📊 Inspect Results" ] }, { "cell_type": "markdown", - "id": "10", + "id": "12", "metadata": {}, "source": [ "### Display Structure\n", @@ -141,7 +159,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -150,7 +168,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "14", "metadata": {}, "source": [ "### Display Fit Results\n", @@ -163,7 +181,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -172,7 +190,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "16", "metadata": {}, "source": [ "### Display Correlations\n", @@ -183,7 +201,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -192,7 +210,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "18", "metadata": {}, "source": [ "### Display Posterior Densities\n", @@ -204,7 +222,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -214,7 +232,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -223,7 +241,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "21", "metadata": {}, "source": [ "### Display Posterior Predictive\n", @@ -237,7 +255,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -246,7 +264,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "23", "metadata": {}, "source": [ "A zoomed view is useful for checking the propagated uncertainty in a\n", @@ -256,7 +274,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -265,7 +283,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "25", "metadata": {}, "source": [ "## 🎲 Resume Sampling" @@ -273,7 +291,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "26", "metadata": {}, "source": [ "### Run Sampling\n", @@ -287,7 +305,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -298,7 +316,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -307,7 +325,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "29", "metadata": {}, "source": [ "### Display Resumed Posterior\n", @@ -318,7 +336,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -328,7 +346,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -338,7 +356,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -347,7 +365,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "33", "metadata": {}, "source": [ "## 💾 Save Project" @@ -356,11 +374,11 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "34", "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_26_lbco_hrpt_emcee')" + "project.save_as(dir_path='projects/bayesian-emcee-resume-lbco-hrpt')" ] } ], diff --git a/docs/docs/tutorials/ed-26.py b/docs/docs/tutorials/bayesian-emcee-resume-lbco-hrpt.py similarity index 86% rename from docs/docs/tutorials/ed-26.py rename to docs/docs/tutorials/bayesian-emcee-resume-lbco-hrpt.py index d3d9be817..006b24aae 100644 --- a/docs/docs/tutorials/ed-26.py +++ b/docs/docs/tutorials/bayesian-emcee-resume-lbco-hrpt.py @@ -29,20 +29,19 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📂 Load Project # %% [markdown] -# ### Download Project +# ### Locate Project # -# The returned path points directly to the saved project directory with -# the completed Bayesian fit and persisted posterior samples and plot -# caches. +# Download and extract the saved emcee project, with the persisted chain +# and posterior caches, from the EasyDiffraction data repository. # %% -project_dir = ed.download_data(id=38, destination='projects') +project_dir = edi.download_data('proj-lbco-hrpt-emcee', destination='projects') # %% [markdown] # ### Load Project @@ -51,7 +50,14 @@ # caches. No new fit is launched in this tutorial. # %% -project = ed.Project.load(project_dir) +project = edi.Project.load(project_dir) + +# %% [markdown] +# Re-save the project to a fresh working directory so resuming the +# chain below writes there instead of the bundled read-only copy. + +# %% +project.save_as(dir_path='projects/bayesian-emcee-resume-lbco-hrpt') # %% [markdown] # ## 📊 Inspect Results @@ -148,4 +154,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_26_lbco_hrpt_emcee') +project.save_as(dir_path='projects/bayesian-emcee-resume-lbco-hrpt') diff --git a/docs/docs/tutorials/ed-22.ipynb b/docs/docs/tutorials/bayesian-emcee-tbti-heidi.ipynb similarity index 97% rename from docs/docs/tutorials/ed-22.ipynb rename to docs/docs/tutorials/bayesian-emcee-tbti-heidi.ipynb index d7f559090..24114daea 100644 --- a/docs/docs/tutorials/ed-22.ipynb +++ b/docs/docs/tutorials/bayesian-emcee-tbti-heidi.ipynb @@ -62,7 +62,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -84,7 +84,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='tbti_heidi_emcee')" + "project = edi.Project(name='tbti_heidi_emcee')" ] }, { @@ -94,7 +94,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_22_tbti_heidi_emcee')" + "project.save_as(dir_path='projects/bayesian-emcee-tbti-heidi')" ] }, { @@ -117,7 +117,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure_path = ed.download_data(id=20, destination='data')" + "structure_path = edi.download_data('struct-tbti', destination='data')" ] }, { @@ -178,7 +178,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=19, destination='data')" + "data_path = edi.download_data('meas-tbti-heidi', destination='data')" ] }, { @@ -222,8 +222,8 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_crystal.id = 'tbti'\n", - "experiment.linked_crystal.scale = 1.0" + "experiment.linked_structure.structure_id = 'tbti'\n", + "experiment.linked_structure.scale = 1.0" ] }, { @@ -302,7 +302,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_crystal.scale.free = True\n", + "experiment.linked_structure.scale.free = True\n", "experiment.extinction.radius.free = True" ] }, diff --git a/docs/docs/tutorials/ed-22.py b/docs/docs/tutorials/bayesian-emcee-tbti-heidi.py similarity index 94% rename from docs/docs/tutorials/ed-22.py rename to docs/docs/tutorials/bayesian-emcee-tbti-heidi.py index d9dcad9ca..d08514a3f 100644 --- a/docs/docs/tutorials/ed-22.py +++ b/docs/docs/tutorials/bayesian-emcee-tbti-heidi.py @@ -25,7 +25,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -35,10 +35,10 @@ # workflow inside this object. # %% -project = ed.Project(name='tbti_heidi_emcee') +project = edi.Project(name='tbti_heidi_emcee') # %% -project.save_as(dir_path='projects/ed_22_tbti_heidi_emcee') +project.save_as(dir_path='projects/bayesian-emcee-tbti-heidi') # %% [markdown] # ## 🧩 Define Structure @@ -49,7 +49,7 @@ # model without rebuilding the full structure by hand. # %% -structure_path = ed.download_data(id=20, destination='data') +structure_path = edi.download_data('struct-tbti', destination='data') # %% project.structures.add_from_cif_path(structure_path) @@ -72,7 +72,7 @@ # wavelength, and extinction model. # %% -data_path = ed.download_data(id=19, destination='data') +data_path = edi.download_data('meas-tbti-heidi', destination='data') # %% project.experiments.add_from_data_path( @@ -90,8 +90,8 @@ # Link the crystal structure to the experiment and set its scale factor. # %% -experiment.linked_crystal.id = 'tbti' -experiment.linked_crystal.scale = 1.0 +experiment.linked_structure.structure_id = 'tbti' +experiment.linked_structure.scale = 1.0 # %% [markdown] # Set the instrument wavelength and starting extinction parameters. @@ -132,7 +132,7 @@ structure.atom_sites['O2'].adp_iso.free = True # %% -experiment.linked_crystal.scale.free = True +experiment.linked_structure.scale.free = True experiment.extinction.radius.free = True # %% [markdown] diff --git a/docs/docs/tutorials/ed-20.ipynb b/docs/docs/tutorials/calibrate-beer-ess.ipynb similarity index 90% rename from docs/docs/tutorials/ed-20.ipynb rename to docs/docs/tutorials/calibrate-beer-ess.ipynb index 7fd3b3725..2ff35fccf 100644 --- a/docs/docs/tutorials/ed-20.ipynb +++ b/docs/docs/tutorials/calibrate-beer-ess.ipynb @@ -80,12 +80,12 @@ "ferrite = StructureFactory.from_scratch(name='ferrite')\n", "\n", "ferrite.space_group.name_h_m = 'I m -3 m'\n", - "ferrite.space_group.it_coordinate_system_code = '1'\n", + "ferrite.space_group.coord_system_code = '1'\n", "\n", "ferrite.cell.length_a = 2.886\n", "\n", "ferrite.atom_sites.create(\n", - " label='Fe',\n", + " id='Fe',\n", " type_symbol='Fe',\n", " fract_x=0.0,\n", " fract_y=0.0,\n", @@ -113,12 +113,12 @@ "austenite = StructureFactory.from_scratch(name='austenite')\n", "\n", "austenite.space_group.name_h_m = 'F m -3 m'\n", - "austenite.space_group.it_coordinate_system_code = '1'\n", + "austenite.space_group.coord_system_code = '1'\n", "\n", "austenite.cell.length_a = 3.6468\n", "\n", "austenite.atom_sites.create(\n", - " label='Fe',\n", + " id='Fe',\n", " type_symbol='Fe',\n", " fract_x=0.0,\n", " fract_y=0.0,\n", @@ -148,8 +148,8 @@ "metadata": {}, "outputs": [], "source": [ - "zip_path = download_data(id=33, destination='data')\n", - "data_paths = extract_data_paths_from_zip(zip_path, destination='data/ed-20')\n", + "zip_path = download_data('meas-ferrite-austenite-beer', destination='data')\n", + "data_paths = extract_data_paths_from_zip(zip_path, destination='data/calibrate-beer-ess')\n", "\n", "data_path_s2 = data_paths[1] # 'Duplex_in_HR_for_IRF_S2.dat'\n", "data_path_n2 = data_paths[0] # 'Duplex_in_HR_for_IRF_N2.dat'" @@ -337,7 +337,9 @@ "outputs": [], "source": [ "for point in expt_s2.background:\n", - " expt_n2.background.create(id=point.id.value, x=point.x.value, y=point.y.value)" + " expt_n2.background.create(\n", + " id=point.id.value, position=point.position.value, intensity=point.intensity.value\n", + " )" ] }, { @@ -345,7 +347,7 @@ "id": "27", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -355,8 +357,8 @@ "metadata": {}, "outputs": [], "source": [ - "expt_s2.linked_phases.create(id='ferrite', scale=10)\n", - "expt_s2.linked_phases.create(id='austenite', scale=10)" + "expt_s2.linked_structures.create(structure_id='ferrite', scale=10)\n", + "expt_s2.linked_structures.create(structure_id='austenite', scale=10)" ] }, { @@ -366,8 +368,8 @@ "metadata": {}, "outputs": [], "source": [ - "expt_n2.linked_phases.create(id='ferrite', scale=10)\n", - "expt_n2.linked_phases.create(id='austenite', scale=10)" + "expt_n2.linked_structures.create(structure_id='ferrite', scale=10)\n", + "expt_n2.linked_structures.create(structure_id='austenite', scale=10)" ] }, { @@ -421,7 +423,7 @@ "outputs": [], "source": [ "project = Project(name='beer_mcstas')\n", - "project.save_as(dir_path='projects/ed_20_beer_mcstas')" + "project.save_as(dir_path='projects/calibrate-beer-ess')" ] }, { @@ -578,8 +580,8 @@ "metadata": {}, "outputs": [], "source": [ - "expt_s2.linked_phases['ferrite'].scale.free = True\n", - "expt_s2.linked_phases['austenite'].scale.free = True\n", + "expt_s2.linked_structures['ferrite'].scale.free = True\n", + "expt_s2.linked_structures['austenite'].scale.free = True\n", "\n", "expt_s2.peak.broad_gauss_sigma_0.free = True\n", "expt_s2.peak.broad_gauss_sigma_1.free = True\n", @@ -589,7 +591,7 @@ "expt_s2.instrument.calib_d_to_tof_offset.free = True\n", "\n", "for segment in expt_s2.background:\n", - " segment.y.free = True" + " segment.intensity.free = True" ] }, { @@ -599,8 +601,8 @@ "metadata": {}, "outputs": [], "source": [ - "expt_n2.linked_phases['ferrite'].scale.free = True\n", - "expt_n2.linked_phases['austenite'].scale.free = True\n", + "expt_n2.linked_structures['ferrite'].scale.free = True\n", + "expt_n2.linked_structures['austenite'].scale.free = True\n", "\n", "expt_n2.peak.broad_gauss_sigma_0.free = True\n", "expt_n2.peak.broad_gauss_sigma_1.free = True\n", @@ -610,7 +612,7 @@ "expt_n2.instrument.calib_d_to_tof_offset.free = True\n", "\n", "for segment in expt_n2.background:\n", - " segment.y.free = True" + " segment.intensity.free = True" ] }, { @@ -629,17 +631,17 @@ "outputs": [], "source": [ "project.analysis.aliases.create(\n", - " label='s2_ferrite_scale', param=expt_s2.linked_phases['ferrite'].scale\n", + " id='s2_ferrite_scale', param=expt_s2.linked_structures['ferrite'].scale\n", ")\n", "project.analysis.aliases.create(\n", - " label='s2_austenite_scale', param=expt_s2.linked_phases['austenite'].scale\n", + " id='s2_austenite_scale', param=expt_s2.linked_structures['austenite'].scale\n", ")\n", "\n", "project.analysis.aliases.create(\n", - " label='n2_ferrite_scale', param=expt_n2.linked_phases['ferrite'].scale\n", + " id='n2_ferrite_scale', param=expt_n2.linked_structures['ferrite'].scale\n", ")\n", "project.analysis.aliases.create(\n", - " label='n2_austenite_scale', param=expt_n2.linked_phases['austenite'].scale\n", + " id='n2_austenite_scale', param=expt_n2.linked_structures['austenite'].scale\n", ")\n", "\n", "project.analysis.constraints.create(expression='n2_ferrite_scale = s2_ferrite_scale')\n", @@ -682,9 +684,9 @@ "outputs": [], "source": [ "for segment in expt_s2.background:\n", - " segment.y.free = False\n", + " segment.intensity.free = False\n", "for segment in expt_n2.background:\n", - " segment.y.free = False" + " segment.intensity.free = False" ] }, { diff --git a/docs/docs/tutorials/ed-20.py b/docs/docs/tutorials/calibrate-beer-ess.py similarity index 82% rename from docs/docs/tutorials/ed-20.py rename to docs/docs/tutorials/calibrate-beer-ess.py index ef8e77eb0..800c86ed0 100644 --- a/docs/docs/tutorials/ed-20.py +++ b/docs/docs/tutorials/calibrate-beer-ess.py @@ -31,12 +31,12 @@ ferrite = StructureFactory.from_scratch(name='ferrite') ferrite.space_group.name_h_m = 'I m -3 m' -ferrite.space_group.it_coordinate_system_code = '1' +ferrite.space_group.coord_system_code = '1' ferrite.cell.length_a = 2.886 ferrite.atom_sites.create( - label='Fe', + id='Fe', type_symbol='Fe', fract_x=0.0, fract_y=0.0, @@ -52,12 +52,12 @@ austenite = StructureFactory.from_scratch(name='austenite') austenite.space_group.name_h_m = 'F m -3 m' -austenite.space_group.it_coordinate_system_code = '1' +austenite.space_group.coord_system_code = '1' austenite.cell.length_a = 3.6468 austenite.atom_sites.create( - label='Fe', + id='Fe', type_symbol='Fe', fract_x=0.0, fract_y=0.0, @@ -75,8 +75,8 @@ # ### Download Data # %% -zip_path = download_data(id=33, destination='data') -data_paths = extract_data_paths_from_zip(zip_path, destination='data/ed-20') +zip_path = download_data('meas-ferrite-austenite-beer', destination='data') +data_paths = extract_data_paths_from_zip(zip_path, destination='data/calibrate-beer-ess') data_path_s2 = data_paths[1] # 'Duplex_in_HR_for_IRF_S2.dat' data_path_n2 = data_paths[0] # 'Duplex_in_HR_for_IRF_N2.dat' @@ -153,18 +153,20 @@ # %% for point in expt_s2.background: - expt_n2.background.create(id=point.id.value, x=point.x.value, y=point.y.value) + expt_n2.background.create( + id=point.id.value, position=point.position.value, intensity=point.intensity.value + ) # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -expt_s2.linked_phases.create(id='ferrite', scale=10) -expt_s2.linked_phases.create(id='austenite', scale=10) +expt_s2.linked_structures.create(structure_id='ferrite', scale=10) +expt_s2.linked_structures.create(structure_id='austenite', scale=10) # %% -expt_n2.linked_phases.create(id='ferrite', scale=10) -expt_n2.linked_phases.create(id='austenite', scale=10) +expt_n2.linked_structures.create(structure_id='ferrite', scale=10) +expt_n2.linked_structures.create(structure_id='austenite', scale=10) # %% [markdown] # ### Set Excluded Regions @@ -187,7 +189,7 @@ # %% project = Project(name='beer_mcstas') -project.save_as(dir_path='projects/ed_20_beer_mcstas') +project.save_as(dir_path='projects/calibrate-beer-ess') # %% [markdown] # ### Add Structures @@ -244,8 +246,8 @@ austenite.atom_sites['Fe'].adp_iso.free = True # %% -expt_s2.linked_phases['ferrite'].scale.free = True -expt_s2.linked_phases['austenite'].scale.free = True +expt_s2.linked_structures['ferrite'].scale.free = True +expt_s2.linked_structures['austenite'].scale.free = True expt_s2.peak.broad_gauss_sigma_0.free = True expt_s2.peak.broad_gauss_sigma_1.free = True @@ -255,11 +257,11 @@ expt_s2.instrument.calib_d_to_tof_offset.free = True for segment in expt_s2.background: - segment.y.free = True + segment.intensity.free = True # %% -expt_n2.linked_phases['ferrite'].scale.free = True -expt_n2.linked_phases['austenite'].scale.free = True +expt_n2.linked_structures['ferrite'].scale.free = True +expt_n2.linked_structures['austenite'].scale.free = True expt_n2.peak.broad_gauss_sigma_0.free = True expt_n2.peak.broad_gauss_sigma_1.free = True @@ -269,24 +271,24 @@ expt_n2.instrument.calib_d_to_tof_offset.free = True for segment in expt_n2.background: - segment.y.free = True + segment.intensity.free = True # %% [markdown] # ### Add Constraints # %% project.analysis.aliases.create( - label='s2_ferrite_scale', param=expt_s2.linked_phases['ferrite'].scale + id='s2_ferrite_scale', param=expt_s2.linked_structures['ferrite'].scale ) project.analysis.aliases.create( - label='s2_austenite_scale', param=expt_s2.linked_phases['austenite'].scale + id='s2_austenite_scale', param=expt_s2.linked_structures['austenite'].scale ) project.analysis.aliases.create( - label='n2_ferrite_scale', param=expt_n2.linked_phases['ferrite'].scale + id='n2_ferrite_scale', param=expt_n2.linked_structures['ferrite'].scale ) project.analysis.aliases.create( - label='n2_austenite_scale', param=expt_n2.linked_phases['austenite'].scale + id='n2_austenite_scale', param=expt_n2.linked_structures['austenite'].scale ) project.analysis.constraints.create(expression='n2_ferrite_scale = s2_ferrite_scale') @@ -305,9 +307,9 @@ # %% for segment in expt_s2.background: - segment.y.free = False + segment.intensity.free = False for segment in expt_n2.background: - segment.y.free = False + segment.intensity.free = False # %% project.analysis.fit() diff --git a/docs/docs/tutorials/ed-13.ipynb b/docs/docs/tutorials/fitting-exercise-si-lbco.ipynb similarity index 94% rename from docs/docs/tutorials/ed-13.ipynb rename to docs/docs/tutorials/fitting-exercise-si-lbco.ipynb index 4a13675e3..7ee95ed1d 100644 --- a/docs/docs/tutorials/ed-13.ipynb +++ b/docs/docs/tutorials/fitting-exercise-si-lbco.ipynb @@ -71,7 +71,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -119,7 +119,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_1 = ed.Project(name='reference')" + "project_1 = edi.Project(name='reference')" ] }, { @@ -140,8 +140,8 @@ "metadata": {}, "outputs": [], "source": [ - "project_1.info.title = 'Reference Silicon Fit'\n", - "project_1.info.description = 'Fitting simulated powder diffraction pattern of Si.'" + "project_1.metadata.title = 'Reference Silicon Fit'\n", + "project_1.metadata.description = 'Fitting simulated powder diffraction pattern of Si.'" ] }, { @@ -200,7 +200,7 @@ "metadata": {}, "outputs": [], "source": [ - "si_xye_path = ed.download_data(id=17, destination=data_dir)" + "si_xye_path = edi.download_data('meas-si-mcstas-dmsc2025', destination=data_dir)" ] }, { @@ -384,10 +384,10 @@ "metadata": {}, "outputs": [], "source": [ - "project_1.experiments['sim_si'].instrument.setup_twotheta_bank = ed.extract_metadata(\n", + "project_1.experiments['sim_si'].instrument.setup_twotheta_bank = edi.extract_metadata(\n", " si_xye_path, r'two_theta\\s*=\\s*(\\d*\\.?\\d+)'\n", ")\n", - "project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = ed.extract_metadata(\n", + "project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = edi.extract_metadata(\n", " si_xye_path, r'DIFC\\s*=\\s*(\\d*\\.?\\d+)'\n", ")" ] @@ -551,10 +551,10 @@ "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.exp_decay_beta_0 = 0.0019\n", - "project_1.experiments['sim_si'].peak.exp_decay_beta_1 = 0.0137\n", - "project_1.experiments['sim_si'].peak.exp_rise_alpha_0 = -0.0055\n", - "project_1.experiments['sim_si'].peak.exp_rise_alpha_1 = 0.0147" + "project_1.experiments['sim_si'].peak.decay_beta_0 = 0.0019\n", + "project_1.experiments['sim_si'].peak.decay_beta_1 = 0.0137\n", + "project_1.experiments['sim_si'].peak.rise_alpha_0 = -0.0055\n", + "project_1.experiments['sim_si'].peak.rise_alpha_1 = 0.0147" ] }, { @@ -620,13 +620,13 @@ "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)" + "project_1.experiments['sim_si'].background.create(id='1', position=50000, intensity=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='2', position=60000, intensity=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='3', position=70000, intensity=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='4', position=80000, intensity=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='5', position=90000, intensity=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='6', position=100000, intensity=0.01)\n", + "project_1.experiments['sim_si'].background.create(id='7', position=110000, intensity=0.01)" ] }, { @@ -779,7 +779,7 @@ "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 = '1'" + "project_1.structures['si'].space_group.coord_system_code = '1'" ] }, { @@ -836,7 +836,7 @@ "outputs": [], "source": [ "project_1.structures['si'].atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0.0,\n", " fract_y=0.0,\n", @@ -886,7 +886,7 @@ "metadata": {}, "source": [ "📖 See\n", - "[documentation](https://docs.easydiffraction.org/lib/latest/user-guide/analysis-workflow/experiment/#linked-phases-category)\n", + "[documentation](https://docs.easydiffraction.org/lib/latest/user-guide/analysis-workflow/experiment/#linked-structures-category)\n", "for more details about linking a structure to an experiment." ] }, @@ -897,7 +897,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_1.experiments['sim_si'].linked_phases.create(id='si', scale=1.0)" + "project_1.experiments['sim_si'].linked_structures.create(structure_id='si', scale=1.0)" ] }, { @@ -978,18 +978,18 @@ "metadata": {}, "outputs": [], "source": [ - "project_1.experiments['sim_si'].linked_phases['si'].scale.free = True\n", + "project_1.experiments['sim_si'].linked_structures['si'].scale.free = True\n", "\n", "for line_segment in project_1.experiments['sim_si'].background:\n", - " line_segment.y.free = True\n", + " line_segment.intensity.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.exp_decay_beta_0.free = True\n", - "project_1.experiments['sim_si'].peak.exp_decay_beta_1.free = True\n", - "project_1.experiments['sim_si'].peak.exp_rise_alpha_0.free = True\n", - "project_1.experiments['sim_si'].peak.exp_rise_alpha_1.free = True" + "project_1.experiments['sim_si'].peak.decay_beta_0.free = True\n", + "project_1.experiments['sim_si'].peak.decay_beta_1.free = True\n", + "project_1.experiments['sim_si'].peak.rise_alpha_0.free = True\n", + "project_1.experiments['sim_si'].peak.rise_alpha_1.free = True" ] }, { @@ -1200,7 +1200,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_1.save_as(dir_path='projects/ed_13_reference')" + "project_1.save_as(dir_path='projects/fitting-exercise-si-lbco-reference')" ] }, { @@ -1256,9 +1256,9 @@ "metadata": {}, "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.'" + "project_2 = edi.Project(name='main')\n", + "project_2.metadata.title = 'La0.5Ba0.5CoO3 Fit'\n", + "project_2.metadata.description = 'Fitting simulated powder diffraction pattern of La0.5Ba0.5CoO3.'" ] }, { @@ -1312,7 +1312,7 @@ "\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", + "lbco_xye_path = edi.download_data('meas-lbco-si-mcstas-dmsc2025', destination=data_dir)\n", "\n", "project_2.experiments.add_from_data_path(\n", " name='sim_lbco',\n", @@ -1419,10 +1419,10 @@ "metadata": {}, "outputs": [], "source": [ - "project_2.experiments['sim_lbco'].instrument.setup_twotheta_bank = ed.extract_metadata(\n", + "project_2.experiments['sim_lbco'].instrument.setup_twotheta_bank = edi.extract_metadata(\n", " lbco_xye_path, r'two_theta\\s*=\\s*(\\d*\\.?\\d+)'\n", ")\n", - "project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = ed.extract_metadata(\n", + "project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = edi.extract_metadata(\n", " lbco_xye_path, r'DIFC\\s*=\\s*(\\d*\\.?\\d+)'\n", ")" ] @@ -1479,10 +1479,10 @@ "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.exp_decay_beta_0 = sim_si_peak.exp_decay_beta_0.value\n", - "project_2.experiments['sim_lbco'].peak.exp_decay_beta_1 = sim_si_peak.exp_decay_beta_1.value\n", - "project_2.experiments['sim_lbco'].peak.exp_rise_alpha_0 = sim_si_peak.exp_rise_alpha_0.value\n", - "project_2.experiments['sim_lbco'].peak.exp_rise_alpha_1 = sim_si_peak.exp_rise_alpha_1.value" + "project_2.experiments['sim_lbco'].peak.decay_beta_0 = sim_si_peak.decay_beta_0.value\n", + "project_2.experiments['sim_lbco'].peak.decay_beta_1 = sim_si_peak.decay_beta_1.value\n", + "project_2.experiments['sim_lbco'].peak.rise_alpha_0 = sim_si_peak.rise_alpha_0.value\n", + "project_2.experiments['sim_lbco'].peak.rise_alpha_1 = sim_si_peak.rise_alpha_1.value" ] }, { @@ -1530,13 +1530,13 @@ "metadata": {}, "outputs": [], "source": [ - "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)" + "project_2.experiments['sim_lbco'].background.create(id='1', position=50000, intensity=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='2', position=60000, intensity=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='3', position=70000, intensity=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='4', position=80000, intensity=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='5', position=90000, intensity=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='6', position=100000, intensity=0.2)\n", + "project_2.experiments['sim_lbco'].background.create(id='7', position=110000, intensity=0.2)" ] }, { @@ -1709,7 +1709,7 @@ "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'" + "project_2.structures['lbco'].space_group.coord_system_code = '1'" ] }, { @@ -1799,7 +1799,7 @@ "outputs": [], "source": [ "project_2.structures['lbco'].atom_sites.create(\n", - " label='La',\n", + " id='La',\n", " type_symbol='La',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -1809,7 +1809,7 @@ " adp_iso=0.95,\n", ")\n", "project_2.structures['lbco'].atom_sites.create(\n", - " label='Ba',\n", + " id='Ba',\n", " type_symbol='Ba',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -1819,7 +1819,7 @@ " adp_iso=0.95,\n", ")\n", "project_2.structures['lbco'].atom_sites.create(\n", - " label='Co',\n", + " id='Co',\n", " type_symbol='Co',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -1828,7 +1828,7 @@ " adp_iso=0.80,\n", ")\n", "project_2.structures['lbco'].atom_sites.create(\n", - " label='O',\n", + " id='O',\n", " type_symbol='O',\n", " fract_x=0,\n", " fract_y=0.5,\n", @@ -1882,7 +1882,7 @@ "id": "140", "metadata": {}, "source": [ - "Use the `linked_phases` attribute of the experiment to link the\n", + "Use the `linked_structures` attribute of the experiment to link the\n", "crystal structure." ] }, @@ -1901,7 +1901,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_2.experiments['sim_lbco'].linked_phases.create(id='lbco', scale=1.0)" + "project_2.experiments['sim_lbco'].linked_structures.create(structure_id='lbco', scale=1.0)" ] }, { @@ -1949,10 +1949,10 @@ "metadata": {}, "outputs": [], "source": [ - "project_2.experiments['sim_lbco'].linked_phases['lbco'].scale.free = True\n", + "project_2.experiments['sim_lbco'].linked_structures['lbco'].scale.free = True\n", "\n", "for line_segment in project_2.experiments['sim_lbco'].background:\n", - " line_segment.y.free = True" + " line_segment.intensity.free = True" ] }, { @@ -2275,10 +2275,10 @@ "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.exp_decay_beta_0.free = True\n", - "project_2.experiments['sim_lbco'].peak.exp_decay_beta_1.free = True\n", - "project_2.experiments['sim_lbco'].peak.exp_rise_alpha_0.free = True\n", - "project_2.experiments['sim_lbco'].peak.exp_rise_alpha_1.free = True\n", + "project_2.experiments['sim_lbco'].peak.decay_beta_0.free = True\n", + "project_2.experiments['sim_lbco'].peak.decay_beta_1.free = True\n", + "project_2.experiments['sim_lbco'].peak.rise_alpha_0.free = True\n", + "project_2.experiments['sim_lbco'].peak.rise_alpha_1.free = True\n", "\n", "project_2.analysis.fit()\n", "project_2.display.fit.results()\n", @@ -2507,14 +2507,14 @@ "# 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", + "project_2.structures['si'].space_group.coord_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", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0.125,\n", " fract_y=0.125,\n", @@ -2523,7 +2523,7 @@ ")\n", "\n", "# Assign Structure to Experiment\n", - "project_2.experiments['sim_lbco'].linked_phases.create(id='si', scale=1.0)" + "project_2.experiments['sim_lbco'].linked_structures.create(structure_id='si', scale=1.0)" ] }, { @@ -2582,7 +2582,7 @@ "# 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", + "project_2.experiments['sim_lbco'].linked_structures['si'].scale.free = True\n", "\n", "# Now we can perform the fit with both phases included.\n", "project_2.analysis.fit()\n", @@ -2635,7 +2635,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_2.save_as(dir_path='projects/ed_13_main')" + "project_2.save_as(dir_path='projects/fitting-exercise-si-lbco-main')" ] }, { diff --git a/docs/docs/tutorials/ed-13.py b/docs/docs/tutorials/fitting-exercise-si-lbco.py similarity index 92% rename from docs/docs/tutorials/ed-13.py rename to docs/docs/tutorials/fitting-exercise-si-lbco.py index d30af7865..9c8994f5d 100644 --- a/docs/docs/tutorials/ed-13.py +++ b/docs/docs/tutorials/fitting-exercise-si-lbco.py @@ -34,7 +34,7 @@ # components. # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📘 Introduction: Simple Reference Fit – Si @@ -65,7 +65,7 @@ # analysis workflow. # %% -project_1 = ed.Project(name='reference') +project_1 = edi.Project(name='reference') # %% [markdown] # You can set the title and description of the project to provide @@ -74,8 +74,8 @@ # future) understand the purpose of the project at a glance. # %% -project_1.info.title = 'Reference Silicon Fit' -project_1.info.description = 'Fitting simulated powder diffraction pattern of Si.' +project_1.metadata.title = 'Reference Silicon Fit' +project_1.metadata.description = 'Fitting simulated powder diffraction pattern of Si.' # %% [markdown] # ### 🔬 Create an Experiment @@ -105,7 +105,7 @@ # file is already present. # %% -si_xye_path = ed.download_data(id=17, destination=data_dir) +si_xye_path = edi.download_data('meas-si-mcstas-dmsc2025', destination=data_dir) # %% [markdown] # Now we can create the experiment and load the measured data. In this @@ -209,10 +209,10 @@ # for more details about the instrument parameters. # %% -project_1.experiments['sim_si'].instrument.setup_twotheta_bank = ed.extract_metadata( +project_1.experiments['sim_si'].instrument.setup_twotheta_bank = edi.extract_metadata( si_xye_path, r'two_theta\s*=\s*(\d*\.?\d+)' ) -project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = ed.extract_metadata( +project_1.experiments['sim_si'].instrument.calib_d_to_tof_linear = edi.extract_metadata( si_xye_path, r'DIFC\s*=\s*(\d*\.?\d+)' ) @@ -318,10 +318,10 @@ project_1.experiments['sim_si'].peak.broad_gauss_sigma_0 = 69498 project_1.experiments['sim_si'].peak.broad_gauss_sigma_1 = -55578 project_1.experiments['sim_si'].peak.broad_gauss_sigma_2 = 14560 -project_1.experiments['sim_si'].peak.exp_decay_beta_0 = 0.0019 -project_1.experiments['sim_si'].peak.exp_decay_beta_1 = 0.0137 -project_1.experiments['sim_si'].peak.exp_rise_alpha_0 = -0.0055 -project_1.experiments['sim_si'].peak.exp_rise_alpha_1 = 0.0147 +project_1.experiments['sim_si'].peak.decay_beta_0 = 0.0019 +project_1.experiments['sim_si'].peak.decay_beta_1 = 0.0137 +project_1.experiments['sim_si'].peak.rise_alpha_0 = -0.0055 +project_1.experiments['sim_si'].peak.rise_alpha_1 = 0.0147 # %% [markdown] # #### Set Background @@ -363,13 +363,13 @@ # %% project_1.experiments['sim_si'].background.type = 'line-segment' -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) +project_1.experiments['sim_si'].background.create(id='1', position=50000, intensity=0.01) +project_1.experiments['sim_si'].background.create(id='2', position=60000, intensity=0.01) +project_1.experiments['sim_si'].background.create(id='3', position=70000, intensity=0.01) +project_1.experiments['sim_si'].background.create(id='4', position=80000, intensity=0.01) +project_1.experiments['sim_si'].background.create(id='5', position=90000, intensity=0.01) +project_1.experiments['sim_si'].background.create(id='6', position=100000, intensity=0.01) +project_1.experiments['sim_si'].background.create(id='7', position=110000, intensity=0.01) # %% [markdown] # ### 🧩 Create a Structure – Si @@ -468,7 +468,7 @@ # %% project_1.structures['si'].space_group.name_h_m = 'F d -3 m' -project_1.structures['si'].space_group.it_coordinate_system_code = '1' +project_1.structures['si'].space_group.coord_system_code = '1' # %% [markdown] # #### Set Unit Cell @@ -491,7 +491,7 @@ # %% project_1.structures['si'].atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.0, fract_y=0.0, @@ -519,11 +519,11 @@ # %% [markdown] tags=["doc-link"] # 📖 See -# [documentation](https://docs.easydiffraction.org/lib/latest/user-guide/analysis-workflow/experiment/#linked-phases-category) +# [documentation](https://docs.easydiffraction.org/lib/latest/user-guide/analysis-workflow/experiment/#linked-structures-category) # for more details about linking a structure to an experiment. # %% -project_1.experiments['sim_si'].linked_phases.create(id='si', scale=1.0) +project_1.experiments['sim_si'].linked_structures.create(structure_id='si', scale=1.0) # %% [markdown] # ### 🚀 Analyze and Fit the Data @@ -575,18 +575,18 @@ # sample is considered a reference sample with known parameters. # %% -project_1.experiments['sim_si'].linked_phases['si'].scale.free = True +project_1.experiments['sim_si'].linked_structures['si'].scale.free = True for line_segment in project_1.experiments['sim_si'].background: - line_segment.y.free = True + line_segment.intensity.free = True project_1.experiments['sim_si'].peak.broad_gauss_sigma_0.free = True project_1.experiments['sim_si'].peak.broad_gauss_sigma_1.free = True project_1.experiments['sim_si'].peak.broad_gauss_sigma_2.free = True -project_1.experiments['sim_si'].peak.exp_decay_beta_0.free = True -project_1.experiments['sim_si'].peak.exp_decay_beta_1.free = True -project_1.experiments['sim_si'].peak.exp_rise_alpha_0.free = True -project_1.experiments['sim_si'].peak.exp_rise_alpha_1.free = True +project_1.experiments['sim_si'].peak.decay_beta_0.free = True +project_1.experiments['sim_si'].peak.decay_beta_1.free = True +project_1.experiments['sim_si'].peak.rise_alpha_0.free = True +project_1.experiments['sim_si'].peak.rise_alpha_1.free = True # %% [markdown] # #### Display Free Parameters @@ -710,7 +710,7 @@ # directory specified by the `dir_path` attribute of the project object. # %% -project_1.save_as(dir_path='projects/ed_13_reference') +project_1.save_as(dir_path='projects/fitting-exercise-si-lbco-reference') # %% [markdown] # ## 💪 Exercise: Complex Fit – LBCO @@ -739,9 +739,9 @@ # **Solution:** # %% tags=["solution", "hide-input"] -project_2 = ed.Project(name='main') -project_2.info.title = 'La0.5Ba0.5CoO3 Fit' -project_2.info.description = 'Fitting simulated powder diffraction pattern of La0.5Ba0.5CoO3.' +project_2 = edi.Project(name='main') +project_2.metadata.title = 'La0.5Ba0.5CoO3 Fit' +project_2.metadata.description = 'Fitting simulated powder diffraction pattern of La0.5Ba0.5CoO3.' # %% [markdown] # ### 🔬 Exercise 2: Define an Experiment @@ -768,7 +768,7 @@ # Uncomment the following line if your data reduction failed and the # reduced data file is missing. -lbco_xye_path = ed.download_data(id=18, destination=data_dir) +lbco_xye_path = edi.download_data('meas-lbco-si-mcstas-dmsc2025', destination=data_dir) project_2.experiments.add_from_data_path( name='sim_lbco', @@ -821,10 +821,10 @@ # **Solution:** # %% tags=["solution", "hide-input"] -project_2.experiments['sim_lbco'].instrument.setup_twotheta_bank = ed.extract_metadata( +project_2.experiments['sim_lbco'].instrument.setup_twotheta_bank = edi.extract_metadata( lbco_xye_path, r'two_theta\s*=\s*(\d*\.?\d+)' ) -project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = ed.extract_metadata( +project_2.experiments['sim_lbco'].instrument.calib_d_to_tof_linear = edi.extract_metadata( lbco_xye_path, r'DIFC\s*=\s*(\d*\.?\d+)' ) @@ -854,10 +854,10 @@ project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_0 = sim_si_peak.broad_gauss_sigma_0.value project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_1 = sim_si_peak.broad_gauss_sigma_1.value project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_2 = sim_si_peak.broad_gauss_sigma_2.value -project_2.experiments['sim_lbco'].peak.exp_decay_beta_0 = sim_si_peak.exp_decay_beta_0.value -project_2.experiments['sim_lbco'].peak.exp_decay_beta_1 = sim_si_peak.exp_decay_beta_1.value -project_2.experiments['sim_lbco'].peak.exp_rise_alpha_0 = sim_si_peak.exp_rise_alpha_0.value -project_2.experiments['sim_lbco'].peak.exp_rise_alpha_1 = sim_si_peak.exp_rise_alpha_1.value +project_2.experiments['sim_lbco'].peak.decay_beta_0 = sim_si_peak.decay_beta_0.value +project_2.experiments['sim_lbco'].peak.decay_beta_1 = sim_si_peak.decay_beta_1.value +project_2.experiments['sim_lbco'].peak.rise_alpha_0 = sim_si_peak.rise_alpha_0.value +project_2.experiments['sim_lbco'].peak.rise_alpha_1 = sim_si_peak.rise_alpha_1.value # %% [markdown] # #### Exercise 2.4: Set Background @@ -878,13 +878,13 @@ # **Solution:** # %% tags=["solution", "hide-input"] -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) +project_2.experiments['sim_lbco'].background.create(id='1', position=50000, intensity=0.2) +project_2.experiments['sim_lbco'].background.create(id='2', position=60000, intensity=0.2) +project_2.experiments['sim_lbco'].background.create(id='3', position=70000, intensity=0.2) +project_2.experiments['sim_lbco'].background.create(id='4', position=80000, intensity=0.2) +project_2.experiments['sim_lbco'].background.create(id='5', position=90000, intensity=0.2) +project_2.experiments['sim_lbco'].background.create(id='6', position=100000, intensity=0.2) +project_2.experiments['sim_lbco'].background.create(id='7', position=110000, intensity=0.2) # %% [markdown] # ### 🧩 Exercise 3: Define a Structure – LBCO @@ -988,7 +988,7 @@ # %% tags=["solution", "hide-input"] project_2.structures['lbco'].space_group.name_h_m = 'P m -3 m' -project_2.structures['lbco'].space_group.it_coordinate_system_code = '1' +project_2.structures['lbco'].space_group.coord_system_code = '1' # %% [markdown] # #### Exercise 3.3: Set Unit Cell @@ -1024,7 +1024,7 @@ # %% tags=["solution", "hide-input"] project_2.structures['lbco'].atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -1034,7 +1034,7 @@ adp_iso=0.95, ) project_2.structures['lbco'].atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -1044,7 +1044,7 @@ adp_iso=0.95, ) project_2.structures['lbco'].atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -1053,7 +1053,7 @@ adp_iso=0.80, ) project_2.structures['lbco'].atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -1080,14 +1080,14 @@ # **Hint:** # %% [markdown] tags=["dmsc-school-hint"] -# Use the `linked_phases` attribute of the experiment to link the +# Use the `linked_structures` attribute of the experiment to link the # crystal structure. # %% [markdown] # **Solution:** # %% tags=["solution", "hide-input"] -project_2.experiments['sim_lbco'].linked_phases.create(id='lbco', scale=1.0) +project_2.experiments['sim_lbco'].linked_structures.create(structure_id='lbco', scale=1.0) # %% [markdown] # ### 🚀 Exercise 5: Analyze and Fit the Data @@ -1108,10 +1108,10 @@ # **Solution:** # %% tags=["solution", "hide-input"] -project_2.experiments['sim_lbco'].linked_phases['lbco'].scale.free = True +project_2.experiments['sim_lbco'].linked_structures['lbco'].scale.free = True for line_segment in project_2.experiments['sim_lbco'].background: - line_segment.y.free = True + line_segment.intensity.free = True # %% [markdown] # #### Exercise 5.2: Run Fitting @@ -1277,10 +1277,10 @@ project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_0.free = True project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_1.free = True project_2.experiments['sim_lbco'].peak.broad_gauss_sigma_2.free = True -project_2.experiments['sim_lbco'].peak.exp_decay_beta_0.free = True -project_2.experiments['sim_lbco'].peak.exp_decay_beta_1.free = True -project_2.experiments['sim_lbco'].peak.exp_rise_alpha_0.free = True -project_2.experiments['sim_lbco'].peak.exp_rise_alpha_1.free = True +project_2.experiments['sim_lbco'].peak.decay_beta_0.free = True +project_2.experiments['sim_lbco'].peak.decay_beta_1.free = True +project_2.experiments['sim_lbco'].peak.rise_alpha_0.free = True +project_2.experiments['sim_lbco'].peak.rise_alpha_1.free = True project_2.analysis.fit() project_2.display.fit.results() @@ -1398,14 +1398,14 @@ # Set Space Group 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' +project_2.structures['si'].space_group.coord_system_code = '2' # Set Lattice Parameters project_2.structures['si'].cell.length_a = 5.43 # Set Atom Sites project_2.structures['si'].atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.125, fract_y=0.125, @@ -1414,7 +1414,7 @@ ) # Assign Structure to Experiment -project_2.experiments['sim_lbco'].linked_phases.create(id='si', scale=1.0) +project_2.experiments['sim_lbco'].linked_structures.create(structure_id='si', scale=1.0) # %% [markdown] # #### Exercise 5.11: Refine the Scale of the Si Phase @@ -1446,7 +1446,7 @@ # and Si peaks are visible in the calculated pattern. However, their # intensities are much too high. Therefore, we need to refine the scale # factor of the Si phase. -project_2.experiments['sim_lbco'].linked_phases['si'].scale.free = True +project_2.experiments['sim_lbco'].linked_structures['si'].scale.free = True # Now we can perform the fit with both phases included. project_2.analysis.fit() @@ -1482,7 +1482,7 @@ # the analysis. # %% -project_2.save_as(dir_path='projects/ed_13_main') +project_2.save_as(dir_path='projects/fitting-exercise-si-lbco-main') # %% [markdown] # #### Final Remarks diff --git a/docs/docs/tutorials/index.json b/docs/docs/tutorials/index.json index 33a9e5537..d92f0f227 100644 --- a/docs/docs/tutorials/index.json +++ b/docs/docs/tutorials/index.json @@ -1,177 +1,202 @@ { - "1": { - "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": "Basic Tutorial: LBCO from CIF", - "description": "Basic Rietveld refinement of La0.5Ba0.5CoO3 with structure and experiment defined via CIF files, using constant wavelength neutron powder diffraction data from HRPT at PSI", - "level": "basic" + "bayesian-dream-display-lbco-hrpt": { + "order": 21, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/bayesian-dream-display-lbco-hrpt/bayesian-dream-display-lbco-hrpt.ipynb", + "original_name": "", + "title": "Bayesian Analysis Display (bumps-dream): LBCO, HRPT", + "description": "Reopen the saved bumps-DREAM Bayesian project for La0.5Ba0.5CoO3 and inspect persisted fit summaries, correlation matrix, and posterior plots without rerunning MCMC sampling", + "level": "advanced" }, - "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 structure and experiment defined directly in code, using constant wavelength neutron powder diffraction data from HRPT at PSI", - "level": "quick" + "bayesian-dream-lbco-hrpt": { + "order": 20, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/bayesian-dream-lbco-hrpt/bayesian-dream-lbco-hrpt.ipynb", + "original_name": "", + "title": "Bayesian Analysis (bumps-dream): LBCO, HRPT", + "description": "Bayesian analysis of La0.5Ba0.5CoO3 using Markov Chain Monte Carlo (MCMC) sampling with the bumps-DREAM minimizer, on constant wavelength neutron powder diffraction data from HRPT at PSI", + "level": "advanced" }, - "3": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-3/ed-3.ipynb", - "original_name": "basic_single-fit_pd-neut-cwl_LBCO-HRPT", - "title": "Complete Tutorial: LBCO, HRPT", - "description": "Comprehensive Rietveld refinement of La0.5Ba0.5CoO3 following a GUI-like workflow with detailed explanations, using constant wavelength neutron powder diffraction data from HRPT at PSI", - "level": "basic" + "bayesian-emcee-lbco-hrpt": { + "order": 22, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/bayesian-emcee-lbco-hrpt/bayesian-emcee-lbco-hrpt.ipynb", + "original_name": "", + "title": "Bayesian Analysis (emcee): LBCO, HRPT", + "description": "Bayesian analysis of La0.5Ba0.5CoO3 using Markov Chain Monte Carlo (MCMC) sampling with the emcee minimizer, on constant wavelength neutron powder diffraction data from HRPT at PSI", + "level": "advanced" }, - "4": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-4/ed-4.ipynb", - "original_name": "advanced_joint-fit_pd-neut-xray-cwl_PbSO4", - "title": "Advanced: PbSO4 Joint NPD+XRD Fit", - "description": "Advanced Rietveld refinement of PbSO4 using a joint fit of constant wavelength neutron and X-ray powder diffraction data", + "bayesian-emcee-resume-lbco-hrpt": { + "order": 23, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/bayesian-emcee-resume-lbco-hrpt/bayesian-emcee-resume-lbco-hrpt.ipynb", + "original_name": "", + "title": "Bayesian Analysis Resume (emcee): LBCO, HRPT", + "description": "Reload a saved emcee Bayesian project for La0.5Ba0.5CoO3, inspect the posterior, and resume MCMC sampling with additional steps (supported only for emcee, not bumps-DREAM)", "level": "advanced" }, - "5": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-5/ed-5.ipynb", - "original_name": "cryst-struct_pd-neut-cwl_CoSiO4-D20", - "title": "Crystal Structure: Co2SiO4, D20", - "description": "Rietveld refinement of Co2SiO4 crystal structure using constant wavelength neutron powder diffraction data from D20 at ILL", - "level": "intermediate" + "bayesian-emcee-tbti-heidi": { + "order": 24, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/bayesian-emcee-tbti-heidi/bayesian-emcee-tbti-heidi.ipynb", + "original_name": "", + "title": "Bayesian Analysis (emcee): Tb2TiO7, HEiDi", + "description": "Bayesian analysis of Tb2TiO7 using Markov Chain Monte Carlo (MCMC) sampling with the emcee minimizer, on constant wavelength single crystal neutron diffraction data from HEiDi at FRM II", + "level": "advanced" }, - "6": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-6/ed-6.ipynb", - "original_name": "cryst-struct_pd-neut-cwl_HS-HRPT", - "title": "Crystal Structure: HS, HRPT", - "description": "Rietveld refinement of HS crystal structure using constant wavelength neutron powder diffraction data from HRPT at PSI", + "calibrate-beer-ess": { + "order": 19, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/calibrate-beer-ess/calibrate-beer-ess.ipynb", + "original_name": "", + "title": "Instrument calibration: BEER at ESS", + "description": "Instrument calibration for BEER at ESS using time-of-flight neutron powder diffraction data simulated with McStas", "level": "intermediate" }, - "7": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-7/ed-7.ipynb", - "original_name": "cryst-struct_pd-neut-tof_Si-SEPD", - "title": "Crystal Structure: Si, SEPD (TOF)", - "description": "Rietveld refinement of Si crystal structure using time-of-flight neutron powder diffraction data from SEPD at Argonne", - "level": "intermediate" + "fitting-exercise-si-lbco": { + "order": 25, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/fitting-exercise-si-lbco/fitting-exercise-si-lbco.ipynb", + "original_name": "dmsc-summer-school-2025_analysis-powder-diffraction", + "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" }, - "8": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-8/ed-8.ipynb", - "original_name": "cryst-struct_pd-neut-tof_multidata_NCAF-WISH", - "title": "Crystal Structure: NCAF, WISH (Multi-Data)", - "description": "Rietveld refinement of Na2Ca3Al2F14 using time-of-flight neutron powder diffraction data from WISH at ISIS with joint fitting of two detector banks", - "level": "intermediate" + "joint-si-bragg-pdf": { + "order": 16, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/joint-si-bragg-pdf/joint-si-bragg-pdf.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" }, - "9": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-9/ed-9.ipynb", - "original_name": "cryst-struct_pd-neut-tof_multiphase-LBCO-Si_McStas", - "title": "Crystal Structure: LBCO+Si Multi-Phase (McStas)", - "description": "Rietveld refinement of La0.5Ba0.5CoO3 with a Si impurity phase using time-of-flight neutron powder diffraction data simulated with McStas", + "load-and-fit-lbco-hrpt": { + "order": 4, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/load-and-fit-lbco-hrpt/load-and-fit-lbco-hrpt.ipynb", + "original_name": "", + "title": "Quick Start: LBCO Load Project", + "description": "Most minimal example: load a saved project and run a Rietveld refinement of La0.5Ba0.5CoO3 using constant wavelength neutron powder diffraction data from HRPT at PSI", + "level": "quick" + }, + "pdf-nacl-xrd": { + "order": 13, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/pdf-nacl-xrd/pdf-nacl-xrd.ipynb", + "original_name": "pdf_pd-xray_NaCl", + "title": "PDF Analysis: NaCl, XRD", + "description": "Pair distribution function (PDF) analysis of NaCl using data from an X-ray powder diffraction experiment", "level": "intermediate" }, - "10": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-10/ed-10.ipynb", + "pdf-ni-npd": { + "order": 11, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/pdf-ni-npd/pdf-ni-npd.ipynb", "original_name": "pdf_pd-neut-cwl_Ni", "title": "PDF Analysis: Ni, NPD", "description": "Pair distribution function (PDF) analysis of Ni using data from a constant wavelength neutron powder diffraction experiment", "level": "intermediate" }, - "11": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-11/ed-11.ipynb", + "pdf-si-nomad": { + "order": 12, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/pdf-si-nomad/pdf-si-nomad.ipynb", "original_name": "pdf_pd-neut-tof_Si-NOMAD", "title": "PDF Analysis: Si, NOMAD (TOF)", "description": "Pair distribution function (PDF) analysis of Si using data from a time-of-flight neutron powder diffraction experiment at NOMAD at SNS", "level": "intermediate" }, - "12": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-12/ed-12.ipynb", - "original_name": "pdf_pd-xray_NaCl", - "title": "PDF Analysis: NaCl, XRD", - "description": "Pair distribution function (PDF) analysis of NaCl using data from an X-ray powder diffraction experiment", - "level": "intermediate" - }, - "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: 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 constant wavelength 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 single crystal neutron diffraction data from SENJU at J-PARC", + "refine-cosio-d20": { + "order": 6, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-cosio-d20/refine-cosio-d20.ipynb", + "original_name": "cryst-struct_pd-neut-cwl_CoSiO4-D20", + "title": "Crystal Structure: Co2SiO4, D20", + "description": "Rietveld refinement of Co2SiO4 crystal structure using constant wavelength neutron powder diffraction data from D20 at ILL", "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", + "refine-cosio-d20-tscan": { + "order": 17, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-cosio-d20-tscan/refine-cosio-d20-tscan.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" }, - "18": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-18/ed-18.ipynb", + "refine-cosio-d20-tscan-resumed": { + "order": 5, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-cosio-d20-tscan-resumed/refine-cosio-d20-tscan-resumed.ipynb", "original_name": "", - "title": "Quick Start: LBCO Load Project", - "description": "Most minimal example: load a saved project and run a Rietveld refinement of La0.5Ba0.5CoO3 using constant wavelength neutron powder diffraction data from HRPT at PSI", - "level": "quick" + "title": "Structure Refinement: Co2SiO4, D20 (Resumed scan)", + "description": "Resume an interrupted sequential Rietveld refinement of Co2SiO4 by reloading a saved project; the remaining temperature-scan datasets are processed and appended to analysis/results.csv", + "level": "advanced" }, - "20": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-20/ed-20.ipynb", - "original_name": "", - "title": "Instrument calibration: BEER at ESS", - "description": "Instrument calibration for BEER at ESS using time-of-flight neutron powder diffraction data simulated with McStas", + "refine-hs-hrpt": { + "order": 7, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-hs-hrpt/refine-hs-hrpt.ipynb", + "original_name": "cryst-struct_pd-neut-cwl_HS-HRPT", + "title": "Crystal Structure: HS, HRPT", + "description": "Rietveld refinement of HS crystal structure using constant wavelength neutron powder diffraction data from HRPT at PSI", "level": "intermediate" }, - "21": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-21/ed-21.ipynb", - "original_name": "", - "title": "Bayesian Analysis (bumps-dream): LBCO, HRPT", - "description": "Bayesian analysis of La0.5Ba0.5CoO3 using Markov Chain Monte Carlo (MCMC) sampling with the bumps-DREAM minimizer, on constant wavelength neutron powder diffraction data from HRPT at PSI", - "level": "advanced" + "refine-lbco-hrpt-from-cif": { + "order": 2, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-lbco-hrpt-from-cif/refine-lbco-hrpt-from-cif.ipynb", + "original_name": "quick_from-cif_pd-neut-cwl_LBCO-HRPT", + "title": "Basic Tutorial: LBCO from CIF", + "description": "Basic Rietveld refinement of La0.5Ba0.5CoO3 with structure and experiment defined via CIF files, using constant wavelength neutron powder diffraction data from HRPT at PSI", + "level": "basic" }, - "22": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-22/ed-22.ipynb", - "original_name": "", - "title": "Bayesian Analysis (emcee): Tb2TiO7, HEiDi", - "description": "Bayesian analysis of Tb2TiO7 using Markov Chain Monte Carlo (MCMC) sampling with the emcee minimizer, on constant wavelength single crystal neutron diffraction data from HEiDi at FRM II", - "level": "advanced" + "refine-lbco-hrpt-from-data": { + "order": 1, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-lbco-hrpt-from-data/refine-lbco-hrpt-from-data.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 structure and experiment defined directly in code, using constant wavelength neutron powder diffraction data from HRPT at PSI", + "level": "quick" }, - "23": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-23/ed-23.ipynb", - "original_name": "", - "title": "Structure Refinement: Co2SiO4, D20 (Resumed scan)", - "description": "Resume an interrupted sequential Rietveld refinement of Co2SiO4 by reloading a saved project; the remaining temperature-scan datasets are processed and appended to analysis/results.csv", - "level": "advanced" + "refine-lbco-hrpt-report": { + "order": 3, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-lbco-hrpt-report/refine-lbco-hrpt-report.ipynb", + "original_name": "basic_single-fit_pd-neut-cwl_LBCO-HRPT", + "title": "Complete Tutorial: LBCO, HRPT", + "description": "Comprehensive Rietveld refinement of La0.5Ba0.5CoO3 following a GUI-like workflow with detailed explanations, using constant wavelength neutron powder diffraction data from HRPT at PSI", + "level": "basic" }, - "24": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-24/ed-24.ipynb", - "original_name": "", - "title": "Bayesian Analysis Display (bumps-dream): LBCO, HRPT", - "description": "Reopen the saved bumps-DREAM Bayesian project for La0.5Ba0.5CoO3 and inspect persisted fit summaries, correlation matrix, and posterior plots without rerunning MCMC sampling", + "refine-lbco-si-mcstas": { + "order": 18, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-lbco-si-mcstas/refine-lbco-si-mcstas.ipynb", + "original_name": "cryst-struct_pd-neut-tof_multiphase-LBCO-Si_McStas", + "title": "Crystal Structure: LBCO+Si Multi-Phase (McStas)", + "description": "Rietveld refinement of La0.5Ba0.5CoO3 with a Si impurity phase using time-of-flight neutron powder diffraction data simulated with McStas", + "level": "intermediate" + }, + "refine-ncaf-wish": { + "order": 15, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-ncaf-wish/refine-ncaf-wish.ipynb", + "original_name": "cryst-struct_pd-neut-tof_multidata_NCAF-WISH", + "title": "Crystal Structure: NCAF, WISH (Multi-Data)", + "description": "Rietveld refinement of Na2Ca3Al2F14 using time-of-flight neutron powder diffraction data from WISH at ISIS with joint fitting of two detector banks", + "level": "intermediate" + }, + "refine-pbso4-joint": { + "order": 14, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-pbso4-joint/refine-pbso4-joint.ipynb", + "original_name": "advanced_joint-fit_pd-neut-xray-cwl_PbSO4", + "title": "Advanced: PbSO4 Joint NPD+XRD Fit", + "description": "Advanced Rietveld refinement of PbSO4 using a joint fit of constant wavelength neutron and X-ray powder diffraction data", "level": "advanced" }, - "25": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-25/ed-25.ipynb", + "refine-si-sepd": { + "order": 8, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-si-sepd/refine-si-sepd.ipynb", + "original_name": "cryst-struct_pd-neut-tof_Si-SEPD", + "title": "Crystal Structure: Si, SEPD (TOF)", + "description": "Rietveld refinement of Si crystal structure using time-of-flight neutron powder diffraction data from SEPD at Argonne", + "level": "intermediate" + }, + "refine-taurine-senju": { + "order": 10, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-taurine-senju/refine-taurine-senju.ipynb", "original_name": "", - "title": "Bayesian Analysis (emcee): LBCO, HRPT", - "description": "Bayesian analysis of La0.5Ba0.5CoO3 using Markov Chain Monte Carlo (MCMC) sampling with the emcee minimizer, on constant wavelength neutron powder diffraction data from HRPT at PSI", - "level": "advanced" + "title": "Crystal Structure: Taurine, SENJU (TOF)", + "description": "Crystal structure refinement of Taurine using time-of-flight single crystal neutron diffraction data from SENJU at J-PARC", + "level": "intermediate" }, - "26": { - "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-26/ed-26.ipynb", + "refine-tbti-heidi": { + "order": 9, + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/refine-tbti-heidi/refine-tbti-heidi.ipynb", "original_name": "", - "title": "Bayesian Analysis Resume (emcee): LBCO, HRPT", - "description": "Reload a saved emcee Bayesian project for La0.5Ba0.5CoO3, inspect the posterior, and resume MCMC sampling with additional steps (supported only for emcee, not bumps-DREAM)", - "level": "advanced" + "title": "Crystal Structure: Tb2TiO7, HEiDi", + "description": "Crystal structure refinement of Tb2TiO7 using constant wavelength single crystal neutron diffraction data from HEiDi at FRM II", + "level": "intermediate" } } diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md index 64511d5a0..893ea99ae 100644 --- a/docs/docs/tutorials/index.md +++ b/docs/docs/tutorials/index.md @@ -1,4 +1,5 @@ --- +title: Tutorials icon: material/school --- @@ -17,21 +18,22 @@ The tutorials are organized into the following categories: ## Getting Started -- [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 `basic` `load`](ed-1.ipynb) – A basic 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 `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 +- [LBCO `quick` `code`](refine-lbco-hrpt-from-data.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 `basic` `load`](refine-lbco-hrpt-from-cif.ipynb) – A basic + 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 `complete`](refine-lbco-hrpt-report.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 @@ -40,124 +42,128 @@ The tutorials are organized into the following categories: ## Load Project -- [LBCO Single Fit](ed-18.ipynb) – The most minimal example showing how - to load a previously saved project from a directory and continue - working with it. -- [Co2SiO4 Sequential Fit](ed-23.ipynb) – Resumes a sequential - refinement from an existing `analysis/results.csv` after an incomplete - previous run. +- [LBCO Single Fit](load-and-fit-lbco-hrpt.ipynb) – The most minimal + example showing how to load a previously saved project from a + directory and continue working with it. +- [Co2SiO4 Sequential Fit](refine-cosio-d20-tscan-resumed.ipynb) – + Resumes a sequential refinement from an existing + `analysis/results.csv` after an incomplete previous run. See also under [Bayesian Analysis](#bayesian-analysis): -[LBCO Bayesian Display (`bumps-dream`)](ed-24.ipynb) and -[LBCO Bayesian Resume (`emcee`)](ed-26.ipynb) — both load saved projects -containing Bayesian fit state. +[LBCO Bayesian Display (`bumps-dream`)](bayesian-dream-display-lbco-hrpt.ipynb) +and +[LBCO Bayesian Resume (`emcee`)](bayesian-emcee-resume-lbco-hrpt.ipynb) +— both load saved projects containing Bayesian fit state. ## Powder Diffraction -- [Co2SiO4 `pd-neut-cwl`](ed-5.ipynb) – Demonstrates a Rietveld - refinement of the Co2SiO4 crystal structure using constant wavelength - neutron powder diffraction data from D20 at ILL. -- [HS `pd-neut-cwl`](ed-6.ipynb) – Demonstrates a Rietveld refinement of - the HS crystal structure using constant wavelength neutron powder - diffraction data from HRPT at PSI. -- [Si `pd-neut-tof`](ed-7.ipynb) – Demonstrates a Rietveld refinement of - the Si crystal structure using time-of-flight neutron powder - diffraction data from SEPD at Argonne. -- [NCAF `pd-neut-tof`](ed-8.ipynb) – Demonstrates a Rietveld refinement - of the Na2Ca3Al2F14 crystal structure using two time-of-flight neutron - powder diffraction datasets (from two detector banks) of the WISH - instrument at ISIS. +- [Co2SiO4 `pd-neut-cwl`](refine-cosio-d20.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`](refine-hs-hrpt.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`](refine-si-sepd.ipynb) – Demonstrates a Rietveld + refinement of the Si crystal structure using time-of-flight neutron + powder diffraction data from SEPD at Argonne. ## Without Measured Data -- [LBCO `pd-neut-cwl`](ed-27.ipynb) – Loads the La0.5Ba0.5CoO3 structure - from a CIF and calculates a constant-wavelength neutron powder pattern - over a chosen 2θ range, with background and Bragg markers and no - measured data, then edits structure and profile parameters and - recalculates to show the pattern update. -- [Si `pd-neut-tof`](ed-28.ipynb) – Calculates a time-of-flight neutron - powder pattern for Si, with the calculation window derived from the - instrument's TOF calibration and then set explicitly. -- [NaCl `pd-xray`](ed-29.ipynb) – The most minimal example: an X-ray - powder pattern for NaCl using the default calculation range. +- [LBCO `pd-neut-cwl`](simulate-lbco-cwl.ipynb) – Loads the + La0.5Ba0.5CoO3 structure from a CIF and calculates a + constant-wavelength neutron powder pattern over a chosen 2θ range, + with background and Bragg markers and no measured data, then edits + structure and profile parameters and recalculates to show the pattern + update. +- [Si `pd-neut-tof`](simulate-si-tof.ipynb) – Calculates a + time-of-flight neutron powder pattern for Si, with the calculation + window derived from the instrument's TOF calibration and then set + explicitly. +- [NaCl `pd-xray`](simulate-nacl-xray.ipynb) – The most minimal example: + an X-ray powder pattern for NaCl using the default calculation range. ## Single Crystal Diffraction -- [Tb2TiO7 `sc-neut-cwl`](ed-14.ipynb) – Demonstrates structure - refinement of Tb2TiO7 using constant wavelength neutron single crystal - diffraction data from HEiDi at FRM II. -- [Taurine `sc-neut-tof`](ed-15.ipynb) – Demonstrates structure - refinement of Taurine using time-of-flight neutron single crystal - diffraction data from SENJU at J-PARC. +- [Tb2TiO7 `sc-neut-cwl`](refine-tbti-heidi.ipynb) – Demonstrates + structure refinement of Tb2TiO7 using constant wavelength neutron + single crystal diffraction data from HEiDi at FRM II. +- [Taurine `sc-neut-tof`](refine-taurine-senju.ipynb) – Demonstrates + structure refinement of Taurine using time-of-flight neutron single + crystal diffraction data from SENJU at J-PARC. ## Pair Distribution Function -- [Ni `pd-neut-cwl`](ed-10.ipynb) – Demonstrates a PDF analysis of Ni - using data collected from a constant wavelength neutron powder +- [Ni `pd-neut-cwl`](pdf-ni-npd.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. +- [Si `pd-neut-tof`](pdf-si-nomad.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`](pdf-nacl-xrd.ipynb) – Demonstrates a PDF analysis of + NaCl using data collected from an X-ray powder diffraction experiment. ## Multiple Data Blocks -- [PbSO4 NPD+XRD](ed-4.ipynb) – Joint fit of PbSO4 using X-ray and - neutron constant wavelength powder diffraction data. -- [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. +- [PbSO4 NPD+XRD](refine-pbso4-joint.ipynb) – Joint fit of PbSO4 using + X-ray and neutron constant wavelength powder diffraction data. +- [NCAF `pd-neut-tof`](refine-ncaf-wish.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. +- [Si Bragg+PDF](joint-si-bragg-pdf.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](refine-cosio-d20-tscan.ipynb) – Sequential + Rietveld refinement of Co2SiO4 using constant wavelength neutron + powder diffraction data from D20 at ILL across a temperature scan. ## Simulated 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. -- [BEER McStas](ed-20.ipynb) – Rietveld refinement based on the data - simulated with McStas for the BEER instrument at ESS. +- [LBCO+Si McStas](refine-lbco-si-mcstas.ipynb) – Multi-phase Rietveld + refinement of La0.5Ba0.5CoO3 with Si impurity using time-of-flight + neutron data simulated with McStas. +- [BEER McStas](calibrate-beer-ess.ipynb) – Rietveld refinement based on + the data simulated with McStas for the BEER instrument at ESS. ## Bayesian Analysis -- [LBCO Bayesian (`bumps-dream`)](ed-21.ipynb) – Demonstrates how to - perform a Bayesian analysis of the La0.5Ba0.5CoO3 crystal structure - using constant wavelength neutron powder diffraction data from HRPT at - PSI. Covers the use of Markov Chain Monte Carlo (MCMC) sampling with - the bumps-DREAM minimizer to explore the posterior distribution of the - refined parameters, providing insights into parameter uncertainties - and correlations. -- [LBCO Bayesian Display (`bumps-dream`)](ed-24.ipynb) – Shows how to - reopen the saved Bayesian project produced by the LBCO Bayesian - tutorial and inspect persisted fit summaries, correlation matrix, - posterior distribution plots, and predictive checks — without +- [LBCO Bayesian (`bumps-dream`)](bayesian-dream-lbco-hrpt.ipynb) – + Demonstrates how to perform a Bayesian analysis of the La0.5Ba0.5CoO3 + crystal structure using constant wavelength neutron powder diffraction + data from HRPT at PSI. Covers the use of Markov Chain Monte Carlo + (MCMC) sampling with the bumps-DREAM minimizer to explore the + posterior distribution of the refined parameters, providing insights + into parameter uncertainties and correlations. +- [LBCO Bayesian Display (`bumps-dream`)](bayesian-dream-display-lbco-hrpt.ipynb) + – Shows how to reopen the saved Bayesian project produced by the LBCO + Bayesian tutorial and inspect persisted fit summaries, correlation + matrix, posterior distribution plots, and predictive checks — without rerunning MCMC sampling. -- [LBCO Bayesian (`emcee`)](ed-25.ipynb) – Two-stage workflow on the - LBCO HRPT dataset: first a quick local refinement to obtain a point - estimate and uncertainties, then full posterior sampling with the - emcee minimizer. Covers credible intervals, parameter correlations, - and propagation of uncertainty into the calculated diffraction - pattern. -- [LBCO Bayesian Resume (`emcee`)](ed-26.ipynb) – Loads a Bayesian - project that already contains an emcee chain, inspects the posterior, - and resumes sampling with additional steps. The full project state - (parameters, chain, plot caches) round-trips through disk. Resuming is - currently supported only for emcee, not for bumps-DREAM. -- [Tb2TiO7 Bayesian (`emcee`)](ed-22.ipynb) – Another example of a - Bayesian analysis, focused on the Tb2TiO7 crystal structure using - constant wavelength neutron single crystal diffraction data from HEiDi - at FRM II. Similar to the LBCO Bayesian tutorial, it covers MCMC - sampling to explore the posterior distribution of the refined - parameters, providing insights into parameter uncertainties and - correlations in the context of single crystal diffraction data. +- [LBCO Bayesian (`emcee`)](bayesian-emcee-lbco-hrpt.ipynb) – Two-stage + workflow on the LBCO HRPT dataset: first a quick local refinement to + obtain a point estimate and uncertainties, then full posterior + sampling with the emcee minimizer. Covers credible intervals, + parameter correlations, and propagation of uncertainty into the + calculated diffraction pattern. +- [LBCO Bayesian Resume (`emcee`)](bayesian-emcee-resume-lbco-hrpt.ipynb) + – Loads a Bayesian project that already contains an emcee chain, + inspects the posterior, and resumes sampling with additional steps. + The full project state (parameters, chain, plot caches) round-trips + through disk. Resuming is currently supported only for emcee, not for + bumps-DREAM. +- [Tb2TiO7 Bayesian (`emcee`)](bayesian-emcee-tbti-heidi.ipynb) – + Another example of a Bayesian analysis, focused on the Tb2TiO7 crystal + structure using constant wavelength neutron single crystal diffraction + data from HEiDi at FRM II. Similar to the LBCO Bayesian tutorial, it + covers MCMC sampling to explore the posterior distribution of the + refined parameters, providing insights into parameter uncertainties + and correlations in the context of single crystal diffraction data. ## 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 +- [DMSC Summer School](fitting-exercise-si-lbco.ipynb) – A workshop + tutorial that demonstrates a Rietveld refinement of the La0.5Ba0.5CoO3 + crystal structure using time-of-flight neutron powder diffraction data simulated with McStas. This tutorial is designed for the ESS DMSC Summer School. diff --git a/docs/docs/tutorials/ed-16.ipynb b/docs/docs/tutorials/joint-si-bragg-pdf.ipynb similarity index 92% rename from docs/docs/tutorials/ed-16.ipynb rename to docs/docs/tutorials/joint-si-bragg-pdf.ipynb index 0e26f35e7..e81fbeaae 100644 --- a/docs/docs/tutorials/ed-16.ipynb +++ b/docs/docs/tutorials/joint-si-bragg-pdf.ipynb @@ -95,7 +95,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'F d -3 m'\n", - "structure.space_group.it_coordinate_system_code = '1'" + "structure.space_group.coord_system_code = '1'" ] }, { @@ -132,7 +132,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -163,7 +163,7 @@ "metadata": {}, "outputs": [], "source": [ - "bragg_data_path = download_data(id=7, destination='data')" + "bragg_data_path = download_data('meas-si-sepd', destination='data')" ] }, { @@ -204,7 +204,7 @@ "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" + "bragg_expt.instrument.calib_d_to_tof_quadratic = -1.54" ] }, { @@ -226,10 +226,10 @@ "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.exp_decay_beta_0 = 0.04221\n", - "bragg_expt.peak.exp_decay_beta_1 = 0.00946\n", - "bragg_expt.peak.exp_rise_alpha_0 = 0.0\n", - "bragg_expt.peak.exp_rise_alpha_1 = 0.5971" + "bragg_expt.peak.decay_beta_0 = 0.04221\n", + "bragg_expt.peak.decay_beta_1 = 0.00946\n", + "bragg_expt.peak.rise_alpha_0 = 0.0\n", + "bragg_expt.peak.rise_alpha_1 = 0.5971" ] }, { @@ -249,7 +249,7 @@ "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)" + " bragg_expt.background.create(id=str(x), position=x, intensity=200)" ] }, { @@ -257,7 +257,7 @@ "id": "22", "metadata": {}, "source": [ - "#### Set Linked Phases" + "#### Set Linked Structures" ] }, { @@ -267,7 +267,7 @@ "metadata": {}, "outputs": [], "source": [ - "bragg_expt.linked_phases.create(id='si', scale=13.0)" + "bragg_expt.linked_structures.create(structure_id='si', scale=13.0)" ] }, { @@ -287,7 +287,7 @@ "metadata": {}, "outputs": [], "source": [ - "pdf_data_path = download_data(id=5, destination='data')" + "pdf_data_path = download_data('meas-si-pdf-nomad', destination='data')" ] }, { @@ -341,7 +341,7 @@ "id": "30", "metadata": {}, "source": [ - "#### Set Linked Phases" + "#### Set Linked Structures" ] }, { @@ -351,7 +351,7 @@ "metadata": {}, "outputs": [], "source": [ - "pdf_expt.linked_phases.create(id='si', scale=1.0)" + "pdf_expt.linked_structures.create(structure_id='si', scale=1.0)" ] }, { @@ -522,13 +522,13 @@ "metadata": {}, "outputs": [], "source": [ - "bragg_expt.linked_phases['si'].scale.free = True\n", + "bragg_expt.linked_structures['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" + " point.intensity.free = True" ] }, { @@ -546,7 +546,7 @@ "metadata": {}, "outputs": [], "source": [ - "pdf_expt.linked_phases['si'].scale.free = True\n", + "pdf_expt.linked_structures['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", @@ -634,7 +634,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_16_si_bragg_pdf')" + "project.save_as(dir_path='projects/joint-si-bragg-pdf')" ] } ], diff --git a/docs/docs/tutorials/ed-16.py b/docs/docs/tutorials/joint-si-bragg-pdf.py similarity index 85% rename from docs/docs/tutorials/ed-16.py rename to docs/docs/tutorials/joint-si-bragg-pdf.py index 1a811a2a8..fbff67859 100644 --- a/docs/docs/tutorials/ed-16.py +++ b/docs/docs/tutorials/joint-si-bragg-pdf.py @@ -34,7 +34,7 @@ # %% structure.space_group.name_h_m = 'F d -3 m' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' # %% [markdown] # ### Set Unit Cell @@ -47,7 +47,7 @@ # %% structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0, fract_y=0, @@ -66,7 +66,7 @@ # #### Download Data # %% -bragg_data_path = download_data(id=7, destination='data') +bragg_data_path = download_data('meas-si-sepd', destination='data') # %% [markdown] # #### Create Experiment @@ -83,7 +83,7 @@ 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 +bragg_expt.instrument.calib_d_to_tof_quadratic = -1.54 # %% [markdown] # #### Set Peak Profile @@ -93,10 +93,10 @@ 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.exp_decay_beta_0 = 0.04221 -bragg_expt.peak.exp_decay_beta_1 = 0.00946 -bragg_expt.peak.exp_rise_alpha_0 = 0.0 -bragg_expt.peak.exp_rise_alpha_1 = 0.5971 +bragg_expt.peak.decay_beta_0 = 0.04221 +bragg_expt.peak.decay_beta_1 = 0.00946 +bragg_expt.peak.rise_alpha_0 = 0.0 +bragg_expt.peak.rise_alpha_1 = 0.5971 # %% [markdown] # #### Set Background @@ -104,13 +104,13 @@ # %% bragg_expt.background.type = 'line-segment' for x in range(0, 35000, 5000): - bragg_expt.background.create(id=str(x), x=x, y=200) + bragg_expt.background.create(id=str(x), position=x, intensity=200) # %% [markdown] -# #### Set Linked Phases +# #### Set Linked Structures # %% -bragg_expt.linked_phases.create(id='si', scale=13.0) +bragg_expt.linked_structures.create(structure_id='si', scale=13.0) # %% [markdown] # ### Experiment 2: PDF (NOMAD, TOF) @@ -118,7 +118,7 @@ # #### Download Data # %% -pdf_data_path = download_data(id=5, destination='data') +pdf_data_path = download_data('meas-si-pdf-nomad', destination='data') # %% [markdown] # #### Create Experiment @@ -143,10 +143,10 @@ pdf_expt.peak.damp_particle_diameter = 0 # %% [markdown] -# #### Set Linked Phases +# #### Set Linked Structures # %% -pdf_expt.linked_phases.create(id='si', scale=1.0) +pdf_expt.linked_structures.create(structure_id='si', scale=1.0) # %% [markdown] # ## 📦 Define Project @@ -214,19 +214,19 @@ # Bragg experiment parameters. # %% -bragg_expt.linked_phases['si'].scale.free = True +bragg_expt.linked_structures['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 + point.intensity.free = True # %% [markdown] # PDF experiment parameters. # %% -pdf_expt.linked_phases['si'].scale.free = True +pdf_expt.linked_structures['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 @@ -259,4 +259,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_16_si_bragg_pdf') +project.save_as(dir_path='projects/joint-si-bragg-pdf') diff --git a/docs/docs/tutorials/ed-18.ipynb b/docs/docs/tutorials/load-and-fit-lbco-hrpt.ipynb similarity index 82% rename from docs/docs/tutorials/ed-18.ipynb rename to docs/docs/tutorials/load-and-fit-lbco-hrpt.ipynb index 0299b4487..ade6960dd 100644 --- a/docs/docs/tutorials/ed-18.ipynb +++ b/docs/docs/tutorials/load-and-fit-lbco-hrpt.ipynb @@ -52,8 +52,7 @@ "metadata": {}, "outputs": [], "source": [ - "from easydiffraction import Project\n", - "from easydiffraction import download_data" + "import easydiffraction as edi" ] }, { @@ -69,7 +68,10 @@ "id": "5", "metadata": {}, "source": [ - "### Download Project" + "### Locate Project\n", + "\n", + "Download and extract the saved project from the EasyDiffraction data\n", + "repository." ] }, { @@ -79,7 +81,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_dir = download_data(id=36, destination='projects')" + "project_dir = edi.download_data('proj-lbco-hrpt', destination='projects')" ] }, { @@ -97,20 +99,39 @@ "metadata": {}, "outputs": [], "source": [ - "project = Project.load(project_dir)" + "project = edi.Project.load(project_dir)" ] }, { "cell_type": "markdown", "id": "9", "metadata": {}, + "source": [ + "Re-save the project to a fresh working directory so fitting below\n", + "writes there instead of the bundled read-only copy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "project.save_as(dir_path='projects/load-and-fit-lbco-hrpt')" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, "source": [ "## 🚀 Perform Analysis" ] }, { "cell_type": "markdown", - "id": "10", + "id": "12", "metadata": {}, "source": [ "### Display Structure" @@ -119,7 +140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -128,7 +149,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "14", "metadata": {}, "source": [ "### Run Fitting" @@ -137,7 +158,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -146,7 +167,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "16", "metadata": {}, "source": [ "### Display Fit Results" @@ -155,7 +176,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -165,7 +186,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -175,7 +196,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -184,7 +205,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "20", "metadata": {}, "source": [ "## 💾 Save Project" @@ -193,11 +214,11 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "21", "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_18_lbco_hrpt')" + "project.save_as(dir_path='projects/load-and-fit-lbco-hrpt')" ] } ], diff --git a/docs/docs/tutorials/ed-18.py b/docs/docs/tutorials/load-and-fit-lbco-hrpt.py similarity index 67% rename from docs/docs/tutorials/ed-18.py rename to docs/docs/tutorials/load-and-fit-lbco-hrpt.py index c65c0243f..d4d38d265 100644 --- a/docs/docs/tutorials/ed-18.py +++ b/docs/docs/tutorials/load-and-fit-lbco-hrpt.py @@ -15,23 +15,32 @@ # ## 🛠️ Import Library # %% -from easydiffraction import Project -from easydiffraction import download_data +import easydiffraction as edi # %% [markdown] # ## 📂 Load Project # %% [markdown] -# ### Download Project +# ### Locate Project +# +# Download and extract the saved project from the EasyDiffraction data +# repository. # %% -project_dir = download_data(id=36, destination='projects') +project_dir = edi.download_data('proj-lbco-hrpt', destination='projects') # %% [markdown] # ### Load Project # %% -project = Project.load(project_dir) +project = edi.Project.load(project_dir) + +# %% [markdown] +# Re-save the project to a fresh working directory so fitting below +# writes there instead of the bundled read-only copy. + +# %% +project.save_as(dir_path='projects/load-and-fit-lbco-hrpt') # %% [markdown] # ## 🚀 Perform Analysis @@ -64,4 +73,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_18_lbco_hrpt') +project.save_as(dir_path='projects/load-and-fit-lbco-hrpt') diff --git a/docs/docs/tutorials/ed-12.ipynb b/docs/docs/tutorials/pdf-nacl-xrd.ipynb similarity index 93% rename from docs/docs/tutorials/ed-12.ipynb rename to docs/docs/tutorials/pdf-nacl-xrd.ipynb index 2499a1981..3279652a8 100644 --- a/docs/docs/tutorials/ed-12.ipynb +++ b/docs/docs/tutorials/pdf-nacl-xrd.ipynb @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -75,7 +75,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='nacl_xray_pdf')" + "project = edi.Project(name='nacl_xray_pdf')" ] }, { @@ -136,10 +136,10 @@ "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'].space_group.coord_system_code = '1'\n", "project.structures['nacl'].cell.length_a = 5.62\n", "project.structures['nacl'].atom_sites.create(\n", - " label='Na',\n", + " id='Na',\n", " type_symbol='Na',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -147,7 +147,7 @@ " adp_iso=1.0,\n", ")\n", "project.structures['nacl'].atom_sites.create(\n", - " label='Cl',\n", + " id='Cl',\n", " type_symbol='Cl',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -189,7 +189,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=4, destination='data')" + "data_path = edi.download_data('meas-nacl-pdf', destination='data')" ] }, { @@ -251,7 +251,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['xray_pdf'].linked_phases.create(id='nacl', scale=0.5)" + "project.experiments['xray_pdf'].linked_structures.create(structure_id='nacl', scale=0.5)" ] }, { @@ -289,7 +289,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['xray_pdf'].linked_phases['nacl'].scale.free = True\n", + "project.experiments['xray_pdf'].linked_structures['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" ] @@ -347,7 +347,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_12_nacl_xray_pdf')" + "project.save_as(dir_path='projects/pdf-nacl-xrd')" ] } ], diff --git a/docs/docs/tutorials/ed-12.py b/docs/docs/tutorials/pdf-nacl-xrd.py similarity index 86% rename from docs/docs/tutorials/ed-12.py rename to docs/docs/tutorials/pdf-nacl-xrd.py index 8b8f31617..8cf71a543 100644 --- a/docs/docs/tutorials/ed-12.py +++ b/docs/docs/tutorials/pdf-nacl-xrd.py @@ -12,7 +12,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -21,7 +21,7 @@ # ### Create Project # %% -project = ed.Project(name='nacl_xray_pdf') +project = edi.Project(name='nacl_xray_pdf') # %% [markdown] # ### Set Plotting Engine @@ -44,10 +44,10 @@ # %% 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'].space_group.coord_system_code = '1' project.structures['nacl'].cell.length_a = 5.62 project.structures['nacl'].atom_sites.create( - label='Na', + id='Na', type_symbol='Na', fract_x=0, fract_y=0, @@ -55,7 +55,7 @@ adp_iso=1.0, ) project.structures['nacl'].atom_sites.create( - label='Cl', + id='Cl', type_symbol='Cl', fract_x=0.5, fract_y=0.5, @@ -73,7 +73,7 @@ # ### Add Experiment # %% -data_path = ed.download_data(id=4, destination='data') +data_path = edi.download_data('meas-nacl-pdf', destination='data') # %% project.experiments.add_from_data_path( @@ -100,7 +100,7 @@ project.experiments['xray_pdf'].peak.damp_particle_diameter = 0 # %% -project.experiments['xray_pdf'].linked_phases.create(id='nacl', scale=0.5) +project.experiments['xray_pdf'].linked_structures.create(structure_id='nacl', scale=0.5) # %% [markdown] # ## 🚀 Perform Analysis @@ -114,7 +114,7 @@ project.structures['nacl'].atom_sites['Cl'].adp_iso.free = True # %% -project.experiments['xray_pdf'].linked_phases['nacl'].scale.free = True +project.experiments['xray_pdf'].linked_structures['nacl'].scale.free = True project.experiments['xray_pdf'].peak.damp_q.free = True project.experiments['xray_pdf'].peak.sharp_delta_2.free = True @@ -136,4 +136,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_12_nacl_xray_pdf') +project.save_as(dir_path='projects/pdf-nacl-xrd') diff --git a/docs/docs/tutorials/ed-10.ipynb b/docs/docs/tutorials/pdf-ni-npd.ipynb similarity index 92% rename from docs/docs/tutorials/ed-10.ipynb rename to docs/docs/tutorials/pdf-ni-npd.ipynb index 0b309da49..26223ac39 100644 --- a/docs/docs/tutorials/ed-10.ipynb +++ b/docs/docs/tutorials/pdf-ni-npd.ipynb @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -75,7 +75,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='ni_pdf')" + "project = edi.Project(name='ni_pdf')" ] }, { @@ -104,10 +104,10 @@ "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'].space_group.coord_system_code = '1'\n", "project.structures['ni'].cell.length_a = 3.52387\n", "project.structures['ni'].atom_sites.create(\n", - " label='Ni',\n", + " id='Ni',\n", " type_symbol='Ni',\n", " fract_x=0.0,\n", " fract_y=0.0,\n", @@ -149,7 +149,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=6, destination='data')" + "data_path = edi.download_data('meas-ni-pdf', destination='data')" ] }, { @@ -176,7 +176,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['pdf'].linked_phases.create(id='ni', scale=1.0)\n", + "project.experiments['pdf'].linked_structures.create(structure_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", @@ -219,7 +219,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['pdf'].linked_phases['ni'].scale.free = True\n", + "project.experiments['pdf'].linked_structures['ni'].scale.free = True\n", "project.experiments['pdf'].peak.broad_q.free = True\n", "project.experiments['pdf'].peak.sharp_delta_2.free = True" ] @@ -277,7 +277,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_10_ni_pdf')" + "project.save_as(dir_path='projects/pdf-ni-npd')" ] } ], diff --git a/docs/docs/tutorials/ed-10.py b/docs/docs/tutorials/pdf-ni-npd.py similarity index 83% rename from docs/docs/tutorials/ed-10.py rename to docs/docs/tutorials/pdf-ni-npd.py index 46f54c02e..e688f84df 100644 --- a/docs/docs/tutorials/ed-10.py +++ b/docs/docs/tutorials/pdf-ni-npd.py @@ -12,7 +12,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -21,7 +21,7 @@ # ### Create Project # %% -project = ed.Project(name='ni_pdf') +project = edi.Project(name='ni_pdf') # %% [markdown] # ### Add Structure @@ -31,10 +31,10 @@ # %% 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'].space_group.coord_system_code = '1' project.structures['ni'].cell.length_a = 3.52387 project.structures['ni'].atom_sites.create( - label='Ni', + id='Ni', type_symbol='Ni', fract_x=0.0, fract_y=0.0, @@ -52,7 +52,7 @@ # ### Add Experiment # %% -data_path = ed.download_data(id=6, destination='data') +data_path = edi.download_data('meas-ni-pdf', destination='data') # %% project.experiments.add_from_data_path( @@ -65,7 +65,7 @@ ) # %% -project.experiments['pdf'].linked_phases.create(id='ni', scale=1.0) +project.experiments['pdf'].linked_structures.create(structure_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 @@ -84,7 +84,7 @@ project.structures['ni'].atom_sites['Ni'].adp_iso.free = True # %% -project.experiments['pdf'].linked_phases['ni'].scale.free = True +project.experiments['pdf'].linked_structures['ni'].scale.free = True project.experiments['pdf'].peak.broad_q.free = True project.experiments['pdf'].peak.sharp_delta_2.free = True @@ -106,4 +106,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_10_ni_pdf') +project.save_as(dir_path='projects/pdf-ni-npd') diff --git a/docs/docs/tutorials/ed-11.ipynb b/docs/docs/tutorials/pdf-si-nomad.ipynb similarity index 93% rename from docs/docs/tutorials/ed-11.ipynb rename to docs/docs/tutorials/pdf-si-nomad.ipynb index 65a861672..a16fd2699 100644 --- a/docs/docs/tutorials/ed-11.ipynb +++ b/docs/docs/tutorials/pdf-si-nomad.ipynb @@ -46,7 +46,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -72,7 +72,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='si_nomad_pdf')" + "project = edi.Project(name='si_nomad_pdf')" ] }, { @@ -131,10 +131,10 @@ "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.space_group.coord_system_code = '1'\n", "structure.cell.length_a = 5.43146\n", "structure.atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -176,7 +176,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=5, destination='data')" + "data_path = edi.download_data('meas-si-pdf-nomad', destination='data')" ] }, { @@ -204,7 +204,7 @@ "outputs": [], "source": [ "experiment = project.experiments['nomad']\n", - "experiment.linked_phases.create(id='si', scale=1.0)\n", + "experiment.linked_structures.create(structure_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", @@ -238,7 +238,7 @@ "source": [ "project.structures['si'].cell.length_a.free = True\n", "project.structures['si'].atom_sites['Si'].adp_iso.free = True\n", - "experiment.linked_phases['si'].scale.free = True" + "experiment.linked_structures['si'].scale.free = True" ] }, { @@ -307,7 +307,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_11_si_nomad_pdf')" + "project.save_as(dir_path='projects/pdf-si-nomad')" ] } ], diff --git a/docs/docs/tutorials/ed-11.py b/docs/docs/tutorials/pdf-si-nomad.py similarity index 84% rename from docs/docs/tutorials/ed-11.py rename to docs/docs/tutorials/pdf-si-nomad.py index a11f1b233..5f4cb4be1 100644 --- a/docs/docs/tutorials/ed-11.py +++ b/docs/docs/tutorials/pdf-si-nomad.py @@ -9,7 +9,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -18,7 +18,7 @@ # ### Create Project # %% -project = ed.Project(name='si_nomad_pdf') +project = edi.Project(name='si_nomad_pdf') # %% [markdown] # ### Set Plotting Engine @@ -39,10 +39,10 @@ # %% structure = project.structures['si'] structure.space_group.name_h_m.value = 'F d -3 m' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' structure.cell.length_a = 5.43146 structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0, fract_y=0, @@ -60,7 +60,7 @@ # ### Add Experiment # %% -data_path = ed.download_data(id=5, destination='data') +data_path = edi.download_data('meas-si-pdf-nomad', destination='data') # %% project.experiments.add_from_data_path( @@ -74,7 +74,7 @@ # %% experiment = project.experiments['nomad'] -experiment.linked_phases.create(id='si', scale=1.0) +experiment.linked_structures.create(structure_id='si', scale=1.0) experiment.peak.damp_q = 0.02 experiment.peak.broad_q = 0.03 experiment.peak.cutoff_q = 35.0 @@ -91,7 +91,7 @@ # %% project.structures['si'].cell.length_a.free = True project.structures['si'].atom_sites['Si'].adp_iso.free = True -experiment.linked_phases['si'].scale.free = True +experiment.linked_structures['si'].scale.free = True # %% experiment.peak.damp_q.free = True @@ -117,4 +117,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_11_si_nomad_pdf') +project.save_as(dir_path='projects/pdf-si-nomad') diff --git a/docs/docs/tutorials/ed-23.ipynb b/docs/docs/tutorials/refine-cosio-d20-tscan-resumed.ipynb similarity index 90% rename from docs/docs/tutorials/ed-23.ipynb rename to docs/docs/tutorials/refine-cosio-d20-tscan-resumed.ipynb index fcca74451..e41be994e 100644 --- a/docs/docs/tutorials/ed-23.ipynb +++ b/docs/docs/tutorials/refine-cosio-d20-tscan-resumed.ipynb @@ -48,7 +48,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -64,11 +64,10 @@ "id": "5", "metadata": {}, "source": [ - "### Download Project\n", + "### Locate Project\n", "\n", - "The returned path points directly to the saved project directory with\n", - "a partially completed sequential fit, including\n", - "`analysis/results.csv`." + "Download and extract the saved Co2SiO4 scan project from the\n", + "EasyDiffraction data repository." ] }, { @@ -78,7 +77,7 @@ "metadata": {}, "outputs": [], "source": [ - "project_dir = ed.download_data(id=37, destination='projects')" + "project_dir = edi.download_data('proj-cosio-d20-scan', destination='projects', overwrite=True)" ] }, { @@ -86,7 +85,10 @@ "id": "7", "metadata": {}, "source": [ - "### Load Project" + "### Load Project\n", + "\n", + "The project is downloaded into a fresh writable working directory so\n", + "resuming the sequential fit appends to its `analysis/results.csv`." ] }, { @@ -96,7 +98,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project.load(project_dir)" + "project = edi.Project.load(project_dir)" ] }, { @@ -274,7 +276,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_23_cosio_d20_scan')" + "project.save_as(dir_path='projects/refine-cosio-d20-tscan-resumed')" ] } ], diff --git a/docs/docs/tutorials/ed-23.py b/docs/docs/tutorials/refine-cosio-d20-tscan-resumed.py similarity index 81% rename from docs/docs/tutorials/ed-23.py rename to docs/docs/tutorials/refine-cosio-d20-tscan-resumed.py index 59ee1c6ef..5831bbe64 100644 --- a/docs/docs/tutorials/ed-23.py +++ b/docs/docs/tutorials/refine-cosio-d20-tscan-resumed.py @@ -11,26 +11,28 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📂 Load Project # %% [markdown] -# ### Download Project +# ### Locate Project # -# The returned path points directly to the saved project directory with -# a partially completed sequential fit, including -# `analysis/results.csv`. +# Download and extract the saved Co2SiO4 scan project from the +# EasyDiffraction data repository. # %% -project_dir = ed.download_data(id=37, destination='projects') +project_dir = edi.download_data('proj-cosio-d20-scan', destination='projects', overwrite=True) # %% [markdown] # ### Load Project +# +# The project is downloaded into a fresh writable working directory so +# resuming the sequential fit appends to its `analysis/results.csv`. # %% -project = ed.Project.load(project_dir) +project = edi.Project.load(project_dir) # %% [markdown] # ## 🚀 Perform Analysis @@ -107,4 +109,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_23_cosio_d20_scan') +project.save_as(dir_path='projects/refine-cosio-d20-tscan-resumed') diff --git a/docs/docs/tutorials/ed-17.ipynb b/docs/docs/tutorials/refine-cosio-d20-tscan.ipynb similarity index 90% rename from docs/docs/tutorials/ed-17.ipynb rename to docs/docs/tutorials/refine-cosio-d20-tscan.ipynb index 81bce1da7..f96cc57d8 100644 --- a/docs/docs/tutorials/ed-17.ipynb +++ b/docs/docs/tutorials/refine-cosio-d20-tscan.ipynb @@ -48,7 +48,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -69,7 +69,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='cosio_d20_scan')\n", + "project = edi.Project(name='cosio_d20_scan')\n", "analysis = project.analysis\n", "display = project.display" ] @@ -90,7 +90,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_17_cosio_d20_scan')" + "project.save_as(dir_path='projects/refine-cosio-d20-tscan')" ] }, { @@ -133,7 +133,7 @@ "outputs": [], "source": [ "struct.space_group.name_h_m = 'P n m a'\n", - "struct.space_group.it_coordinate_system_code = 'abc'" + "struct.space_group.coord_system_code = 'abc'" ] }, { @@ -172,7 +172,7 @@ "outputs": [], "source": [ "struct.atom_sites.create(\n", - " label='Co1',\n", + " id='Co1',\n", " type_symbol='Co',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -180,7 +180,7 @@ " adp_iso=0.3,\n", ")\n", "struct.atom_sites.create(\n", - " label='Co2',\n", + " id='Co2',\n", " type_symbol='Co',\n", " fract_x=0.279,\n", " fract_y=0.25,\n", @@ -188,7 +188,7 @@ " adp_iso=0.3,\n", ")\n", "struct.atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0.094,\n", " fract_y=0.25,\n", @@ -196,7 +196,7 @@ " adp_iso=0.34,\n", ")\n", "struct.atom_sites.create(\n", - " label='O1',\n", + " id='O1',\n", " type_symbol='O',\n", " fract_x=0.091,\n", " fract_y=0.25,\n", @@ -204,7 +204,7 @@ " adp_iso=0.63,\n", ")\n", "struct.atom_sites.create(\n", - " label='O2',\n", + " id='O2',\n", " type_symbol='O',\n", " fract_x=0.448,\n", " fract_y=0.25,\n", @@ -212,7 +212,7 @@ " adp_iso=0.59,\n", ")\n", "struct.atom_sites.create(\n", - " label='O3',\n", + " id='O3',\n", " type_symbol='O',\n", " fract_x=0.164,\n", " fract_y=0.032,\n", @@ -249,7 +249,7 @@ "\n", "For sequential fitting, we create a single template experiment from\n", "the first data file. This template defines the instrument, peak\n", - "profile, background, and linked phases that will be reused for every\n", + "profile, background, and linked structures that will be reused for every\n", "data file in the scan.\n", "\n", "### Download Data" @@ -262,7 +262,7 @@ "metadata": {}, "outputs": [], "source": [ - "zip_path = ed.download_data(id=25, destination='data')" + "zip_path = edi.download_data('meas-cosio-d20-scan-3f', destination='data')" ] }, { @@ -281,9 +281,9 @@ "outputs": [], "source": [ "scan_data_dir = 'experiments/d20_scan'\n", - "data_paths = ed.extract_data_paths_from_zip(\n", + "data_paths = edi.extract_data_paths_from_zip(\n", " zip_path,\n", - " destination=project.info.path / scan_data_dir,\n", + " destination=project.metadata.path / scan_data_dir,\n", ")" ] }, @@ -383,20 +383,20 @@ "metadata": {}, "outputs": [], "source": [ - "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)" + "expt.background.create(id='1', position=8, intensity=609)\n", + "expt.background.create(id='2', position=9, intensity=581)\n", + "expt.background.create(id='3', position=10, intensity=563)\n", + "expt.background.create(id='4', position=11, intensity=540)\n", + "expt.background.create(id='5', position=12, intensity=520)\n", + "expt.background.create(id='6', position=15, intensity=507)\n", + "expt.background.create(id='7', position=25, intensity=463)\n", + "expt.background.create(id='8', position=30, intensity=434)\n", + "expt.background.create(id='9', position=50, intensity=451)\n", + "expt.background.create(id='10', position=70, intensity=431)\n", + "expt.background.create(id='11', position=90, intensity=414)\n", + "expt.background.create(id='12', position=110, intensity=361)\n", + "expt.background.create(id='13', position=130, intensity=292)\n", + "expt.background.create(id='14', position=150, intensity=241)" ] }, { @@ -404,7 +404,7 @@ "id": "32", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -414,7 +414,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt.linked_phases.create(id='cosio', scale=1.2)" + "expt.linked_structures.create(structure_id='cosio', scale=1.2)" ] }, { @@ -474,7 +474,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt.linked_phases['cosio'].scale.free = True\n", + "expt.linked_structures['cosio'].scale.free = True\n", "\n", "expt.instrument.calib_twotheta_offset.free = True\n", "\n", @@ -484,7 +484,7 @@ "expt.peak.broad_lorentz_y.free = True\n", "\n", "for point in expt.background:\n", - " point.y.free = True" + " point.intensity.free = True" ] }, { @@ -505,11 +505,11 @@ "outputs": [], "source": [ "analysis.aliases.create(\n", - " label='biso_Co1',\n", + " id='biso_Co1',\n", " param=struct.atom_sites['Co1'].adp_iso,\n", ")\n", "analysis.aliases.create(\n", - " label='biso_Co2',\n", + " id='biso_Co2',\n", " param=struct.atom_sites['Co2'].adp_iso,\n", ")" ] diff --git a/docs/docs/tutorials/ed-17.py b/docs/docs/tutorials/refine-cosio-d20-tscan.py similarity index 84% rename from docs/docs/tutorials/ed-17.py rename to docs/docs/tutorials/refine-cosio-d20-tscan.py index 7391e1582..0bd414cd4 100644 --- a/docs/docs/tutorials/ed-17.py +++ b/docs/docs/tutorials/refine-cosio-d20-tscan.py @@ -11,7 +11,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -20,7 +20,7 @@ # and other related components. # %% -project = ed.Project(name='cosio_d20_scan') +project = edi.Project(name='cosio_d20_scan') analysis = project.analysis display = project.display @@ -29,7 +29,7 @@ # results can be written to `analysis/results.csv`. # %% -project.save_as(dir_path='projects/ed_17_cosio_d20_scan') +project.save_as(dir_path='projects/refine-cosio-d20-tscan') # %% [markdown] # ## 🧩 Define Structure @@ -48,7 +48,7 @@ # %% struct.space_group.name_h_m = 'P n m a' -struct.space_group.it_coordinate_system_code = 'abc' +struct.space_group.coord_system_code = 'abc' # %% [markdown] # ### Set Unit Cell @@ -63,7 +63,7 @@ # %% struct.atom_sites.create( - label='Co1', + id='Co1', type_symbol='Co', fract_x=0, fract_y=0, @@ -71,7 +71,7 @@ adp_iso=0.3, ) struct.atom_sites.create( - label='Co2', + id='Co2', type_symbol='Co', fract_x=0.279, fract_y=0.25, @@ -79,7 +79,7 @@ adp_iso=0.3, ) struct.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.094, fract_y=0.25, @@ -87,7 +87,7 @@ adp_iso=0.34, ) struct.atom_sites.create( - label='O1', + id='O1', type_symbol='O', fract_x=0.091, fract_y=0.25, @@ -95,7 +95,7 @@ adp_iso=0.63, ) struct.atom_sites.create( - label='O2', + id='O2', type_symbol='O', fract_x=0.448, fract_y=0.25, @@ -103,7 +103,7 @@ adp_iso=0.59, ) struct.atom_sites.create( - label='O3', + id='O3', type_symbol='O', fract_x=0.164, fract_y=0.032, @@ -123,22 +123,22 @@ # # For sequential fitting, we create a single template experiment from # the first data file. This template defines the instrument, peak -# profile, background, and linked phases that will be reused for every +# profile, background, and linked structures that will be reused for every # data file in the scan. # # ### Download Data # %% -zip_path = ed.download_data(id=25, destination='data') +zip_path = edi.download_data('meas-cosio-d20-scan-3f', destination='data') # %% [markdown] # ### Extract Data Files # %% scan_data_dir = 'experiments/d20_scan' -data_paths = ed.extract_data_paths_from_zip( +data_paths = edi.extract_data_paths_from_zip( zip_path, - destination=project.info.path / scan_data_dir, + destination=project.metadata.path / scan_data_dir, ) # %% [markdown] @@ -178,26 +178,26 @@ # ### Set Background # %% -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) +expt.background.create(id='1', position=8, intensity=609) +expt.background.create(id='2', position=9, intensity=581) +expt.background.create(id='3', position=10, intensity=563) +expt.background.create(id='4', position=11, intensity=540) +expt.background.create(id='5', position=12, intensity=520) +expt.background.create(id='6', position=15, intensity=507) +expt.background.create(id='7', position=25, intensity=463) +expt.background.create(id='8', position=30, intensity=434) +expt.background.create(id='9', position=50, intensity=451) +expt.background.create(id='10', position=70, intensity=431) +expt.background.create(id='11', position=90, intensity=414) +expt.background.create(id='12', position=110, intensity=361) +expt.background.create(id='13', position=130, intensity=292) +expt.background.create(id='14', position=150, intensity=241) # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -expt.linked_phases.create(id='cosio', scale=1.2) +expt.linked_structures.create(structure_id='cosio', scale=1.2) # %% [markdown] # ## 🚀 Perform Analysis @@ -233,7 +233,7 @@ struct.atom_sites['O3'].adp_iso.free = True # %% -expt.linked_phases['cosio'].scale.free = True +expt.linked_structures['cosio'].scale.free = True expt.instrument.calib_twotheta_offset.free = True @@ -243,7 +243,7 @@ expt.peak.broad_lorentz_y.free = True for point in expt.background: - point.y.free = True + point.intensity.free = True # %% [markdown] # ### Set Constraints @@ -252,11 +252,11 @@ # %% analysis.aliases.create( - label='biso_Co1', + id='biso_Co1', param=struct.atom_sites['Co1'].adp_iso, ) analysis.aliases.create( - label='biso_Co2', + id='biso_Co2', param=struct.atom_sites['Co2'].adp_iso, ) diff --git a/docs/docs/tutorials/ed-5.ipynb b/docs/docs/tutorials/refine-cosio-d20.ipynb similarity index 89% rename from docs/docs/tutorials/ed-5.ipynb rename to docs/docs/tutorials/refine-cosio-d20.ipynb index a57352ba9..44f40405a 100644 --- a/docs/docs/tutorials/ed-5.ipynb +++ b/docs/docs/tutorials/refine-cosio-d20.ipynb @@ -94,7 +94,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'P n m a'\n", - "structure.space_group.it_coordinate_system_code = 'abc'" + "structure.space_group.coord_system_code = 'abc'" ] }, { @@ -133,7 +133,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Co1',\n", + " id='Co1',\n", " type_symbol='Co',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -141,7 +141,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Co2',\n", + " id='Co2',\n", " type_symbol='Co',\n", " fract_x=0.279,\n", " fract_y=0.25,\n", @@ -149,7 +149,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0.094,\n", " fract_y=0.25,\n", @@ -157,7 +157,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='O1',\n", + " id='O1',\n", " type_symbol='O',\n", " fract_x=0.091,\n", " fract_y=0.25,\n", @@ -165,7 +165,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='O2',\n", + " id='O2',\n", " type_symbol='O',\n", " fract_x=0.448,\n", " fract_y=0.25,\n", @@ -173,7 +173,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='O3',\n", + " id='O3',\n", " type_symbol='O',\n", " fract_x=0.164,\n", " fract_y=0.032,\n", @@ -202,7 +202,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = download_data(id=12, destination='data')" + "data_path = download_data('meas-cosio-d20', destination='data')" ] }, { @@ -307,20 +307,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)" + "expt.background.create(id='1', position=8, intensity=500)\n", + "expt.background.create(id='2', position=9, intensity=500)\n", + "expt.background.create(id='3', position=10, intensity=500)\n", + "expt.background.create(id='4', position=11, intensity=500)\n", + "expt.background.create(id='5', position=12, intensity=500)\n", + "expt.background.create(id='6', position=15, intensity=500)\n", + "expt.background.create(id='7', position=25, intensity=500)\n", + "expt.background.create(id='8', position=30, intensity=500)\n", + "expt.background.create(id='9', position=50, intensity=500)\n", + "expt.background.create(id='10', position=70, intensity=500)\n", + "expt.background.create(id='11', position=90, intensity=500)\n", + "expt.background.create(id='12', position=110, intensity=500)\n", + "expt.background.create(id='13', position=130, intensity=500)\n", + "expt.background.create(id='14', position=150, intensity=500)" ] }, { @@ -328,7 +328,7 @@ "id": "25", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -338,7 +338,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt.linked_phases.create(id='cosio', scale=1.0)" + "expt.linked_structures.create(structure_id='cosio', scale=1.0)" ] }, { @@ -371,7 +371,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_5_cosio_d20')" + "project.save_as(dir_path='projects/refine-cosio-d20')" ] }, { @@ -499,7 +499,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt.linked_phases['cosio'].scale.free = True\n", + "expt.linked_structures['cosio'].scale.free = True\n", "\n", "expt.instrument.calib_twotheta_offset.free = True\n", "\n", @@ -511,7 +511,7 @@ "expt.peak.asym_empir_2.free = True\n", "\n", "for point in expt.background:\n", - " point.y.free = True" + " point.intensity.free = True" ] }, { @@ -550,11 +550,11 @@ "outputs": [], "source": [ "project.analysis.aliases.create(\n", - " label='biso_Co1',\n", + " id='biso_Co1',\n", " param=project.structures['cosio'].atom_sites['Co1'].adp_iso,\n", ")\n", "project.analysis.aliases.create(\n", - " label='biso_Co2',\n", + " id='biso_Co2',\n", " param=project.structures['cosio'].atom_sites['Co2'].adp_iso,\n", ")" ] diff --git a/docs/docs/tutorials/ed-5.py b/docs/docs/tutorials/refine-cosio-d20.py similarity index 79% rename from docs/docs/tutorials/ed-5.py rename to docs/docs/tutorials/refine-cosio-d20.py index 23a091d6e..4da90a8b3 100644 --- a/docs/docs/tutorials/ed-5.py +++ b/docs/docs/tutorials/refine-cosio-d20.py @@ -33,7 +33,7 @@ # %% structure.space_group.name_h_m = 'P n m a' -structure.space_group.it_coordinate_system_code = 'abc' +structure.space_group.coord_system_code = 'abc' # %% [markdown] # ### Set Unit Cell @@ -48,7 +48,7 @@ # %% structure.atom_sites.create( - label='Co1', + id='Co1', type_symbol='Co', fract_x=0, fract_y=0, @@ -56,7 +56,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='Co2', + id='Co2', type_symbol='Co', fract_x=0.279, fract_y=0.25, @@ -64,7 +64,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.094, fract_y=0.25, @@ -72,7 +72,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='O1', + id='O1', type_symbol='O', fract_x=0.091, fract_y=0.25, @@ -80,7 +80,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='O2', + id='O2', type_symbol='O', fract_x=0.448, fract_y=0.25, @@ -88,7 +88,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='O3', + id='O3', type_symbol='O', fract_x=0.164, fract_y=0.032, @@ -105,7 +105,7 @@ # ### Download Data # %% -data_path = download_data(id=12, destination='data') +data_path = download_data('meas-cosio-d20', destination='data') # %% [markdown] # ### Create Experiment @@ -141,26 +141,26 @@ expt.background.show_supported() # %% -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) +expt.background.create(id='1', position=8, intensity=500) +expt.background.create(id='2', position=9, intensity=500) +expt.background.create(id='3', position=10, intensity=500) +expt.background.create(id='4', position=11, intensity=500) +expt.background.create(id='5', position=12, intensity=500) +expt.background.create(id='6', position=15, intensity=500) +expt.background.create(id='7', position=25, intensity=500) +expt.background.create(id='8', position=30, intensity=500) +expt.background.create(id='9', position=50, intensity=500) +expt.background.create(id='10', position=70, intensity=500) +expt.background.create(id='11', position=90, intensity=500) +expt.background.create(id='12', position=110, intensity=500) +expt.background.create(id='13', position=130, intensity=500) +expt.background.create(id='14', position=150, intensity=500) # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -expt.linked_phases.create(id='cosio', scale=1.0) +expt.linked_structures.create(structure_id='cosio', scale=1.0) # %% [markdown] # ## 📦 Define Project @@ -174,7 +174,7 @@ project = Project(name='cosio_d20') # %% -project.save_as(dir_path='projects/ed_5_cosio_d20') +project.save_as(dir_path='projects/refine-cosio-d20') # %% [markdown] # ### Add Structure @@ -228,7 +228,7 @@ atom_site.occupancy.free = True # %% -expt.linked_phases['cosio'].scale.free = True +expt.linked_structures['cosio'].scale.free = True expt.instrument.calib_twotheta_offset.free = True @@ -240,7 +240,7 @@ expt.peak.asym_empir_2.free = True for point in expt.background: - point.y.free = True + point.intensity.free = True # %% [markdown] # Show free parameters after selection. @@ -255,11 +255,11 @@ # %% project.analysis.aliases.create( - label='biso_Co1', + id='biso_Co1', param=project.structures['cosio'].atom_sites['Co1'].adp_iso, ) project.analysis.aliases.create( - label='biso_Co2', + id='biso_Co2', param=project.structures['cosio'].atom_sites['Co2'].adp_iso, ) diff --git a/docs/docs/tutorials/ed-6.ipynb b/docs/docs/tutorials/refine-hs-hrpt.ipynb similarity index 97% rename from docs/docs/tutorials/ed-6.ipynb rename to docs/docs/tutorials/refine-hs-hrpt.ipynb index d3d24bc99..8d0f7fb58 100644 --- a/docs/docs/tutorials/ed-6.ipynb +++ b/docs/docs/tutorials/refine-hs-hrpt.ipynb @@ -91,7 +91,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'R -3 m'\n", - "structure.space_group.it_coordinate_system_code = 'h'" + "structure.space_group.coord_system_code = 'h'" ] }, { @@ -131,7 +131,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Zn',\n", + " id='Zn',\n", " type_symbol='Zn',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -139,7 +139,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Cu',\n", + " id='Cu',\n", " type_symbol='Cu',\n", " fract_x=0.5,\n", " fract_y=0,\n", @@ -147,7 +147,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='O',\n", + " id='O',\n", " type_symbol='O',\n", " fract_x=0.21,\n", " fract_y=-0.21,\n", @@ -155,7 +155,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Cl',\n", + " id='Cl',\n", " type_symbol='Cl',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -163,7 +163,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='H',\n", + " id='H',\n", " type_symbol='2H',\n", " fract_x=0.13,\n", " fract_y=-0.13,\n", @@ -192,7 +192,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = download_data(id=11, destination='data')" + "data_path = download_data('meas-hs-hrpt', destination='data')" ] }, { @@ -279,7 +279,7 @@ "id": "22", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -289,7 +289,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt.linked_phases.create(id='hs', scale=0.5)" + "expt.linked_structures.create(structure_id='hs', scale=0.5)" ] }, { @@ -423,7 +423,7 @@ "structure.cell.length_a.free = True\n", "structure.cell.length_c.free = True\n", "\n", - "expt.linked_phases['hs'].scale.free = True\n", + "expt.linked_structures['hs'].scale.free = True\n", "expt.instrument.calib_twotheta_offset.free = True" ] }, @@ -524,7 +524,7 @@ "expt.peak.broad_lorentz_y.free = True\n", "\n", "for point in expt.background:\n", - " point.y.free = True" + " point.intensity.free = True" ] }, { @@ -838,7 +838,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_6_hs_hrpt')" + "project.save_as(dir_path='projects/refine-hs-hrpt')" ] } ], diff --git a/docs/docs/tutorials/ed-6.py b/docs/docs/tutorials/refine-hs-hrpt.py similarity index 93% rename from docs/docs/tutorials/ed-6.py rename to docs/docs/tutorials/refine-hs-hrpt.py index 995ff0a50..a735e6d69 100644 --- a/docs/docs/tutorials/ed-6.py +++ b/docs/docs/tutorials/refine-hs-hrpt.py @@ -30,7 +30,7 @@ # %% structure.space_group.name_h_m = 'R -3 m' -structure.space_group.it_coordinate_system_code = 'h' +structure.space_group.coord_system_code = 'h' # %% [markdown] # ### Set Unit Cell @@ -45,7 +45,7 @@ # %% structure.atom_sites.create( - label='Zn', + id='Zn', type_symbol='Zn', fract_x=0, fract_y=0, @@ -53,7 +53,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='Cu', + id='Cu', type_symbol='Cu', fract_x=0.5, fract_y=0, @@ -61,7 +61,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0.21, fract_y=-0.21, @@ -69,7 +69,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='Cl', + id='Cl', type_symbol='Cl', fract_x=0, fract_y=0, @@ -77,7 +77,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='H', + id='H', type_symbol='2H', fract_x=0.13, fract_y=-0.13, @@ -94,7 +94,7 @@ # ### Download Data # %% -data_path = download_data(id=11, destination='data') +data_path = download_data('meas-hs-hrpt', destination='data') # %% [markdown] # ### Create Experiment @@ -128,10 +128,10 @@ expt.background.auto_estimate() # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -expt.linked_phases.create(id='hs', scale=0.5) +expt.linked_structures.create(structure_id='hs', scale=0.5) # %% [markdown] # ## 📦 Define Project @@ -186,7 +186,7 @@ structure.cell.length_a.free = True structure.cell.length_c.free = True -expt.linked_phases['hs'].scale.free = True +expt.linked_structures['hs'].scale.free = True expt.instrument.calib_twotheta_offset.free = True # %% [markdown] @@ -225,7 +225,7 @@ expt.peak.broad_lorentz_y.free = True for point in expt.background: - point.y.free = True + point.intensity.free = True # %% [markdown] # Show free parameters after selection. @@ -341,4 +341,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_6_hs_hrpt') +project.save_as(dir_path='projects/refine-hs-hrpt') diff --git a/docs/docs/tutorials/ed-1.ipynb b/docs/docs/tutorials/refine-lbco-hrpt-from-cif.ipynb similarity index 95% rename from docs/docs/tutorials/ed-1.ipynb rename to docs/docs/tutorials/refine-lbco-hrpt-from-cif.ipynb index 0ca03392d..bd7b3d06a 100644 --- a/docs/docs/tutorials/ed-1.ipynb +++ b/docs/docs/tutorials/refine-lbco-hrpt-from-cif.ipynb @@ -57,7 +57,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -76,7 +76,7 @@ "outputs": [], "source": [ "# Create a minimal project with a short name\n", - "project = ed.Project(name='lbco_hrpt')" + "project = edi.Project(name='lbco_hrpt')" ] }, { @@ -95,7 +95,7 @@ "outputs": [], "source": [ "# Download CIF file from repository\n", - "structure_path = ed.download_data(id=1, destination='data')" + "structure_path = edi.download_data('struct-lbco', destination='data')" ] }, { @@ -136,7 +136,7 @@ "outputs": [], "source": [ "# Download CIF file from repository\n", - "expt_path = ed.download_data(id=2, destination='data')" + "expt_path = edi.download_data('expt-lbco-hrpt', destination='data')" ] }, { @@ -223,11 +223,11 @@ "# Therefore, it is necessary to constrain them to be equal. First we\n", "# define aliases and then use them to create a constraint.\n", "project.analysis.aliases.create(\n", - " label='biso_La',\n", + " id='biso_La',\n", " param=project.structures['lbco'].atom_sites['La'].adp_iso,\n", ")\n", "project.analysis.aliases.create(\n", - " label='biso_Ba',\n", + " id='biso_Ba',\n", " param=project.structures['lbco'].atom_sites['Ba'].adp_iso,\n", ")\n", "project.analysis.constraints.create(expression='biso_Ba = biso_La')" @@ -304,7 +304,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_1_lbco_hrpt')" + "project.save_as(dir_path='projects/refine-lbco-hrpt-from-cif')" ] } ], diff --git a/docs/docs/tutorials/ed-1.py b/docs/docs/tutorials/refine-lbco-hrpt-from-cif.py similarity index 90% rename from docs/docs/tutorials/ed-1.py rename to docs/docs/tutorials/refine-lbco-hrpt-from-cif.py index 59a574582..75b27003d 100644 --- a/docs/docs/tutorials/ed-1.py +++ b/docs/docs/tutorials/refine-lbco-hrpt-from-cif.py @@ -20,21 +20,21 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project # %% # Create a minimal project with a short name -project = ed.Project(name='lbco_hrpt') +project = edi.Project(name='lbco_hrpt') # %% [markdown] # ## 🧩 Define Structure # %% # Download CIF file from repository -structure_path = ed.download_data(id=1, destination='data') +structure_path = edi.download_data('struct-lbco', destination='data') # %% # Add structure from downloaded CIF @@ -49,7 +49,7 @@ # %% # Download CIF file from repository -expt_path = ed.download_data(id=2, destination='data') +expt_path = edi.download_data('expt-lbco-hrpt', destination='data') # %% # Add experiment from downloaded CIF @@ -86,11 +86,11 @@ # Therefore, it is necessary to constrain them to be equal. First we # define aliases and then use them to create a constraint. project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=project.structures['lbco'].atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=project.structures['lbco'].atom_sites['Ba'].adp_iso, ) project.analysis.constraints.create(expression='biso_Ba = biso_La') @@ -120,4 +120,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_1_lbco_hrpt') +project.save_as(dir_path='projects/refine-lbco-hrpt-from-cif') diff --git a/docs/docs/tutorials/ed-2.ipynb b/docs/docs/tutorials/refine-lbco-hrpt-from-data.ipynb similarity index 95% rename from docs/docs/tutorials/ed-2.ipynb rename to docs/docs/tutorials/refine-lbco-hrpt-from-data.ipynb index fe6db46f2..19122a43d 100644 --- a/docs/docs/tutorials/ed-2.ipynb +++ b/docs/docs/tutorials/refine-lbco-hrpt-from-data.ipynb @@ -62,7 +62,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -80,7 +80,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='lbco_hrpt')" + "project = edi.Project(name='lbco_hrpt')" ] }, { @@ -119,7 +119,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'P m -3 m'\n", - "structure.space_group.it_coordinate_system_code = '1'" + "structure.space_group.coord_system_code = '1'" ] }, { @@ -140,7 +140,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='La',\n", + " id='La',\n", " type_symbol='La',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -149,7 +149,7 @@ " occupancy=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Ba',\n", + " id='Ba',\n", " type_symbol='Ba',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -158,7 +158,7 @@ " occupancy=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Co',\n", + " id='Co',\n", " type_symbol='Co',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -166,7 +166,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='O',\n", + " id='O',\n", " type_symbol='O',\n", " fract_x=0,\n", " fract_y=0.5,\n", @@ -200,7 +200,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=3, destination='data')" + "data_path = edi.download_data('meas-lbco-hrpt', destination='data')" ] }, { @@ -281,7 +281,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases.create(id='lbco', scale=10.0)" + "experiment.linked_structures.create(structure_id='lbco', scale=10.0)" ] }, { @@ -330,9 +330,9 @@ "experiment.peak.broad_lorentz_y.free = True\n", "\n", "for point in experiment.background:\n", - " point.y.free = True\n", + " point.intensity.free = True\n", "\n", - "experiment.linked_phases['lbco'].scale.free = True" + "experiment.linked_structures['lbco'].scale.free = True" ] }, { @@ -398,11 +398,11 @@ "# Therefore, it is necessary to constrain them to be equal. First we\n", "# define aliases and then use them to create a constraint.\n", "project.analysis.aliases.create(\n", - " label='biso_La',\n", + " id='biso_La',\n", " param=project.structures['lbco'].atom_sites['La'].adp_iso,\n", ")\n", "project.analysis.aliases.create(\n", - " label='biso_Ba',\n", + " id='biso_Ba',\n", " param=project.structures['lbco'].atom_sites['Ba'].adp_iso,\n", ")\n", "project.analysis.constraints.create(expression='biso_Ba = biso_La')" @@ -542,7 +542,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_2_lbco_hrpt')" + "project.save_as(dir_path='projects/refine-lbco-hrpt-from-data')" ] } ], diff --git a/docs/docs/tutorials/ed-2.py b/docs/docs/tutorials/refine-lbco-hrpt-from-data.py similarity index 90% rename from docs/docs/tutorials/ed-2.py rename to docs/docs/tutorials/refine-lbco-hrpt-from-data.py index 67febc7f3..62b324463 100644 --- a/docs/docs/tutorials/ed-2.py +++ b/docs/docs/tutorials/refine-lbco-hrpt-from-data.py @@ -25,13 +25,13 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project # %% -project = ed.Project(name='lbco_hrpt') +project = edi.Project(name='lbco_hrpt') # %% [markdown] # ## 🧩 Define Structure @@ -44,14 +44,14 @@ # %% structure.space_group.name_h_m = 'P m -3 m' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' # %% structure.cell.length_a = 3.88 # %% structure.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -60,7 +60,7 @@ occupancy=0.5, ) structure.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -69,7 +69,7 @@ occupancy=0.5, ) structure.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -77,7 +77,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -92,7 +92,7 @@ # ## 🔬 Define Experiment # %% -data_path = ed.download_data(id=3, destination='data') +data_path = edi.download_data('meas-lbco-hrpt', destination='data') # %% project.experiments.add_from_data_path( @@ -124,7 +124,7 @@ experiment.background.auto_estimate() # %% -experiment.linked_phases.create(id='lbco', scale=10.0) +experiment.linked_structures.create(structure_id='lbco', scale=10.0) # %% [markdown] # ## 🚀 Perform Analysis @@ -149,9 +149,9 @@ experiment.peak.broad_lorentz_y.free = True for point in experiment.background: - point.y.free = True + point.intensity.free = True -experiment.linked_phases['lbco'].scale.free = True +experiment.linked_structures['lbco'].scale.free = True # %% project.analysis.fit() @@ -177,11 +177,11 @@ # Therefore, it is necessary to constrain them to be equal. First we # define aliases and then use them to create a constraint. project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=project.structures['lbco'].atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=project.structures['lbco'].atom_sites['Ba'].adp_iso, ) project.analysis.constraints.create(expression='biso_Ba = biso_La') @@ -227,4 +227,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_2_lbco_hrpt') +project.save_as(dir_path='projects/refine-lbco-hrpt-from-data') diff --git a/docs/docs/tutorials/ed-3.ipynb b/docs/docs/tutorials/refine-lbco-hrpt-report.ipynb similarity index 94% rename from docs/docs/tutorials/ed-3.ipynb rename to docs/docs/tutorials/refine-lbco-hrpt-report.ipynb index 2ed3d57e0..e10adb567 100644 --- a/docs/docs/tutorials/ed-3.ipynb +++ b/docs/docs/tutorials/refine-lbco-hrpt-report.ipynb @@ -59,7 +59,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -87,7 +87,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='lbco_hrpt')" + "project = edi.Project(name='lbco_hrpt')" ] }, { @@ -105,8 +105,8 @@ "metadata": {}, "outputs": [], "source": [ - "project.info.title = 'La0.5Ba0.5CoO3 at HRPT@PSI'\n", - "project.info.description = \"\"\"This project demonstrates a standard\n", + "project.metadata.title = 'La0.5Ba0.5CoO3 at HRPT@PSI'\n", + "project.metadata.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).\"\"\"" @@ -117,7 +117,7 @@ "id": "9", "metadata": {}, "source": [ - "### Show Project Metadata as CIF" + "### Show Project Metadata as Text" ] }, { @@ -127,7 +127,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.info.show_as_cif()" + "project.metadata.show_as_text()" ] }, { @@ -148,7 +148,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_3_lbco_hrpt')" + "project.save_as(dir_path='projects/refine-lbco-hrpt-report')" ] }, { @@ -221,7 +221,7 @@ "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'" + "project.structures['lbco'].space_group.coord_system_code = '1'" ] }, { @@ -262,7 +262,7 @@ "outputs": [], "source": [ "project.structures['lbco'].atom_sites.create(\n", - " label='La',\n", + " id='La',\n", " type_symbol='La',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -271,7 +271,7 @@ " occupancy=0.5,\n", ")\n", "project.structures['lbco'].atom_sites.create(\n", - " label='Ba',\n", + " id='Ba',\n", " type_symbol='Ba',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -280,7 +280,7 @@ " occupancy=0.5,\n", ")\n", "project.structures['lbco'].atom_sites.create(\n", - " label='Co',\n", + " id='Co',\n", " type_symbol='Co',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -288,7 +288,7 @@ " adp_iso=0.5,\n", ")\n", "project.structures['lbco'].atom_sites.create(\n", - " label='O',\n", + " id='O',\n", " type_symbol='O',\n", " fract_x=0,\n", " fract_y=0.5,\n", @@ -302,7 +302,7 @@ "id": "24", "metadata": {}, "source": [ - "### Show Structure as CIF" + "### Show Structure as Text" ] }, { @@ -312,7 +312,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.structures['lbco'].show_as_cif()" + "project.structures['lbco'].show_as_text()" ] }, { @@ -404,7 +404,7 @@ "outputs": [], "source": [ "project.structures['lbco'].geom.min_bond_distance_cutoff = 0.5\n", - "project.structures['lbco'].geom.bond_distance_incr = 0.25" + "project.structures['lbco'].geom.bond_distance_inc = 0.25" ] }, { @@ -534,7 +534,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = ed.download_data(id=3, destination='data')" + "data_path = edi.download_data('meas-lbco-hrpt', destination='data')" ] }, { @@ -737,11 +737,11 @@ "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)" + "project.experiments['hrpt'].background.create(id='10', position=10, intensity=170)\n", + "project.experiments['hrpt'].background.create(id='30', position=30, intensity=170)\n", + "project.experiments['hrpt'].background.create(id='50', position=50, intensity=170)\n", + "project.experiments['hrpt'].background.create(id='110', position=110, intensity=170)\n", + "project.experiments['hrpt'].background.create(id='165', position=165, intensity=170)" ] }, { @@ -767,7 +767,7 @@ "id": "71", "metadata": {}, "source": [ - "### Set Linked Phases\n", + "### Set Linked Structures\n", "\n", "Link the structure defined in the previous step to the experiment." ] @@ -779,7 +779,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['hrpt'].linked_phases.create(id='lbco', scale=10.0)" + "project.experiments['hrpt'].linked_structures.create(structure_id='lbco', scale=10.0)" ] }, { @@ -787,7 +787,7 @@ "id": "73", "metadata": {}, "source": [ - "### Show Experiment as CIF" + "### Show Experiment as Text" ] }, { @@ -797,7 +797,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['hrpt'].show_as_cif()" + "project.experiments['hrpt'].show_as_text()" ] }, { @@ -1116,13 +1116,13 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['hrpt'].linked_phases['lbco'].scale.free = True\n", + "project.experiments['hrpt'].linked_structures['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" + "project.experiments['hrpt'].background['10'].intensity.free = True\n", + "project.experiments['hrpt'].background['30'].intensity.free = True\n", + "project.experiments['hrpt'].background['50'].intensity.free = True\n", + "project.experiments['hrpt'].background['110'].intensity.free = True\n", + "project.experiments['hrpt'].background['165'].intensity.free = True" ] }, { @@ -1404,11 +1404,11 @@ "outputs": [], "source": [ "project.analysis.aliases.create(\n", - " label='biso_La',\n", + " id='biso_La',\n", " param=project.structures['lbco'].atom_sites['La'].adp_iso,\n", ")\n", "project.analysis.aliases.create(\n", - " label='biso_Ba',\n", + " id='biso_Ba',\n", " param=project.structures['lbco'].atom_sites['Ba'].adp_iso,\n", ")" ] @@ -1534,11 +1534,11 @@ "outputs": [], "source": [ "project.analysis.aliases.create(\n", - " label='occ_La',\n", + " id='occ_La',\n", " param=project.structures['lbco'].atom_sites['La'].occupancy,\n", ")\n", "project.analysis.aliases.create(\n", - " label='occ_Ba',\n", + " id='occ_Ba',\n", " param=project.structures['lbco'].atom_sites['Ba'].occupancy,\n", ")" ] diff --git a/docs/docs/tutorials/ed-3.py b/docs/docs/tutorials/refine-lbco-hrpt-report.py similarity index 88% rename from docs/docs/tutorials/ed-3.py rename to docs/docs/tutorials/refine-lbco-hrpt-report.py index 16af51f1c..3b6543b8d 100644 --- a/docs/docs/tutorials/ed-3.py +++ b/docs/docs/tutorials/refine-lbco-hrpt-report.py @@ -22,7 +22,7 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project @@ -33,23 +33,23 @@ # ### Create Project # %% -project = ed.Project(name='lbco_hrpt') +project = edi.Project(name='lbco_hrpt') # %% [markdown] # ### Set Project Metadata # %% -project.info.title = 'La0.5Ba0.5CoO3 at HRPT@PSI' -project.info.description = """This project demonstrates a standard +project.metadata.title = 'La0.5Ba0.5CoO3 at HRPT@PSI' +project.metadata.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).""" # %% [markdown] -# ### Show Project Metadata as CIF +# ### Show Project Metadata as Text # %% -project.info.show_as_cif() +project.metadata.show_as_text() # %% [markdown] # ### Save Project @@ -58,7 +58,7 @@ # directory path. # %% -project.save_as(dir_path='projects/ed_3_lbco_hrpt') +project.save_as(dir_path='projects/refine-lbco-hrpt-report') # %% [markdown] # ## 🧩 Define Structure @@ -90,7 +90,7 @@ # %% project.structures['lbco'].space_group.name_h_m = 'P m -3 m' -project.structures['lbco'].space_group.it_coordinate_system_code = '1' +project.structures['lbco'].space_group.coord_system_code = '1' # %% [markdown] # ### Set Unit Cell @@ -107,7 +107,7 @@ # %% project.structures['lbco'].atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -116,7 +116,7 @@ occupancy=0.5, ) project.structures['lbco'].atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -125,7 +125,7 @@ occupancy=0.5, ) project.structures['lbco'].atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -133,7 +133,7 @@ adp_iso=0.5, ) project.structures['lbco'].atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -142,10 +142,10 @@ ) # %% [markdown] -# ### Show Structure as CIF +# ### Show Structure as Text # %% -project.structures['lbco'].show_as_cif() +project.structures['lbco'].show_as_text() # %% [markdown] # ### Display Structure @@ -182,7 +182,7 @@ # %% project.structures['lbco'].geom.min_bond_distance_cutoff = 0.5 -project.structures['lbco'].geom.bond_distance_incr = 0.25 +project.structures['lbco'].geom.bond_distance_inc = 0.25 # %% [markdown] # List which features the structure data and the active engine can draw. @@ -233,7 +233,7 @@ # Download the data file from the EasyDiffraction repository on GitHub. # %% -data_path = ed.download_data(id=3, destination='data') +data_path = edi.download_data('meas-lbco-hrpt', destination='data') # %% [markdown] # ### Create Experiment @@ -311,11 +311,11 @@ # Add background points. # %% -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) +project.experiments['hrpt'].background.create(id='10', position=10, intensity=170) +project.experiments['hrpt'].background.create(id='30', position=30, intensity=170) +project.experiments['hrpt'].background.create(id='50', position=50, intensity=170) +project.experiments['hrpt'].background.create(id='110', position=110, intensity=170) +project.experiments['hrpt'].background.create(id='165', position=165, intensity=170) # %% [markdown] # Show current background points. @@ -324,18 +324,18 @@ project.experiments['hrpt'].background.show() # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # # Link the structure defined in the previous step to the experiment. # %% -project.experiments['hrpt'].linked_phases.create(id='lbco', scale=10.0) +project.experiments['hrpt'].linked_structures.create(structure_id='lbco', scale=10.0) # %% [markdown] -# ### Show Experiment as CIF +# ### Show Experiment as Text # %% -project.experiments['hrpt'].show_as_cif() +project.experiments['hrpt'].show_as_text() # %% [markdown] # ### Save Project State @@ -455,13 +455,13 @@ # Set experiment parameters to be refined. # %% -project.experiments['hrpt'].linked_phases['lbco'].scale.free = True +project.experiments['hrpt'].linked_structures['lbco'].scale.free = True project.experiments['hrpt'].instrument.calib_twotheta_offset.free = True -project.experiments['hrpt'].background['10'].y.free = True -project.experiments['hrpt'].background['30'].y.free = True -project.experiments['hrpt'].background['50'].y.free = True -project.experiments['hrpt'].background['110'].y.free = True -project.experiments['hrpt'].background['165'].y.free = True +project.experiments['hrpt'].background['10'].intensity.free = True +project.experiments['hrpt'].background['30'].intensity.free = True +project.experiments['hrpt'].background['50'].intensity.free = True +project.experiments['hrpt'].background['110'].intensity.free = True +project.experiments['hrpt'].background['165'].intensity.free = True # %% [markdown] # Show free parameters after selection. @@ -566,11 +566,11 @@ # %% project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=project.structures['lbco'].atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=project.structures['lbco'].atom_sites['Ba'].adp_iso, ) @@ -617,11 +617,11 @@ # %% project.analysis.aliases.create( - label='occ_La', + id='occ_La', param=project.structures['lbco'].atom_sites['La'].occupancy, ) project.analysis.aliases.create( - label='occ_Ba', + id='occ_Ba', param=project.structures['lbco'].atom_sites['Ba'].occupancy, ) diff --git a/docs/docs/tutorials/ed-9.ipynb b/docs/docs/tutorials/refine-lbco-si-mcstas.ipynb similarity index 86% rename from docs/docs/tutorials/ed-9.ipynb rename to docs/docs/tutorials/refine-lbco-si-mcstas.ipynb index 613268b88..000dd0c17 100644 --- a/docs/docs/tutorials/ed-9.ipynb +++ b/docs/docs/tutorials/refine-lbco-si-mcstas.ipynb @@ -91,7 +91,7 @@ "outputs": [], "source": [ "structure_1.space_group.name_h_m = 'P m -3 m'\n", - "structure_1.space_group.it_coordinate_system_code = '1'" + "structure_1.space_group.coord_system_code = '1'" ] }, { @@ -128,7 +128,7 @@ "outputs": [], "source": [ "structure_1.atom_sites.create(\n", - " label='La',\n", + " id='La',\n", " type_symbol='La',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -137,7 +137,7 @@ " occupancy=0.5,\n", ")\n", "structure_1.atom_sites.create(\n", - " label='Ba',\n", + " id='Ba',\n", " type_symbol='Ba',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -146,7 +146,7 @@ " occupancy=0.5,\n", ")\n", "structure_1.atom_sites.create(\n", - " label='Co',\n", + " id='Co',\n", " type_symbol='Co',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -154,7 +154,7 @@ " adp_iso=0.2567,\n", ")\n", "structure_1.atom_sites.create(\n", - " label='O',\n", + " id='O',\n", " type_symbol='O',\n", " fract_x=0,\n", " fract_y=0.5,\n", @@ -197,7 +197,7 @@ "outputs": [], "source": [ "structure_2.space_group.name_h_m = 'F d -3 m'\n", - "structure_2.space_group.it_coordinate_system_code = '2'" + "structure_2.space_group.coord_system_code = '2'" ] }, { @@ -234,7 +234,7 @@ "outputs": [], "source": [ "structure_2.atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0.0,\n", " fract_y=0.0,\n", @@ -263,7 +263,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = download_data(id=8, destination='data')" + "data_path = download_data('meas-lbco-si-mcstas', destination='data')" ] }, { @@ -328,10 +328,10 @@ "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.exp_decay_beta_0 = 0.0055\n", - "experiment.peak.exp_decay_beta_1 = 0.0041\n", - "experiment.peak.exp_rise_alpha_0 = 0\n", - "experiment.peak.exp_rise_alpha_1 = 0.0097" + "experiment.peak.decay_beta_0 = 0.0055\n", + "experiment.peak.decay_beta_1 = 0.0041\n", + "experiment.peak.rise_alpha_0 = 0\n", + "experiment.peak.rise_alpha_1 = 0.0097" ] }, { @@ -375,19 +375,19 @@ "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)" + "experiment.background.create(id='1', position=45000, intensity=0.2)\n", + "experiment.background.create(id='2', position=50000, intensity=0.2)\n", + "experiment.background.create(id='3', position=55000, intensity=0.2)\n", + "experiment.background.create(id='4', position=65000, intensity=0.2)\n", + "experiment.background.create(id='5', position=70000, intensity=0.2)\n", + "experiment.background.create(id='6', position=75000, intensity=0.2)\n", + "experiment.background.create(id='7', position=80000, intensity=0.2)\n", + "experiment.background.create(id='8', position=85000, intensity=0.2)\n", + "experiment.background.create(id='9', position=90000, intensity=0.2)\n", + "experiment.background.create(id='10', position=95000, intensity=0.2)\n", + "experiment.background.create(id='11', position=100000, intensity=0.2)\n", + "experiment.background.create(id='12', position=105000, intensity=0.2)\n", + "experiment.background.create(id='13', position=110000, intensity=0.2)" ] }, { @@ -395,7 +395,7 @@ "id": "33", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -405,8 +405,8 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases.create(id='lbco', scale=4.0)\n", - "experiment.linked_phases.create(id='si', scale=0.2)" + "experiment.linked_structures.create(structure_id='lbco', scale=4.0)\n", + "experiment.linked_structures.create(structure_id='si', scale=0.2)" ] }, { @@ -586,7 +586,7 @@ "id": "53", "metadata": {}, "source": [ - "Show experiment as CIF." + "Show experiment as text." ] }, { @@ -596,7 +596,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['mcstas'].show_as_cif()" + "project.experiments['mcstas'].show_as_text()" ] }, { @@ -643,19 +643,19 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lbco'].scale.free = True\n", - "experiment.linked_phases['si'].scale.free = True\n", + "experiment.linked_structures['lbco'].scale.free = True\n", + "experiment.linked_structures['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.exp_rise_alpha_1.free = True\n", - "experiment.peak.exp_decay_beta_0.free = True\n", - "experiment.peak.exp_decay_beta_1.free = True\n", + "experiment.peak.rise_alpha_1.free = True\n", + "experiment.peak.decay_beta_0.free = True\n", + "experiment.peak.decay_beta_1.free = True\n", "\n", "for point in experiment.background:\n", - " point.y.free = True" + " point.intensity.free = True" ] }, { @@ -711,7 +711,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_9_lbco_si_mcstas')" + "project.save_as(dir_path='projects/refine-lbco-si-mcstas')" ] } ], diff --git a/docs/docs/tutorials/ed-9.py b/docs/docs/tutorials/refine-lbco-si-mcstas.py similarity index 73% rename from docs/docs/tutorials/ed-9.py rename to docs/docs/tutorials/refine-lbco-si-mcstas.py index a869b2583..44704828e 100644 --- a/docs/docs/tutorials/ed-9.py +++ b/docs/docs/tutorials/refine-lbco-si-mcstas.py @@ -30,7 +30,7 @@ # %% structure_1.space_group.name_h_m = 'P m -3 m' -structure_1.space_group.it_coordinate_system_code = '1' +structure_1.space_group.coord_system_code = '1' # %% [markdown] # #### Set Unit Cell @@ -43,7 +43,7 @@ # %% structure_1.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -52,7 +52,7 @@ occupancy=0.5, ) structure_1.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -61,7 +61,7 @@ occupancy=0.5, ) structure_1.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -69,7 +69,7 @@ adp_iso=0.2567, ) structure_1.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -88,7 +88,7 @@ # %% structure_2.space_group.name_h_m = 'F d -3 m' -structure_2.space_group.it_coordinate_system_code = '2' +structure_2.space_group.coord_system_code = '2' # %% [markdown] # #### Set Unit Cell @@ -101,7 +101,7 @@ # %% structure_2.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.0, fract_y=0.0, @@ -118,7 +118,7 @@ # ### Download Data # %% -data_path = download_data(id=8, destination='data') +data_path = download_data('meas-lbco-si-mcstas', destination='data') # %% [markdown] # ### Create Experiment @@ -147,10 +147,10 @@ experiment.peak.broad_gauss_sigma_0 = 45137 experiment.peak.broad_gauss_sigma_1 = -52394 experiment.peak.broad_gauss_sigma_2 = 22998 -experiment.peak.exp_decay_beta_0 = 0.0055 -experiment.peak.exp_decay_beta_1 = 0.0041 -experiment.peak.exp_rise_alpha_0 = 0 -experiment.peak.exp_rise_alpha_1 = 0.0097 +experiment.peak.decay_beta_0 = 0.0055 +experiment.peak.decay_beta_1 = 0.0041 +experiment.peak.rise_alpha_0 = 0 +experiment.peak.rise_alpha_1 = 0.0097 # %% [markdown] # ### Set Background @@ -165,26 +165,26 @@ # Add background points. # %% -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.background.create(id='1', position=45000, intensity=0.2) +experiment.background.create(id='2', position=50000, intensity=0.2) +experiment.background.create(id='3', position=55000, intensity=0.2) +experiment.background.create(id='4', position=65000, intensity=0.2) +experiment.background.create(id='5', position=70000, intensity=0.2) +experiment.background.create(id='6', position=75000, intensity=0.2) +experiment.background.create(id='7', position=80000, intensity=0.2) +experiment.background.create(id='8', position=85000, intensity=0.2) +experiment.background.create(id='9', position=90000, intensity=0.2) +experiment.background.create(id='10', position=95000, intensity=0.2) +experiment.background.create(id='11', position=100000, intensity=0.2) +experiment.background.create(id='12', position=105000, intensity=0.2) +experiment.background.create(id='13', position=110000, intensity=0.2) + +# %% [markdown] +# ### Set Linked Structures # %% -experiment.linked_phases.create(id='lbco', scale=4.0) -experiment.linked_phases.create(id='si', scale=0.2) +experiment.linked_structures.create(structure_id='lbco', scale=4.0) +experiment.linked_structures.create(structure_id='si', scale=0.2) # %% [markdown] # ## 📦 Define Project @@ -251,10 +251,10 @@ project.display.pattern(expt_name='mcstas') # %% [markdown] -# Show experiment as CIF. +# Show experiment as text. # %% -project.experiments['mcstas'].show_as_cif() +project.experiments['mcstas'].show_as_text() # %% [markdown] # ## 🚀 Perform Analysis @@ -277,19 +277,19 @@ # Set experiment parameters to be optimized. # %% -experiment.linked_phases['lbco'].scale.free = True -experiment.linked_phases['si'].scale.free = True +experiment.linked_structures['lbco'].scale.free = True +experiment.linked_structures['si'].scale.free = True experiment.peak.broad_gauss_sigma_0.free = True experiment.peak.broad_gauss_sigma_1.free = True experiment.peak.broad_gauss_sigma_2.free = True -experiment.peak.exp_rise_alpha_1.free = True -experiment.peak.exp_decay_beta_0.free = True -experiment.peak.exp_decay_beta_1.free = True +experiment.peak.rise_alpha_1.free = True +experiment.peak.decay_beta_0.free = True +experiment.peak.decay_beta_1.free = True for point in experiment.background: - point.y.free = True + point.intensity.free = True # %% [markdown] # ### Run Fitting @@ -309,4 +309,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_9_lbco_si_mcstas') +project.save_as(dir_path='projects/refine-lbco-si-mcstas') diff --git a/docs/docs/tutorials/ed-8.ipynb b/docs/docs/tutorials/refine-ncaf-wish.ipynb similarity index 90% rename from docs/docs/tutorials/ed-8.ipynb rename to docs/docs/tutorials/refine-ncaf-wish.ipynb index 097ac6f57..f184b8ab5 100644 --- a/docs/docs/tutorials/ed-8.ipynb +++ b/docs/docs/tutorials/refine-ncaf-wish.ipynb @@ -94,7 +94,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'I 21 3'\n", - "structure.space_group.it_coordinate_system_code = '1'" + "structure.space_group.coord_system_code = '1'" ] }, { @@ -131,7 +131,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Ca',\n", + " id='Ca',\n", " type_symbol='Ca',\n", " fract_x=0.4663,\n", " fract_y=0.0,\n", @@ -139,7 +139,7 @@ " adp_iso=0.92,\n", ")\n", "structure.atom_sites.create(\n", - " label='Al',\n", + " id='Al',\n", " type_symbol='Al',\n", " fract_x=0.2521,\n", " fract_y=0.2521,\n", @@ -147,7 +147,7 @@ " adp_iso=0.73,\n", ")\n", "structure.atom_sites.create(\n", - " label='Na',\n", + " id='Na',\n", " type_symbol='Na',\n", " fract_x=0.0851,\n", " fract_y=0.0851,\n", @@ -155,7 +155,7 @@ " adp_iso=2.08,\n", ")\n", "structure.atom_sites.create(\n", - " label='F1',\n", + " id='F1',\n", " type_symbol='F',\n", " fract_x=0.1377,\n", " fract_y=0.3054,\n", @@ -163,7 +163,7 @@ " adp_iso=0.90,\n", ")\n", "structure.atom_sites.create(\n", - " label='F2',\n", + " id='F2',\n", " type_symbol='F',\n", " fract_x=0.3625,\n", " fract_y=0.3633,\n", @@ -171,7 +171,7 @@ " adp_iso=1.37,\n", ")\n", "structure.atom_sites.create(\n", - " label='F3',\n", + " id='F3',\n", " type_symbol='F',\n", " fract_x=0.4612,\n", " fract_y=0.4612,\n", @@ -200,7 +200,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path56 = download_data(id=9, destination='data')" + "data_path56 = download_data('meas-ncaf-wish-b56', destination='data')" ] }, { @@ -210,7 +210,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path47 = download_data(id=10, destination='data')" + "data_path47 = download_data('meas-ncaf-wish-b47', destination='data')" ] }, { @@ -267,7 +267,7 @@ "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" + "expt56.instrument.calib_d_to_tof_quadratic = -1.08308" ] }, { @@ -280,7 +280,7 @@ "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" + "expt47.instrument.calib_d_to_tof_quadratic = -0.47488" ] }, { @@ -302,10 +302,10 @@ "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.exp_decay_beta_0 = 0.007\n", - "expt56.peak.exp_decay_beta_1 = 0.01\n", - "expt56.peak.exp_rise_alpha_0 = -0.0094\n", - "expt56.peak.exp_rise_alpha_1 = 0.1" + "expt56.peak.decay_beta_0 = 0.007\n", + "expt56.peak.decay_beta_1 = 0.01\n", + "expt56.peak.rise_alpha_0 = -0.0094\n", + "expt56.peak.rise_alpha_1 = 0.1" ] }, { @@ -318,10 +318,10 @@ "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.exp_decay_beta_0 = 0.006\n", - "expt47.peak.exp_decay_beta_1 = 0.015\n", - "expt47.peak.exp_rise_alpha_0 = -0.0115\n", - "expt47.peak.exp_rise_alpha_1 = 0.1" + "expt47.peak.decay_beta_0 = 0.006\n", + "expt47.peak.decay_beta_1 = 0.015\n", + "expt47.peak.rise_alpha_0 = -0.0115\n", + "expt47.peak.rise_alpha_1 = 0.1" ] }, { @@ -374,7 +374,7 @@ " ],\n", " start=1,\n", "):\n", - " expt56.background.create(id=str(idx), x=x, y=y)" + " expt56.background.create(id=str(idx), position=x, intensity=y)" ] }, { @@ -417,7 +417,7 @@ " ],\n", " start=1,\n", "):\n", - " expt47.background.create(id=str(idx), x=x, y=y)" + " expt47.background.create(id=str(idx), position=x, intensity=y)" ] }, { @@ -425,7 +425,7 @@ "id": "27", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -435,7 +435,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt56.linked_phases.create(id='ncaf', scale=1.0)" + "expt56.linked_structures.create(structure_id='ncaf', scale=1.0)" ] }, { @@ -445,7 +445,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt47.linked_phases.create(id='ncaf', scale=2.0)" + "expt47.linked_structures.create(structure_id='ncaf', scale=2.0)" ] }, { @@ -592,21 +592,21 @@ "metadata": {}, "outputs": [], "source": [ - "expt56.linked_phases['ncaf'].scale.free = True\n", + "expt56.linked_structures['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.exp_decay_beta_0.free = True\n", - "expt56.peak.exp_decay_beta_1.free = True\n", - "expt56.peak.exp_rise_alpha_1.free = True\n", + "expt56.peak.decay_beta_0.free = True\n", + "expt56.peak.decay_beta_1.free = True\n", + "expt56.peak.rise_alpha_1.free = True\n", "\n", - "expt47.linked_phases['ncaf'].scale.free = True\n", + "expt47.linked_structures['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.exp_decay_beta_0.free = True\n", - "expt47.peak.exp_decay_beta_1.free = True\n", - "expt47.peak.exp_rise_alpha_1.free = True" + "expt47.peak.decay_beta_0.free = True\n", + "expt47.peak.decay_beta_1.free = True\n", + "expt47.peak.rise_alpha_1.free = True" ] }, { @@ -729,7 +729,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_8_ncaf_wish')" + "project.save_as(dir_path='projects/refine-ncaf-wish')" ] } ], diff --git a/docs/docs/tutorials/ed-8.py b/docs/docs/tutorials/refine-ncaf-wish.py similarity index 83% rename from docs/docs/tutorials/ed-8.py rename to docs/docs/tutorials/refine-ncaf-wish.py index 94c0a89d5..efd65a16a 100644 --- a/docs/docs/tutorials/ed-8.py +++ b/docs/docs/tutorials/refine-ncaf-wish.py @@ -33,7 +33,7 @@ # %% structure.space_group.name_h_m = 'I 21 3' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' # %% [markdown] # ### Set Unit Cell @@ -46,7 +46,7 @@ # %% structure.atom_sites.create( - label='Ca', + id='Ca', type_symbol='Ca', fract_x=0.4663, fract_y=0.0, @@ -54,7 +54,7 @@ adp_iso=0.92, ) structure.atom_sites.create( - label='Al', + id='Al', type_symbol='Al', fract_x=0.2521, fract_y=0.2521, @@ -62,7 +62,7 @@ adp_iso=0.73, ) structure.atom_sites.create( - label='Na', + id='Na', type_symbol='Na', fract_x=0.0851, fract_y=0.0851, @@ -70,7 +70,7 @@ adp_iso=2.08, ) structure.atom_sites.create( - label='F1', + id='F1', type_symbol='F', fract_x=0.1377, fract_y=0.3054, @@ -78,7 +78,7 @@ adp_iso=0.90, ) structure.atom_sites.create( - label='F2', + id='F2', type_symbol='F', fract_x=0.3625, fract_y=0.3633, @@ -86,7 +86,7 @@ adp_iso=1.37, ) structure.atom_sites.create( - label='F3', + id='F3', type_symbol='F', fract_x=0.4612, fract_y=0.4612, @@ -103,10 +103,10 @@ # ### Download Data # %% -data_path56 = download_data(id=9, destination='data') +data_path56 = download_data('meas-ncaf-wish-b56', destination='data') # %% -data_path47 = download_data(id=10, destination='data') +data_path47 = download_data('meas-ncaf-wish-b47', destination='data') # %% [markdown] # ### Create Experiment @@ -132,13 +132,13 @@ expt56.instrument.setup_twotheta_bank = 152.827 expt56.instrument.calib_d_to_tof_offset = -13.5 expt56.instrument.calib_d_to_tof_linear = 20773.0 -expt56.instrument.calib_d_to_tof_quad = -1.08308 +expt56.instrument.calib_d_to_tof_quadratic = -1.08308 # %% expt47.instrument.setup_twotheta_bank = 121.660 expt47.instrument.calib_d_to_tof_offset = -15.0 expt47.instrument.calib_d_to_tof_linear = 18660.0 -expt47.instrument.calib_d_to_tof_quad = -0.47488 +expt47.instrument.calib_d_to_tof_quadratic = -0.47488 # %% [markdown] # ### Set Peak Profile @@ -148,19 +148,19 @@ expt56.peak.broad_gauss_sigma_0 = 0.0 expt56.peak.broad_gauss_sigma_1 = 0.0 expt56.peak.broad_gauss_sigma_2 = 15.5 -expt56.peak.exp_decay_beta_0 = 0.007 -expt56.peak.exp_decay_beta_1 = 0.01 -expt56.peak.exp_rise_alpha_0 = -0.0094 -expt56.peak.exp_rise_alpha_1 = 0.1 +expt56.peak.decay_beta_0 = 0.007 +expt56.peak.decay_beta_1 = 0.01 +expt56.peak.rise_alpha_0 = -0.0094 +expt56.peak.rise_alpha_1 = 0.1 # %% expt47.peak.broad_gauss_sigma_0 = 0.0 expt47.peak.broad_gauss_sigma_1 = 29.8 expt47.peak.broad_gauss_sigma_2 = 18.0 -expt47.peak.exp_decay_beta_0 = 0.006 -expt47.peak.exp_decay_beta_1 = 0.015 -expt47.peak.exp_rise_alpha_0 = -0.0115 -expt47.peak.exp_rise_alpha_1 = 0.1 +expt47.peak.decay_beta_0 = 0.006 +expt47.peak.decay_beta_1 = 0.015 +expt47.peak.rise_alpha_0 = -0.0115 +expt47.peak.rise_alpha_1 = 0.1 # %% [markdown] # ### Set Background @@ -201,7 +201,7 @@ ], start=1, ): - expt56.background.create(id=str(idx), x=x, y=y) + expt56.background.create(id=str(idx), position=x, intensity=y) # %% expt47.background.type = 'line-segment' @@ -237,16 +237,16 @@ ], start=1, ): - expt47.background.create(id=str(idx), x=x, y=y) + expt47.background.create(id=str(idx), position=x, intensity=y) # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -expt56.linked_phases.create(id='ncaf', scale=1.0) +expt56.linked_structures.create(structure_id='ncaf', scale=1.0) # %% -expt47.linked_phases.create(id='ncaf', scale=2.0) +expt47.linked_structures.create(structure_id='ncaf', scale=2.0) # %% [markdown] # ### Set Excluded Regions @@ -307,21 +307,21 @@ structure.atom_sites['F3'].adp_iso.free = True # %% -expt56.linked_phases['ncaf'].scale.free = True +expt56.linked_structures['ncaf'].scale.free = True expt56.instrument.calib_d_to_tof_offset.free = True expt56.instrument.calib_d_to_tof_linear.free = True expt56.peak.broad_gauss_sigma_2.free = True -expt56.peak.exp_decay_beta_0.free = True -expt56.peak.exp_decay_beta_1.free = True -expt56.peak.exp_rise_alpha_1.free = True +expt56.peak.decay_beta_0.free = True +expt56.peak.decay_beta_1.free = True +expt56.peak.rise_alpha_1.free = True -expt47.linked_phases['ncaf'].scale.free = True +expt47.linked_structures['ncaf'].scale.free = True expt47.instrument.calib_d_to_tof_linear.free = True expt47.instrument.calib_d_to_tof_offset.free = True expt47.peak.broad_gauss_sigma_2.free = True -expt47.peak.exp_decay_beta_0.free = True -expt47.peak.exp_decay_beta_1.free = True -expt47.peak.exp_rise_alpha_1.free = True +expt47.peak.decay_beta_0.free = True +expt47.peak.decay_beta_1.free = True +expt47.peak.rise_alpha_1.free = True # %% [markdown] # ### Display Structure @@ -365,4 +365,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_8_ncaf_wish') +project.save_as(dir_path='projects/refine-ncaf-wish') diff --git a/docs/docs/tutorials/ed-4.ipynb b/docs/docs/tutorials/refine-pbso4-joint.ipynb similarity index 95% rename from docs/docs/tutorials/ed-4.ipynb rename to docs/docs/tutorials/refine-pbso4-joint.ipynb index 8f7797640..bea6450d3 100644 --- a/docs/docs/tutorials/ed-4.ipynb +++ b/docs/docs/tutorials/refine-pbso4-joint.ipynb @@ -137,7 +137,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Pb',\n", + " id='Pb',\n", " type_symbol='Pb',\n", " fract_x=0.1876,\n", " fract_y=0.25,\n", @@ -145,7 +145,7 @@ " adp_iso=1.37,\n", ")\n", "structure.atom_sites.create(\n", - " label='S',\n", + " id='S',\n", " type_symbol='S',\n", " fract_x=0.0654,\n", " fract_y=0.25,\n", @@ -153,7 +153,7 @@ " adp_iso=0.3777,\n", ")\n", "structure.atom_sites.create(\n", - " label='O1',\n", + " id='O1',\n", " type_symbol='O',\n", " fract_x=0.9082,\n", " fract_y=0.25,\n", @@ -161,7 +161,7 @@ " adp_iso=1.9764,\n", ")\n", "structure.atom_sites.create(\n", - " label='O2',\n", + " id='O2',\n", " type_symbol='O',\n", " fract_x=0.1935,\n", " fract_y=0.25,\n", @@ -169,7 +169,7 @@ " adp_iso=1.4456,\n", ")\n", "structure.atom_sites.create(\n", - " label='O3',\n", + " id='O3',\n", " type_symbol='O',\n", " fract_x=0.0811,\n", " fract_y=0.0272,\n", @@ -200,7 +200,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path1 = download_data(id=13, destination='data')" + "data_path1 = download_data('meas-pbso4-d1a', destination='data')" ] }, { @@ -317,7 +317,7 @@ " ('7', 120.0, 244.4525),\n", " ('8', 153.0, 226.0595),\n", "]:\n", - " expt1.background.create(id=id, x=x, y=y)" + " expt1.background.create(id=id, position=x, intensity=y)" ] }, { @@ -325,7 +325,7 @@ "id": "25", "metadata": {}, "source": [ - "#### Set Linked Phases" + "#### Set Linked Structures" ] }, { @@ -335,7 +335,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt1.linked_phases.create(id='pbso4', scale=1.5)" + "expt1.linked_structures.create(structure_id='pbso4', scale=1.5)" ] }, { @@ -355,7 +355,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path2 = download_data(id=16, destination='data')" + "data_path2 = download_data('meas-pbso4-xray', destination='data')" ] }, { @@ -478,7 +478,7 @@ "id": "40", "metadata": {}, "source": [ - "#### Set Linked Phases" + "#### Set Linked Structures" ] }, { @@ -488,7 +488,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt2.linked_phases.create(id='pbso4', scale=0.001)" + "expt2.linked_structures.create(structure_id='pbso4', scale=0.001)" ] }, { @@ -629,7 +629,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt1.linked_phases['pbso4'].scale.free = True\n", + "expt1.linked_structures['pbso4'].scale.free = True\n", "\n", "expt1.instrument.calib_twotheta_offset.free = True\n", "\n", @@ -646,7 +646,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt2.linked_phases['pbso4'].scale.free = True\n", + "expt2.linked_structures['pbso4'].scale.free = True\n", "\n", "expt2.instrument.calib_twotheta_offset.free = True\n", "\n", @@ -728,7 +728,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_4_pbso4_joint')" + "project.save_as(dir_path='projects/refine-pbso4-joint')" ] } ], diff --git a/docs/docs/tutorials/ed-4.py b/docs/docs/tutorials/refine-pbso4-joint.py similarity index 90% rename from docs/docs/tutorials/ed-4.py rename to docs/docs/tutorials/refine-pbso4-joint.py index 1ab7a4416..75da5f862 100644 --- a/docs/docs/tutorials/ed-4.py +++ b/docs/docs/tutorials/refine-pbso4-joint.py @@ -50,7 +50,7 @@ # %% structure.atom_sites.create( - label='Pb', + id='Pb', type_symbol='Pb', fract_x=0.1876, fract_y=0.25, @@ -58,7 +58,7 @@ adp_iso=1.37, ) structure.atom_sites.create( - label='S', + id='S', type_symbol='S', fract_x=0.0654, fract_y=0.25, @@ -66,7 +66,7 @@ adp_iso=0.3777, ) structure.atom_sites.create( - label='O1', + id='O1', type_symbol='O', fract_x=0.9082, fract_y=0.25, @@ -74,7 +74,7 @@ adp_iso=1.9764, ) structure.atom_sites.create( - label='O2', + id='O2', type_symbol='O', fract_x=0.1935, fract_y=0.25, @@ -82,7 +82,7 @@ adp_iso=1.4456, ) structure.atom_sites.create( - label='O3', + id='O3', type_symbol='O', fract_x=0.0811, fract_y=0.0272, @@ -102,7 +102,7 @@ # #### Download Data # %% -data_path1 = download_data(id=13, destination='data') +data_path1 = download_data('meas-pbso4-d1a', destination='data') # %% [markdown] # #### Create Experiment @@ -154,13 +154,13 @@ ('7', 120.0, 244.4525), ('8', 153.0, 226.0595), ]: - expt1.background.create(id=id, x=x, y=y) + expt1.background.create(id=id, position=x, intensity=y) # %% [markdown] -# #### Set Linked Phases +# #### Set Linked Structures # %% -expt1.linked_phases.create(id='pbso4', scale=1.5) +expt1.linked_structures.create(structure_id='pbso4', scale=1.5) # %% [markdown] # ### Experiment 2: xrd @@ -168,7 +168,7 @@ # #### Download Data # %% -data_path2 = download_data(id=16, destination='data') +data_path2 = download_data('meas-pbso4-xray', destination='data') # %% [markdown] # #### Create Experiment @@ -221,10 +221,10 @@ expt2.background.create(id=id, order=x, coef=y) # %% [markdown] -# #### Set Linked Phases +# #### Set Linked Structures # %% -expt2.linked_phases.create(id='pbso4', scale=0.001) +expt2.linked_structures.create(structure_id='pbso4', scale=0.001) # %% [markdown] # ## 📦 Define Project @@ -281,7 +281,7 @@ # Set experiment parameters to be optimized. # %% -expt1.linked_phases['pbso4'].scale.free = True +expt1.linked_structures['pbso4'].scale.free = True expt1.instrument.calib_twotheta_offset.free = True @@ -291,7 +291,7 @@ expt1.peak.broad_lorentz_y.free = True # %% -expt2.linked_phases['pbso4'].scale.free = True +expt2.linked_structures['pbso4'].scale.free = True expt2.instrument.calib_twotheta_offset.free = True @@ -325,4 +325,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_4_pbso4_joint') +project.save_as(dir_path='projects/refine-pbso4-joint') diff --git a/docs/docs/tutorials/ed-7.ipynb b/docs/docs/tutorials/refine-si-sepd.ipynb similarity index 93% rename from docs/docs/tutorials/ed-7.ipynb rename to docs/docs/tutorials/refine-si-sepd.ipynb index 5f5ae0b00..d55544bf7 100644 --- a/docs/docs/tutorials/ed-7.ipynb +++ b/docs/docs/tutorials/refine-si-sepd.ipynb @@ -93,7 +93,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'F d -3 m'\n", - "structure.space_group.it_coordinate_system_code = '2'" + "structure.space_group.coord_system_code = '2'" ] }, { @@ -130,7 +130,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0.125,\n", " fract_y=0.125,\n", @@ -159,7 +159,7 @@ "metadata": {}, "outputs": [], "source": [ - "data_path = download_data(id=7, destination='data')" + "data_path = download_data('meas-si-sepd', destination='data')" ] }, { @@ -200,7 +200,7 @@ "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" + "expt.instrument.calib_d_to_tof_quadratic = -1.54" ] }, { @@ -222,10 +222,10 @@ "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.exp_decay_beta_0 = 0.04221\n", - "expt.peak.exp_decay_beta_1 = 0.00946\n", - "expt.peak.exp_rise_alpha_0 = 0.0\n", - "expt.peak.exp_rise_alpha_1 = 0.5971" + "expt.peak.decay_beta_0 = 0.04221\n", + "expt.peak.decay_beta_1 = 0.00946\n", + "expt.peak.rise_alpha_0 = 0.0\n", + "expt.peak.rise_alpha_1 = 0.5971" ] }, { @@ -245,7 +245,7 @@ "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)" + " expt.background.create(id=str(x), position=x, intensity=200)" ] }, { @@ -253,7 +253,7 @@ "id": "22", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -263,7 +263,7 @@ "metadata": {}, "outputs": [], "source": [ - "expt.linked_phases.create(id='si', scale=10.0)" + "expt.linked_structures.create(structure_id='si', scale=10.0)" ] }, { @@ -386,7 +386,7 @@ "source": [ "structure.cell.length_a.free = True\n", "\n", - "expt.linked_phases['si'].scale.free = True\n", + "expt.linked_structures['si'].scale.free = True\n", "expt.instrument.calib_d_to_tof_offset.free = True" ] }, @@ -473,7 +473,7 @@ "outputs": [], "source": [ "for point in expt.background:\n", - " point.y.free = True" + " point.intensity.free = True" ] }, { @@ -559,7 +559,7 @@ "outputs": [], "source": [ "for point in expt.background:\n", - " point.y.free = False" + " point.intensity.free = False" ] }, { @@ -666,9 +666,9 @@ "source": [ "structure.atom_sites['Si'].adp_iso.free = True\n", "\n", - "expt.peak.exp_decay_beta_0.free = True\n", - "expt.peak.exp_decay_beta_1.free = True\n", - "expt.peak.exp_rise_alpha_1.free = True" + "expt.peak.decay_beta_0.free = True\n", + "expt.peak.decay_beta_1.free = True\n", + "expt.peak.rise_alpha_1.free = True" ] }, { @@ -834,9 +834,9 @@ "expt.peak.broad_gauss_sigma_0 = 3.0148\n", "expt.peak.broad_gauss_sigma_1 = 33.3451\n", "expt.peak.broad_lorentz_gamma_1 = 2.5489\n", - "expt.peak.exp_decay_beta_0 = 0.04221\n", - "expt.peak.exp_decay_beta_1 = 0.00946\n", - "expt.peak.exp_rise_alpha_1 = 0.5971" + "expt.peak.decay_beta_0 = 0.04221\n", + "expt.peak.decay_beta_1 = 0.00946\n", + "expt.peak.rise_alpha_1 = 0.5971" ] }, { @@ -857,9 +857,9 @@ "expt.peak.broad_gauss_sigma_0.free = True\n", "expt.peak.broad_gauss_sigma_1.free = True\n", "expt.peak.broad_lorentz_gamma_1.free = True\n", - "expt.peak.exp_decay_beta_0.free = True\n", - "expt.peak.exp_decay_beta_1.free = True\n", - "expt.peak.exp_rise_alpha_1.free = True" + "expt.peak.decay_beta_0.free = True\n", + "expt.peak.decay_beta_1.free = True\n", + "expt.peak.rise_alpha_1.free = True" ] }, { @@ -942,7 +942,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_7_si_sepd')" + "project.save_as(dir_path='projects/refine-si-sepd')" ] } ], diff --git a/docs/docs/tutorials/ed-7.py b/docs/docs/tutorials/refine-si-sepd.py similarity index 86% rename from docs/docs/tutorials/ed-7.py rename to docs/docs/tutorials/refine-si-sepd.py index 09e51da4f..ca41ee82c 100644 --- a/docs/docs/tutorials/ed-7.py +++ b/docs/docs/tutorials/refine-si-sepd.py @@ -32,7 +32,7 @@ # %% structure.space_group.name_h_m = 'F d -3 m' -structure.space_group.it_coordinate_system_code = '2' +structure.space_group.coord_system_code = '2' # %% [markdown] # ### Set Unit Cell @@ -45,7 +45,7 @@ # %% structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.125, fract_y=0.125, @@ -62,7 +62,7 @@ # ### Download Data # %% -data_path = download_data(id=7, destination='data') +data_path = download_data('meas-si-sepd', destination='data') # %% [markdown] # ### Create Experiment @@ -79,7 +79,7 @@ expt.instrument.setup_twotheta_bank = 144.845 expt.instrument.calib_d_to_tof_offset = 0.0 expt.instrument.calib_d_to_tof_linear = 7476.91 -expt.instrument.calib_d_to_tof_quad = -1.54 +expt.instrument.calib_d_to_tof_quadratic = -1.54 # %% [markdown] # ### Set Peak Profile @@ -89,10 +89,10 @@ expt.peak.broad_gauss_sigma_0 = 3.0 expt.peak.broad_gauss_sigma_1 = 40.0 expt.peak.broad_gauss_sigma_2 = 2.0 -expt.peak.exp_decay_beta_0 = 0.04221 -expt.peak.exp_decay_beta_1 = 0.00946 -expt.peak.exp_rise_alpha_0 = 0.0 -expt.peak.exp_rise_alpha_1 = 0.5971 +expt.peak.decay_beta_0 = 0.04221 +expt.peak.decay_beta_1 = 0.00946 +expt.peak.rise_alpha_0 = 0.0 +expt.peak.rise_alpha_1 = 0.5971 # %% [markdown] # ### Set Background @@ -100,13 +100,13 @@ # %% expt.background.type = 'line-segment' for x in range(0, 35000, 5000): - expt.background.create(id=str(x), x=x, y=200) + expt.background.create(id=str(x), position=x, intensity=200) # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -expt.linked_phases.create(id='si', scale=10.0) +expt.linked_structures.create(structure_id='si', scale=10.0) # %% [markdown] # ## 📦 Define Project @@ -157,7 +157,7 @@ # %% structure.cell.length_a.free = True -expt.linked_phases['si'].scale.free = True +expt.linked_structures['si'].scale.free = True expt.instrument.calib_d_to_tof_offset.free = True # %% [markdown] @@ -189,7 +189,7 @@ # %% for point in expt.background: - point.y.free = True + point.intensity.free = True # %% [markdown] # Show free parameters after selection. @@ -220,7 +220,7 @@ # %% for point in expt.background: - point.y.free = False + point.intensity.free = False # %% [markdown] # Set more parameters to be refined. @@ -260,9 +260,9 @@ # %% structure.atom_sites['Si'].adp_iso.free = True -expt.peak.exp_decay_beta_0.free = True -expt.peak.exp_decay_beta_1.free = True -expt.peak.exp_rise_alpha_1.free = True +expt.peak.decay_beta_0.free = True +expt.peak.decay_beta_1.free = True +expt.peak.rise_alpha_1.free = True # %% [markdown] # Show free parameters after selection. @@ -320,9 +320,9 @@ expt.peak.broad_gauss_sigma_0 = 3.0148 expt.peak.broad_gauss_sigma_1 = 33.3451 expt.peak.broad_lorentz_gamma_1 = 2.5489 -expt.peak.exp_decay_beta_0 = 0.04221 -expt.peak.exp_decay_beta_1 = 0.00946 -expt.peak.exp_rise_alpha_1 = 0.5971 +expt.peak.decay_beta_0 = 0.04221 +expt.peak.decay_beta_1 = 0.00946 +expt.peak.rise_alpha_1 = 0.5971 # %% [markdown] # #### Add new free parameters @@ -331,9 +331,9 @@ expt.peak.broad_gauss_sigma_0.free = True expt.peak.broad_gauss_sigma_1.free = True expt.peak.broad_lorentz_gamma_1.free = True -expt.peak.exp_decay_beta_0.free = True -expt.peak.exp_decay_beta_1.free = True -expt.peak.exp_rise_alpha_1.free = True +expt.peak.decay_beta_0.free = True +expt.peak.decay_beta_1.free = True +expt.peak.rise_alpha_1.free = True # %% [markdown] # #### Run Fitting @@ -361,4 +361,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_7_si_sepd') +project.save_as(dir_path='projects/refine-si-sepd') diff --git a/docs/docs/tutorials/ed-15.ipynb b/docs/docs/tutorials/refine-taurine-senju.ipynb similarity index 93% rename from docs/docs/tutorials/ed-15.ipynb rename to docs/docs/tutorials/refine-taurine-senju.ipynb index 2fcc797fa..e50fe05e3 100644 --- a/docs/docs/tutorials/ed-15.ipynb +++ b/docs/docs/tutorials/refine-taurine-senju.ipynb @@ -45,7 +45,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -64,7 +64,7 @@ "outputs": [], "source": [ "# Create a minimal project with a short name\n", - "project = ed.Project(name='taurine_senju')" + "project = edi.Project(name='taurine_senju')" ] }, { @@ -83,7 +83,7 @@ "outputs": [], "source": [ "# Download CIF file from repository\n", - "structure_path = ed.download_data(id=21, destination='data')" + "structure_path = edi.download_data('struct-taurine', destination='data')" ] }, { @@ -123,7 +123,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -152,7 +152,7 @@ "outputs": [], "source": [ "# Download data file from repository\n", - "data_path = ed.download_data(id=22, destination='data')" + "data_path = edi.download_data('meas-taurine-senju', destination='data')" ] }, { @@ -188,8 +188,8 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_crystal.id = 'taurine'\n", - "experiment.linked_crystal.scale = 1.0" + "experiment.linked_structure.structure_id = 'taurine'\n", + "experiment.linked_structure.scale = 1.0" ] }, { @@ -236,7 +236,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_crystal.scale.free = True\n", + "experiment.linked_structure.scale.free = True\n", "experiment.extinction.radius.free = True" ] }, @@ -301,7 +301,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -363,7 +363,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -423,7 +423,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -441,7 +441,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_15_taurine_senju')" + "project.save_as(dir_path='projects/refine-taurine-senju')" ] } ], diff --git a/docs/docs/tutorials/ed-15.py b/docs/docs/tutorials/refine-taurine-senju.py similarity index 81% rename from docs/docs/tutorials/ed-15.py rename to docs/docs/tutorials/refine-taurine-senju.py index c8d799405..6a331b8a7 100644 --- a/docs/docs/tutorials/ed-15.py +++ b/docs/docs/tutorials/refine-taurine-senju.py @@ -8,21 +8,21 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project # %% # Create a minimal project with a short name -project = ed.Project(name='taurine_senju') +project = edi.Project(name='taurine_senju') # %% [markdown] # ## 🧩 Define Structure # %% # Download CIF file from repository -structure_path = ed.download_data(id=21, destination='data') +structure_path = edi.download_data('struct-taurine', destination='data') # %% project.structures.add_from_cif_path(structure_path) @@ -34,7 +34,7 @@ structure = project.structures['taurine'] # %% -structure.show_as_cif() +structure.show_as_text() # %% project.display.structure(struct_name='taurine') @@ -44,7 +44,7 @@ # %% # Download data file from repository -data_path = ed.download_data(id=22, destination='data') +data_path = edi.download_data('meas-taurine-senju', destination='data') # %% project.experiments.add_from_data_path( @@ -59,8 +59,8 @@ experiment = project.experiments['senju'] # %% -experiment.linked_crystal.id = 'taurine' -experiment.linked_crystal.scale = 1.0 +experiment.linked_structure.structure_id = 'taurine' +experiment.linked_structure.scale = 1.0 # %% experiment.extinction.mosaicity = 1000.0 @@ -76,7 +76,7 @@ project.display.pattern(expt_name='senju') # %% -experiment.linked_crystal.scale.free = True +experiment.linked_structure.scale.free = True experiment.extinction.radius.free = True # %% @@ -99,7 +99,7 @@ project.display.fit.results() # %% -structure.show_as_cif() +structure.show_as_text() # %% project.experiments.show_names() @@ -121,7 +121,7 @@ getattr(atom_site, component).free = True # %% -structure.show_as_cif() +structure.show_as_text() # %% project.display.parameters.free() @@ -139,10 +139,10 @@ project.display.pattern(expt_name='senju') # %% -structure.show_as_cif() +structure.show_as_text() # %% [markdown] # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_15_taurine_senju') +project.save_as(dir_path='projects/refine-taurine-senju') diff --git a/docs/docs/tutorials/ed-14.ipynb b/docs/docs/tutorials/refine-tbti-heidi.ipynb similarity index 94% rename from docs/docs/tutorials/ed-14.ipynb rename to docs/docs/tutorials/refine-tbti-heidi.ipynb index 9488915db..c69d6ca0b 100644 --- a/docs/docs/tutorials/ed-14.ipynb +++ b/docs/docs/tutorials/refine-tbti-heidi.ipynb @@ -45,7 +45,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -64,13 +64,13 @@ "outputs": [], "source": [ "# Create a minimal project with a short name\n", - "project = ed.Project(name='tbti_heidi')\n", - "project.info.title = 'Tb2Ti2O7 at HEiDi@FRMII'\n", - "project.info.description = \"\"\"This project demonstrates a standard\n", + "project = edi.Project(name='tbti_heidi')\n", + "project.metadata.title = 'Tb2Ti2O7 at HEiDi@FRMII'\n", + "project.metadata.description = \"\"\"This project demonstrates a standard\n", "refinement of the crystal structure of Tb2Ti2O7 using single crystal \n", "neutron diffraction data from HEiDi at FRM II.\"\"\"\n", "\n", - "project.save_as(dir_path='projects/ed_14_tbti_heidi')" + "project.save_as(dir_path='projects/refine-tbti-heidi')" ] }, { @@ -89,7 +89,7 @@ "outputs": [], "source": [ "# Download CIF file from repository\n", - "structure_path = ed.download_data(id=20, destination='data')" + "structure_path = edi.download_data('struct-tbti', destination='data')" ] }, { @@ -129,7 +129,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -216,7 +216,7 @@ "outputs": [], "source": [ "# Download data file from repository\n", - "data_path = ed.download_data(id=19, destination='data')" + "data_path = edi.download_data('meas-tbti-heidi', destination='data')" ] }, { @@ -252,8 +252,8 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_crystal.id = 'tbti'\n", - "experiment.linked_crystal.scale = 1.0" + "experiment.linked_structure.structure_id = 'tbti'\n", + "experiment.linked_structure.scale = 1.0" ] }, { @@ -329,7 +329,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_crystal.scale.free = True\n", + "experiment.linked_structure.scale.free = True\n", "experiment.extinction.radius.free = True" ] }, @@ -363,7 +363,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -491,7 +491,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { diff --git a/docs/docs/tutorials/ed-14.py b/docs/docs/tutorials/refine-tbti-heidi.py similarity index 87% rename from docs/docs/tutorials/ed-14.py rename to docs/docs/tutorials/refine-tbti-heidi.py index 03a2b329d..ded14b307 100644 --- a/docs/docs/tutorials/ed-14.py +++ b/docs/docs/tutorials/refine-tbti-heidi.py @@ -8,27 +8,27 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project # %% # Create a minimal project with a short name -project = ed.Project(name='tbti_heidi') -project.info.title = 'Tb2Ti2O7 at HEiDi@FRMII' -project.info.description = """This project demonstrates a standard +project = edi.Project(name='tbti_heidi') +project.metadata.title = 'Tb2Ti2O7 at HEiDi@FRMII' +project.metadata.description = """This project demonstrates a standard refinement of the crystal structure of Tb2Ti2O7 using single crystal neutron diffraction data from HEiDi at FRM II.""" -project.save_as(dir_path='projects/ed_14_tbti_heidi') +project.save_as(dir_path='projects/refine-tbti-heidi') # %% [markdown] # ## 🧩 Define Structure # %% # Download CIF file from repository -structure_path = ed.download_data(id=20, destination='data') +structure_path = edi.download_data('struct-tbti', destination='data') # %% project.structures.add_from_cif_path(structure_path) @@ -40,7 +40,7 @@ structure = project.structures['tbti'] # %% -structure.show_as_cif() +structure.show_as_text() # %% structure.atom_sites['Tb'].adp_type = 'Uiso' @@ -75,7 +75,7 @@ # %% # Download data file from repository -data_path = ed.download_data(id=19, destination='data') +data_path = edi.download_data('meas-tbti-heidi', destination='data') # %% project.experiments.add_from_data_path( @@ -90,8 +90,8 @@ experiment = project.experiments['heidi'] # %% -experiment.linked_crystal.id = 'tbti' -experiment.linked_crystal.scale = 1.0 +experiment.linked_structure.structure_id = 'tbti' +experiment.linked_structure.scale = 1.0 # %% experiment.instrument.setup_wavelength = 0.793 @@ -122,7 +122,7 @@ structure.atom_sites['O2'].adp_iso.free = True # %% -experiment.linked_crystal.scale.free = True +experiment.linked_structure.scale.free = True experiment.extinction.radius.free = True # %% @@ -135,7 +135,7 @@ project.display.fit.results() # %% -structure.show_as_cif() +structure.show_as_text() # %% project.display.structure(struct_name='tbti') @@ -181,7 +181,7 @@ project.display.pattern(expt_name='heidi') # %% -structure.show_as_cif() +structure.show_as_text() # %% [markdown] # ### Display Structure (final) diff --git a/docs/docs/tutorials/ed-27.ipynb b/docs/docs/tutorials/simulate-lbco-cwl.ipynb similarity index 94% rename from docs/docs/tutorials/ed-27.ipynb rename to docs/docs/tutorials/simulate-lbco-cwl.ipynb index e989a00c6..803a7662e 100644 --- a/docs/docs/tutorials/ed-27.ipynb +++ b/docs/docs/tutorials/simulate-lbco-cwl.ipynb @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -73,7 +73,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='lbco_simulation')" + "project = edi.Project(name='lbco_simulation')" ] }, { @@ -99,7 +99,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure_path = ed.download_data(id=1, destination='data')" + "structure_path = edi.download_data('struct-lbco', destination='data')" ] }, { @@ -234,8 +234,8 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.background.create(id='1', x=10, y=20)\n", - "experiment.background.create(id='2', x=160, y=20)" + "experiment.background.create(id='1', position=10, intensity=20)\n", + "experiment.background.create(id='2', position=160, intensity=20)" ] }, { @@ -268,7 +268,7 @@ "id": "24", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -278,7 +278,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases.create(id='lbco', scale=10.0)" + "experiment.linked_structures.create(structure_id='lbco', scale=10.0)" ] }, { @@ -371,7 +371,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_27_lbco_simulation')" + "project.save_as(dir_path='projects/simulate-lbco-cwl')" ] } ], diff --git a/docs/docs/tutorials/ed-27.py b/docs/docs/tutorials/simulate-lbco-cwl.py similarity index 88% rename from docs/docs/tutorials/ed-27.py rename to docs/docs/tutorials/simulate-lbco-cwl.py index acac1eb6a..3f4be8f86 100644 --- a/docs/docs/tutorials/ed-27.py +++ b/docs/docs/tutorials/simulate-lbco-cwl.py @@ -18,13 +18,13 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project # %% -project = ed.Project(name='lbco_simulation') +project = edi.Project(name='lbco_simulation') # %% [markdown] # ## 🧩 Define Structure @@ -33,7 +33,7 @@ # ### Download CIF file # %% -structure_path = ed.download_data(id=1, destination='data') +structure_path = edi.download_data('struct-lbco', destination='data') # %% [markdown] # ### Add Structure from CIF @@ -89,8 +89,8 @@ # ### Set Background # %% -experiment.background.create(id='1', x=10, y=20) -experiment.background.create(id='2', x=160, y=20) +experiment.background.create(id='1', position=10, intensity=20) +experiment.background.create(id='2', position=160, intensity=20) # %% [markdown] # ### Set Calculation Range @@ -106,10 +106,10 @@ experiment.data_range.two_theta_inc = 0.05 # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -experiment.linked_phases.create(id='lbco', scale=10.0) +experiment.linked_structures.create(structure_id='lbco', scale=10.0) # %% [markdown] # ## 🚀 Perform Calculation @@ -145,4 +145,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_27_lbco_simulation') +project.save_as(dir_path='projects/simulate-lbco-cwl') diff --git a/docs/docs/tutorials/ed-29.ipynb b/docs/docs/tutorials/simulate-nacl-xray.ipynb similarity index 94% rename from docs/docs/tutorials/ed-29.ipynb rename to docs/docs/tutorials/simulate-nacl-xray.ipynb index fde68a3f3..5c82b1c71 100644 --- a/docs/docs/tutorials/ed-29.ipynb +++ b/docs/docs/tutorials/simulate-nacl-xray.ipynb @@ -51,7 +51,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -69,7 +69,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='nacl_simulation')" + "project = edi.Project(name='nacl_simulation')" ] }, { @@ -108,7 +108,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'F m -3 m'\n", - "structure.space_group.it_coordinate_system_code = '1'" + "structure.space_group.coord_system_code = '1'" ] }, { @@ -129,7 +129,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Na',\n", + " id='Na',\n", " type_symbol='Na',\n", " fract_x=0,\n", " fract_y=0,\n", @@ -137,7 +137,7 @@ " adp_iso=0.5,\n", ")\n", "structure.atom_sites.create(\n", - " label='Cl',\n", + " id='Cl',\n", " type_symbol='Cl',\n", " fract_x=0.5,\n", " fract_y=0.5,\n", @@ -224,7 +224,7 @@ "id": "19", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -234,7 +234,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases.create(id='nacl', scale=1.0)" + "experiment.linked_structures.create(structure_id='nacl', scale=1.0)" ] }, { @@ -295,7 +295,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_29_nacl_simulation')" + "project.save_as(dir_path='projects/simulate-nacl-xray')" ] } ], diff --git a/docs/docs/tutorials/ed-29.py b/docs/docs/tutorials/simulate-nacl-xray.py similarity index 87% rename from docs/docs/tutorials/ed-29.py rename to docs/docs/tutorials/simulate-nacl-xray.py index 3a89668e2..65f1065d3 100644 --- a/docs/docs/tutorials/ed-29.py +++ b/docs/docs/tutorials/simulate-nacl-xray.py @@ -14,13 +14,13 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project # %% -project = ed.Project(name='nacl_simulation') +project = edi.Project(name='nacl_simulation') # %% [markdown] # ## 🧩 Define Structure @@ -33,14 +33,14 @@ # %% structure.space_group.name_h_m = 'F m -3 m' -structure.space_group.it_coordinate_system_code = '1' +structure.space_group.coord_system_code = '1' # %% structure.cell.length_a = 5.62 # %% structure.atom_sites.create( - label='Na', + id='Na', type_symbol='Na', fract_x=0, fract_y=0, @@ -48,7 +48,7 @@ adp_iso=0.5, ) structure.atom_sites.create( - label='Cl', + id='Cl', type_symbol='Cl', fract_x=0.5, fract_y=0.5, @@ -87,10 +87,10 @@ experiment.peak.broad_gauss_w = 0.1 # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -experiment.linked_phases.create(id='nacl', scale=1.0) +experiment.linked_structures.create(structure_id='nacl', scale=1.0) # %% [markdown] # ### Inspect the Default Calculation Range @@ -115,4 +115,4 @@ # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_29_nacl_simulation') +project.save_as(dir_path='projects/simulate-nacl-xray') diff --git a/docs/docs/tutorials/ed-28.ipynb b/docs/docs/tutorials/simulate-si-tof.ipynb similarity index 89% rename from docs/docs/tutorials/ed-28.ipynb rename to docs/docs/tutorials/simulate-si-tof.ipynb index 59eb7a9f0..a3992665e 100644 --- a/docs/docs/tutorials/ed-28.ipynb +++ b/docs/docs/tutorials/simulate-si-tof.ipynb @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "import easydiffraction as edi" ] }, { @@ -73,7 +73,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project(name='si_simulation')" + "project = edi.Project(name='si_simulation')" ] }, { @@ -112,7 +112,7 @@ "outputs": [], "source": [ "structure.space_group.name_h_m = 'F d -3 m'\n", - "structure.space_group.it_coordinate_system_code = '2'" + "structure.space_group.coord_system_code = '2'" ] }, { @@ -133,7 +133,7 @@ "outputs": [], "source": [ "structure.atom_sites.create(\n", - " label='Si',\n", + " id='Si',\n", " type_symbol='Si',\n", " fract_x=0.125,\n", " fract_y=0.125,\n", @@ -208,7 +208,7 @@ "experiment.instrument.setup_twotheta_bank = 144.845\n", "experiment.instrument.calib_d_to_tof_offset = 0.0\n", "experiment.instrument.calib_d_to_tof_linear = 7476.91\n", - "experiment.instrument.calib_d_to_tof_quad = -1.54" + "experiment.instrument.calib_d_to_tof_quadratic = -1.54" ] }, { @@ -229,10 +229,10 @@ "experiment.peak.broad_gauss_sigma_0 = 3.0\n", "experiment.peak.broad_gauss_sigma_1 = 40.0\n", "experiment.peak.broad_gauss_sigma_2 = 2.0\n", - "experiment.peak.exp_decay_beta_0 = 0.04221\n", - "experiment.peak.exp_decay_beta_1 = 0.00946\n", - "experiment.peak.exp_rise_alpha_0 = 0.0\n", - "experiment.peak.exp_rise_alpha_1 = 0.5971" + "experiment.peak.decay_beta_0 = 0.04221\n", + "experiment.peak.decay_beta_1 = 0.00946\n", + "experiment.peak.rise_alpha_0 = 0.0\n", + "experiment.peak.rise_alpha_1 = 0.5971" ] }, { @@ -251,8 +251,8 @@ "outputs": [], "source": [ "experiment.background.type = 'line-segment'\n", - "experiment.background.create(id='1', x=10000, y=500)\n", - "experiment.background.create(id='2', x=40000, y=500)" + "experiment.background.create(id='1', position=10000, intensity=500)\n", + "experiment.background.create(id='2', position=40000, intensity=500)" ] }, { @@ -280,7 +280,7 @@ "id": "24", "metadata": {}, "source": [ - "### Set Linked Phases" + "### Set Linked Structures" ] }, { @@ -290,7 +290,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases.create(id='si', scale=10.0)" + "experiment.linked_structures.create(structure_id='si', scale=10.0)" ] }, { @@ -328,7 +328,7 @@ "id": "29", "metadata": {}, "source": [ - "### Inspect as CIF" + "### Inspect as Text" ] }, { @@ -338,7 +338,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.experiments['sim'].show_as_cif()" + "project.experiments['sim'].show_as_text()" ] }, { @@ -356,7 +356,7 @@ "metadata": {}, "outputs": [], "source": [ - "project.save_as(dir_path='projects/ed_28_si_simulation')" + "project.save_as(dir_path='projects/simulate-si-tof')" ] } ], diff --git a/docs/docs/tutorials/ed-28.py b/docs/docs/tutorials/simulate-si-tof.py similarity index 78% rename from docs/docs/tutorials/ed-28.py rename to docs/docs/tutorials/simulate-si-tof.py index d716aef45..36fe007e8 100644 --- a/docs/docs/tutorials/ed-28.py +++ b/docs/docs/tutorials/simulate-si-tof.py @@ -18,13 +18,13 @@ # ## 🛠️ Import Library # %% -import easydiffraction as ed +import easydiffraction as edi # %% [markdown] # ## 📦 Define Project # %% -project = ed.Project(name='si_simulation') +project = edi.Project(name='si_simulation') # %% [markdown] # ## 🧩 Define Structure @@ -37,14 +37,14 @@ # %% structure.space_group.name_h_m = 'F d -3 m' -structure.space_group.it_coordinate_system_code = '2' +structure.space_group.coord_system_code = '2' # %% structure.cell.length_a = 5.431 # %% structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.125, fract_y=0.125, @@ -81,7 +81,7 @@ experiment.instrument.setup_twotheta_bank = 144.845 experiment.instrument.calib_d_to_tof_offset = 0.0 experiment.instrument.calib_d_to_tof_linear = 7476.91 -experiment.instrument.calib_d_to_tof_quad = -1.54 +experiment.instrument.calib_d_to_tof_quadratic = -1.54 # %% [markdown] # ### Set Peak Profile @@ -90,18 +90,18 @@ experiment.peak.broad_gauss_sigma_0 = 3.0 experiment.peak.broad_gauss_sigma_1 = 40.0 experiment.peak.broad_gauss_sigma_2 = 2.0 -experiment.peak.exp_decay_beta_0 = 0.04221 -experiment.peak.exp_decay_beta_1 = 0.00946 -experiment.peak.exp_rise_alpha_0 = 0.0 -experiment.peak.exp_rise_alpha_1 = 0.5971 +experiment.peak.decay_beta_0 = 0.04221 +experiment.peak.decay_beta_1 = 0.00946 +experiment.peak.rise_alpha_0 = 0.0 +experiment.peak.rise_alpha_1 = 0.5971 # %% [markdown] # ### Set Background # %% experiment.background.type = 'line-segment' -experiment.background.create(id='1', x=10000, y=500) -experiment.background.create(id='2', x=40000, y=500) +experiment.background.create(id='1', position=10000, intensity=500) +experiment.background.create(id='2', position=40000, intensity=500) # %% [markdown] # ### Set Calculation Range @@ -112,10 +112,10 @@ experiment.data_range.time_of_flight_inc = 2.0 # %% [markdown] -# ### Set Linked Phases +# ### Set Linked Structures # %% -experiment.linked_phases.create(id='si', scale=10.0) +experiment.linked_structures.create(structure_id='si', scale=10.0) # %% [markdown] # ## 🚀 Perform Calculation @@ -129,13 +129,13 @@ project.display.pattern(expt_name='sim', x_min=5000, x_max=6000) # %% [markdown] -# ### Inspect as CIF +# ### Inspect as Text # %% -project.experiments['sim'].show_as_cif() +project.experiments['sim'].show_as_text() # %% [markdown] # ## 💾 Save Project # %% -project.save_as(dir_path='projects/ed_28_si_simulation') +project.save_as(dir_path='projects/simulate-si-tof') diff --git a/docs/docs/user-guide/analysis-workflow/analysis.md b/docs/docs/user-guide/analysis-workflow/analysis.md index c3a71d747..894bfa221 100644 --- a/docs/docs/user-guide/analysis-workflow/analysis.md +++ b/docs/docs/user-guide/analysis-workflow/analysis.md @@ -1,4 +1,5 @@ --- +title: Analysis icon: material/calculator --- @@ -214,10 +215,10 @@ Here is an example of how to set 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 +project.experiments['hrpt'].linked_structures['lbco'].scale.free = True project.experiments['hrpt'].instrument.calib_twotheta_offset.free = True -project.experiments['hrpt'].background['10'].y.free = True -project.experiments['hrpt'].background['165'].y.free = True +project.experiments['hrpt'].background['10'].intensity.free = True +project.experiments['hrpt'].background['165'].intensity.free = True ``` After setting the parameters to be refined, you can perform the fit @@ -285,7 +286,7 @@ regions where peaks overlap: project.experiments['hrpt'].background.auto_estimate() # Optionally free some of the new (fixed) points and fit again -project.experiments['hrpt'].background['1'].y.free = True +project.experiments['hrpt'].background['1'].intensity.free = True project.analysis.fit() ``` @@ -329,7 +330,7 @@ project.display.posterior.predictive(expt_name='hrpt') When posterior or posterior-predictive arrays are persisted, they are stored in `analysis/results.h5`. Scalar summaries remain in -`analysis/analysis.cif`. +`analysis/analysis.edi`. ## Constraints @@ -349,21 +350,21 @@ An example of setting aliases for parameters in a structure: ```python # Set aliases for the atomic displacement parameters project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=project.structures['lbco'].atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=project.structures['lbco'].atom_sites['Ba'].adp_iso, ) # Set aliases for the occupancies of the atom sites project.analysis.aliases.create( - label='occ_La', + id='occ_La', param=project.structures['lbco'].atom_sites['La'].occupancy, ) project.analysis.aliases.create( - label='occ_Ba', + id='occ_Ba', param=project.structures['lbco'].atom_sites['Ba'].occupancy, ) ``` @@ -410,43 +411,49 @@ User defined constraints Constraints enabled: True -## Analysis as CIF +## Analysis as Text -To inspect an analysis configuration in CIF format, use: +To inspect an analysis configuration as text, use: ```python -# Show analysis as CIF -project.analysis.show_as_cif() +# Show analysis as text +project.analysis.show_as_text() ``` -Example output: +Once a fit has run, the output also includes a `_fit_result.*` block. +Example output (configuration before fitting): ``` -╒════════════════════════════════════════════════╕ -│ _fitting_mode.type single │ -│ _minimizer.type "lmfit (leastsq)" │ -│ │ -│ loop_ │ -│ _alias.label │ -│ _alias.param_unique_name │ -│ biso_La lbco.atom_site.La.B_iso_or_equiv │ -│ biso_Ba lbco.atom_site.Ba.B_iso_or_equiv │ -│ occ_La lbco.atom_site.La.occupancy │ -│ occ_Ba lbco.atom_site.Ba.occupancy │ -│ │ -│ loop_ │ -│ _constraint.id │ -│ _constraint.expression │ -│ biso_Ba "biso_Ba = biso_La" │ -│ occ_Ba "occ_Ba = 1 - occ_La" │ -╘════════════════════════════════════════════════╛ +Analysis info as text +┌────────────────────────────────────────────────────┐ +│ CIF │ +├────────────────────────────────────────────────────┤ +│ 1 _fitting_mode.type single │ +│ 2 │ +│ 3 _minimizer.type "lmfit (leastsq)" │ +│ 4 _minimizer.max_iterations 1000 │ +│ 5 │ +│ 6 loop_ │ +│ 7 _alias.id │ +│ 8 _alias.parameter_unique_name │ +│ 9 biso_La lbco.atom_site.La.adp_iso │ +│ 10 biso_Ba lbco.atom_site.Ba.adp_iso │ +│ 11 occ_La lbco.atom_site.La.occupancy │ +│ 12 occ_Ba lbco.atom_site.Ba.occupancy │ +│ 13 │ +│ 14 loop_ │ +│ 15 _constraint.id │ +│ 16 _constraint.expression │ +│ 17 biso_Ba "biso_Ba = biso_La" │ +│ 18 occ_Ba "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. +will also save the analysis settings to `analysis/analysis.edi` inside +the project directory. <br> diff --git a/docs/docs/user-guide/analysis-workflow/experiment.md b/docs/docs/user-guide/analysis-workflow/experiment.md index 41cfccd7f..cfd98764c 100644 --- a/docs/docs/user-guide/analysis-workflow/experiment.md +++ b/docs/docs/user-guide/analysis-workflow/experiment.md @@ -1,4 +1,5 @@ --- +title: Experiment icon: material/microscope --- @@ -64,7 +65,7 @@ example, if the CIF file contains a data block with the id `hrpt`, <pre> data_<span class="red"><b>hrpt</b></span> -<span class="blue"><b>_expt_type</b>.beam_mode</span> "constant wavelength" +<span class="blue"><b>_experiment_type</b>.beam_mode</span> "constant wavelength" ... </pre> </div> @@ -193,9 +194,9 @@ aspects of the experiment: 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 +4. **Linked Structures Category**: Links the structure defined in the previous step to the experiment, allowing you to specify the scale - factor for the linked phase. + factor for the linked structure. 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. @@ -237,11 +238,11 @@ project.experiments['hrpt'].peak.broad_lorentz_y = 0.1 project.experiments['hrpt'].background.type = 'line-segment' # Add background points -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) +project.experiments['hrpt'].background.create(position=10, intensity=170) +project.experiments['hrpt'].background.create(position=30, intensity=170) +project.experiments['hrpt'].background.create(position=50, intensity=170) +project.experiments['hrpt'].background.create(position=110, intensity=170) +project.experiments['hrpt'].background.create(position=165, intensity=170) ``` Instead of placing every point by hand, you can let EasyDiffraction @@ -274,11 +275,11 @@ cap the number of points or choose a specific method: project.experiments['hrpt'].background.auto_estimate(n_points=10) ``` -### 5. Linked Phases Category { #linked-phases-category } +### 5. Linked Structures Category { #linked-structures-category } ```python # Link the structure defined in the previous step to the experiment -project.experiments['hrpt'].linked_phases.create(id='lbco', scale=10.0) +project.experiments['hrpt'].linked_structures.create(structure_id='lbco', scale=10.0) ``` ### 6. Preferred Orientation Category { #preferred-orientation-category } @@ -292,32 +293,32 @@ fraction `march_random_fract`: ```python # Add a March–Dollase preferred-orientation correction for a phase project.experiments['hrpt'].preferred_orientation.create( - phase_id='lbco', march_r=1.2, index_h=0, index_k=0, index_l=1 + structure_id='lbco', march_r=1.2, index_h=0, index_k=0, index_l=1 ) ``` This is a constant-wavelength Bragg powder correction, available on the `cryspy` engine. The defaults (`march_r=1`, `march_random_fract=0`) apply no texture, so it has no effect until you set them. See the -[preferred-orientation parameters](../parameters/pref_orient.md) for -details. +[preferred-orientation parameters](../parameters/experiment/preferred_orientation.md) +for details. ### 7. 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. +data from a file in a supported format. The measured data is added to +the experiment and saved with the project as Edi. The expected format +depends on the experiment type. #### Supported data file formats: - `.xye` or `.xys` (3 columns, including standard deviations) - - [\_pd_meas.2theta_scan](../parameters/pd_meas.md) - - [\_pd_meas.intensity_total](../parameters/pd_meas.md) - - [\_pd_meas.intensity_total_su](../parameters/pd_meas.md) + - [\_data.two_theta](../parameters/experiment/data.md#data-two-theta) + - [\_data.intensity_meas](../parameters/experiment/data.md#data-intensity-meas) + - [\_data.intensity_meas_su](../parameters/experiment/data.md#data-intensity-meas-su) - `.xy` (2 columns, no standard deviations): - - [\_pd_meas.2theta_scan](../parameters/pd_meas.md) - - [\_pd_meas.intensity_total](../parameters/pd_meas.md) + - [\_data.two_theta](../parameters/experiment/data.md#data-two-theta) + - [\_data.intensity_meas](../parameters/experiment/data.md#data-intensity-meas) If no **standard deviations** are provided, they are automatically calculated as the **square root** of measured intensities. @@ -404,79 +405,80 @@ Defined experiments 🔬 ['hrpt'] ``` -## Viewing an Experiment as CIF +## Viewing an Experiment as Text -To inspect an experiment in CIF format, use: +To inspect an experiment's serialized text (the same content the project +persists into its Edi files), use: ```python -# Show experiment as CIF -project.experiments['hrpt'].show_as_cif() +# Show experiment as text +project.experiments['hrpt'].show_as_text() ``` -Example output: +Long loops (measured data, reflections) are truncated with `...` for +display. Example output: ``` -Experiment 🔬 'hrpt' as cif -╒═════════════════════════════════════════════╕ -│ data_hrpt │ -│ │ -│ _expt_type.beam_mode "constant wavelength" │ -│ _expt_type.radiation_probe neutron │ -│ _expt_type.sample_form powder │ -│ _expt_type.scattering_type bragg │ -│ │ -│ _instr.2theta_offset 0.6 │ -│ _instr.wavelength 1.494 │ -│ │ -│ _peak.broad_gauss_u 0.1 │ -│ _peak.broad_gauss_v -0.1 │ -│ _peak.broad_gauss_w 0.1 │ -│ _peak.broad_lorentz_x 0 │ -│ _peak.broad_lorentz_y 0.1 │ -│ │ -│ loop_ │ -│ _pd_phase_block.id │ -│ _pd_phase_block.scale │ -│ lbco 10.0 │ -│ │ -│ loop_ │ -│ _pd_background.line_segment_X │ -│ _pd_background.line_segment_intensity │ -│ 10 170 │ -│ 30 170 │ -│ 50 170 │ -│ 110 170 │ -│ 165 170 │ -│ │ -│ loop_ │ -│ _pd_meas.2theta_scan │ -│ _pd_meas.intensity_total │ -│ _pd_meas.intensity_total_su │ -│ 10.0 167.0 12.6 │ -│ 10.05 157.0 12.5 │ -│ 10.1 187.0 13.3 │ -│ 10.15 197.0 14.0 │ -│ 10.2 164.0 12.5 │ -│ ... │ -│ 164.65 173.0 30.1 │ -│ 164.7 187.0 27.9 │ -│ 164.75 175.0 38.2 │ -│ 164.8 168.0 30.9 │ -│ 164.85 109.0 41.2 │ -╘═════════════════════════════════════════════╛ +Experiment 🔬 'hrpt' as text +┌────────────────────────────────────────────────────┐ +│ CIF │ +├────────────────────────────────────────────────────┤ +│ 1 data_hrpt │ +│ 2 │ +│ 3 _experiment_type.sample_form powder │ +│ 4 _experiment_type.beam_mode "constant wavelength" │ +│ 5 _experiment_type.radiation_probe neutron │ +│ 6 _experiment_type.scattering_type bragg │ +│ 7 │ +│ 8 _calculator.type cryspy │ +│ 9 │ +│ 10 _peak.broad_gauss_u 0.0816(31) │ +│ 11 _peak.broad_gauss_v -0.1159(66) │ +│ 12 _peak.broad_gauss_w 0.1204(32) │ +│ 13 _peak.broad_lorentz_x 0. │ +│ 14 _peak.broad_lorentz_y 0.0844(21) │ +│ 15 _peak.type cwl-pseudo-voigt │ +│ 16 │ +│ 17 _instrument.setup_wavelength 1.494 │ +│ 18 _instrument.calib_twotheta_offset 0.6226(10) │ +│ 19 │ +│ 20 loop_ │ +│ 21 _linked_structure.structure_id │ +│ 22 _linked_structure.scale │ +│ 23 lbco 9.135(54) │ +│ 24 │ +│ 25 _background.type line-segment │ +│ 26 │ +│ 27 loop_ │ +│ 28 _background.id │ +│ 29 _background.position │ +│ 30 _background.intensity │ +│ 31 1 10. 168.4(1.4) │ +│ 32 ... │ +│ 33 │ +│ 34 loop_ │ +│ 35 _data.two_theta │ +│ 36 _data.id │ +│ 37 _data.intensity_meas │ +│ 38 _data.intensity_meas_su │ +│ 39 10. 1 167. 12.6 │ +│ 40 ... │ +└────────────────────────────────────────────────────┘ ``` ## Saving an Experiment -Saving the project, as described in the [Project](project.md) section, will -also save the experiment. Each experiment is saved as a separate CIF file in -the `experiments` 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 experiment. Each experiment is saved as a separate +`.edi` file in the `experiments` subdirectory of the project +directory. The project file contains references to these files. -EasyDiffraction supports different types of experiments, and each experiment -is saved in a dedicated CIF file with experiment-specific parameters. +EasyDiffraction supports different types of experiments, and each +experiment is saved in a dedicated Edi file with experiment-specific +parameters. -Below are examples of how different experiments are saved in CIF format. +Below are examples of how different experiments are saved in Edi +format. ### [pd-neut-cwl][3]{:.label-experiment} @@ -489,13 +491,13 @@ experiment: <pre> data_<span class="red"><b>hrpt</b></span> -<span class="blue"><b>_expt_type</b>.beam_mode</span> "constant wavelength" -<span class="blue"><b>_expt_type</b>.radiation_probe</span> neutron -<span class="blue"><b>_expt_type</b>.sample_form</span> powder -<span class="blue"><b>_expt_type</b>.scattering_type</span> bragg +<span class="blue"><b>_experiment_type</b>.beam_mode</span> "constant wavelength" +<span class="blue"><b>_experiment_type</b>.radiation_probe</span> neutron +<span class="blue"><b>_experiment_type</b>.sample_form</span> powder +<span class="blue"><b>_experiment_type</b>.scattering_type</span> bragg -<span class="blue"><b>_instr</b>.wavelength</span> 1.494 -<span class="blue"><b>_instr</b>.2theta_offset</span> 0.6225(4) +<span class="blue"><b>_instrument</b>.setup_wavelength</span> 1.494 +<span class="blue"><b>_instrument</b>.calib_twotheta_offset</span> 0.6225(4) <span class="blue"><b>_peak</b>.broad_gauss_u</span> 0.0834 <span class="blue"><b>_peak</b>.broad_gauss_v</span> -0.1168 @@ -504,35 +506,36 @@ data_<span class="red"><b>hrpt</b></span> <span class="blue"><b>_peak</b>.broad_lorentz_y</span> 0.0797 loop_ -<span class="green"><b>_pd_phase_block</b>.id</span> -<span class="green"><b>_pd_phase_block</b>.scale</span> +<span class="green"><b>_linked_structure</b>.structure_id</span> +<span class="green"><b>_linked_structure</b>.scale</span> lbco 9.0976(3) loop_ -<span class="green"><b>_pd_background</b>.line_segment_X</span> -<span class="green"><b>_pd_background</b>.line_segment_intensity</span> -<span class="green"><b>_pd_background</b>.X_coordinate</span> - 10 174.3 2theta - 20 159.8 2theta - 30 167.9 2theta - 50 166.1 2theta - 70 172.3 2theta - 90 171.1 2theta -110 172.4 2theta -130 182.5 2theta -150 173.0 2theta -165 171.1 2theta +<span class="green"><b>_background</b>.id</span> +<span class="green"><b>_background</b>.position</span> +<span class="green"><b>_background</b>.intensity</span> + 1 10 174.3 + 2 20 159.8 + 3 30 167.9 + 4 50 166.1 + 5 70 172.3 + 6 90 171.1 + 7 110 172.4 + 8 130 182.5 + 9 150 173.0 +10 165 171.1 loop_ -<span class="green"><b>_pd_meas</b>.2theta_scan</span> -<span class="green"><b>_pd_meas</b>.intensity_total</span> -<span class="green"><b>_pd_meas</b>.intensity_total_su</span> - 10.00 167 12.6 - 10.05 157 12.5 - 10.10 187 13.3 - 10.15 197 14.0 - 10.20 164 12.5 - 10.25 171 13.0 +<span class="green"><b>_data</b>.id</span> +<span class="green"><b>_data</b>.two_theta</span> +<span class="green"><b>_data</b>.intensity_meas</span> +<span class="green"><b>_data</b>.intensity_meas_su</span> +1 10.00 167 12.6 +2 10.05 157 12.5 +3 10.10 187 13.3 +4 10.15 197 14.0 +5 10.20 164 12.5 +6 10.25 171 13.0 ... 164.60 153 20.7 164.65 173 30.1 @@ -555,57 +558,61 @@ experiment: <pre> data_<span class="red"><b>wish</b></span> -<span class="blue"><b>_diffrn_radiation</b>.probe</span> neutron +<span class="blue"><b>_experiment_type</b>.beam_mode</span> "time-of-flight" +<span class="blue"><b>_experiment_type</b>.radiation_probe</span> neutron +<span class="blue"><b>_experiment_type</b>.sample_form</span> powder +<span class="blue"><b>_experiment_type</b>.scattering_type</span> bragg -<span class="blue"><b>_instr</b>.2theta_bank</span> 152.827 +<span class="blue"><b>_instrument</b>.setup_twotheta_bank</span> 152.827 -<span class="blue"><b>_instr</b>.d_to_tof_linear</span> 20773.1(3) -<span class="blue"><b>_instr</b>.d_to_tof_quad</span> -1.08308 -<span class="blue"><b>_instr</b>.d_to_tof_offset</span> -13.7(5) +<span class="blue"><b>_instrument</b>.calib_d_to_tof_linear</span> 20773.1(3) +<span class="blue"><b>_instrument</b>.calib_d_to_tof_quadratic</span> -1.08308 +<span class="blue"><b>_instrument</b>.calib_d_to_tof_offset</span> -13.7(5) -<span class="blue"><b>_peak</b>.rise_alpha_0</span> -0.009(1) -<span class="blue"><b>_peak</b>.rise_alpha_1</span> 0.109(2) -<span class="blue"><b>_peak</b>.decay_beta_0</span> 0.00670(3) -<span class="blue"><b>_peak</b>.decay_beta_1</span> 0.0100(3) -<span class="blue"><b>_peak</b>.gauss_sigma_0</span> 0 -<span class="blue"><b>_peak</b>.gauss_sigma_1</span> 0 -<span class="blue"><b>_peak</b>.gauss_sigma_2</span> 15.7(8) +<span class="blue"><b>_peak</b>.rise_alpha_0</span> -0.009(1) +<span class="blue"><b>_peak</b>.rise_alpha_1</span> 0.109(2) +<span class="blue"><b>_peak</b>.decay_beta_0</span> 0.00670(3) +<span class="blue"><b>_peak</b>.decay_beta_1</span> 0.0100(3) +<span class="blue"><b>_peak</b>.broad_gauss_sigma_0</span> 0 +<span class="blue"><b>_peak</b>.broad_gauss_sigma_1</span> 0 +<span class="blue"><b>_peak</b>.broad_gauss_sigma_2</span> 15.7(8) loop_ -<span class="green"><b>_pd_phase_block</b>.id</span> -<span class="green"><b>_pd_phase_block</b>.scale</span> +<span class="green"><b>_linked_structure</b>.structure_id</span> +<span class="green"><b>_linked_structure</b>.scale</span> ncaf 1.093(5) loop_ -<span class="green"><b>_pd_background</b>.line_segment_X</span> -<span class="green"><b>_pd_background</b>.line_segment_intensity</span> -<span class="green"><b>_pd_background</b>.X_coordinate</span> - 9162.3 465(38) time-of-flight - 11136.8 593(30) time-of-flight - 14906.5 546(18) time-of-flight - 17352.2 496(14) time-of-flight - 20179.5 452(10) time-of-flight - 22176.0 468(12) time-of-flight - 24644.7 380(6) time-of-flight - 28257.2 378(4) time-of-flight - 34034.4 328(4) time-of-flight - 41214.6 323(3) time-of-flight - 49830.9 273(3) time-of-flight - 58204.9 260(4) time-of-flight - 70186.9 262(5) time-of-flight - 82103.2 268(5) time-of-flight -102712.0 262(15) time-of-flight +<span class="green"><b>_background</b>.id</span> +<span class="green"><b>_background</b>.position</span> +<span class="green"><b>_background</b>.intensity</span> + 1 9162.3 465(38) + 2 11136.8 593(30) + 3 14906.5 546(18) + 4 17352.2 496(14) + 5 20179.5 452(10) + 6 22176.0 468(12) + 7 24644.7 380(6) + 8 28257.2 378(4) + 9 34034.4 328(4) +10 41214.6 323(3) +11 49830.9 273(3) +12 58204.9 260(4) +13 70186.9 262(5) +14 82103.2 268(5) +15 102712.0 262(15) loop_ -<span class="green"><b>_pd_meas</b>.time_of_flight</span> -<span class="green"><b>_pd_meas</b>.intensity_total</span> -<span class="green"><b>_pd_meas</b>.intensity_total_su</span> - 9001.0 616.523 124.564 - 9006.8 578.769 123.141 - 9012.6 574.184 120.507 - 9018.5 507.739 111.300 - 9024.3 404.672 101.616 - 9030.1 469.244 107.991 +<span class="green"><b>_data</b>.id</span> +<span class="green"><b>_data</b>.time_of_flight</span> +<span class="green"><b>_data</b>.intensity_meas</span> +<span class="green"><b>_data</b>.intensity_meas_su</span> +1 9001.0 616.523 124.564 +2 9006.8 578.769 123.141 +3 9012.6 574.184 120.507 +4 9018.5 507.739 111.300 +5 9024.3 404.672 101.616 +6 9030.1 469.244 107.991 ... 103085.0 275.072 60.978 103151.4 214.187 55.675 @@ -626,12 +633,16 @@ This example represents a single-crystal neutron diffraction experiment: <pre> data_<span class="red"><b>heidi</b></span> -<span class="blue"><b>_diffrn_radiation</b>.probe</span> neutron -<span class="blue"><b>_instr</b>.wavelength</span> 0.793 +<span class="blue"><b>_experiment_type</b>.beam_mode</span> "constant wavelength" +<span class="blue"><b>_experiment_type</b>.radiation_probe</span> neutron +<span class="blue"><b>_experiment_type</b>.sample_form</span> "single crystal" +<span class="blue"><b>_experiment_type</b>.scattering_type</span> bragg + +<span class="blue"><b>_instrument</b>.setup_wavelength</span> 0.793 loop_ -<span class="green"><b>_exptl_crystal</b>.id</span> -<span class="green"><b>_exptl_crystal</b>.scale</span> +<span class="green"><b>_linked_structure</b>.structure_id</span> +<span class="green"><b>_linked_structure</b>.scale</span> tbti 2.92(6) loop_ diff --git a/docs/docs/user-guide/analysis-workflow/model.md b/docs/docs/user-guide/analysis-workflow/model.md index 3f0783a28..f1a3e94ca 100644 --- a/docs/docs/user-guide/analysis-workflow/model.md +++ b/docs/docs/user-guide/analysis-workflow/model.md @@ -1,4 +1,5 @@ --- +title: Structure icon: material/puzzle --- @@ -102,7 +103,7 @@ project.structures['nacl'].cell.length_a = 5.691694 ```python # Add atomic sites project.structures['nacl'].atom_sites.create( - label='Na', + id='Na', type_symbol='Na', fract_x=0, fract_y=0, @@ -111,7 +112,7 @@ project.structures['nacl'].atom_sites.create( adp_iso=0.5, ) project.structures['nacl'].atom_sites.create( - label='Cl', + id='Cl', type_symbol='Cl', fract_x=0, fract_y=0, @@ -137,47 +138,51 @@ Defined structures 🧩 ['lbco', 'nacl'] ``` -## Viewing a Structure as CIF +## Viewing a Structure as Text -To inspect a structure in CIF format, use: +To inspect a structure's serialized text (the same content the project +persists into its Edi files), use: ```python -# Show structure as CIF -project.structures['lbco'].show_as_cif() +# Show structure as text +project.structures['lbco'].show_as_text() ``` Example output: ``` -Structure 🧩 'lbco' as cif -╒═══════════════════════════════════════════╕ -│ data_lbco │ -│ │ -│ _space_group.IT_coordinate_system_code 1 │ -│ _space_group.name_H-M_alt "P m -3 m" │ -│ │ -│ _cell.angle_alpha 90 │ -│ _cell.angle_beta 90 │ -│ _cell.angle_gamma 90 │ -│ _cell.length_a 3.88 │ -│ _cell.length_b 3.88 │ -│ _cell.length_c 3.88 │ -│ │ -│ loop_ │ -│ _atom_site.ADP_type │ -│ _atom_site.B_iso_or_equiv │ -│ _atom_site.fract_x │ -│ _atom_site.fract_y │ -│ _atom_site.fract_z │ -│ _atom_site.label │ -│ _atom_site.occupancy │ -│ _atom_site.type_symbol │ -│ _atom_site.Wyckoff_symbol │ -│ Biso 0.5 0.0 0.0 0.0 La 0.5 La a │ -│ Biso 0.5 0.0 0.0 0.0 Ba 0.5 Ba a │ -│ Biso 0.5 0.5 0.5 0.5 Co 1.0 Co b │ -│ Biso 0.5 0.0 0.5 0.5 O 1.0 O c │ -╘═══════════════════════════════════════════╛ +Structure 🧩 'lbco' as text +┌──────────────────────────────────────────────┐ +│ CIF │ +├──────────────────────────────────────────────┤ +│ 1 data_lbco │ +│ 2 │ +│ 3 _cell.length_a 3.890868(38) │ +│ 4 _cell.length_b 3.890868 │ +│ 5 _cell.length_c 3.890868 │ +│ 6 _cell.angle_alpha 90. │ +│ 7 _cell.angle_beta 90. │ +│ 8 _cell.angle_gamma 90. │ +│ 9 │ +│ 10 _space_group.name_h_m "P m -3 m" │ +│ 11 _space_group.coord_system_code 1 │ +│ 12 │ +│ 13 loop_ │ +│ 14 _atom_site.id │ +│ 15 _atom_site.type_symbol │ +│ 16 _atom_site.fract_x │ +│ 17 _atom_site.fract_y │ +│ 18 _atom_site.fract_z │ +│ 19 _atom_site.wyckoff_letter │ +│ 20 _atom_site.multiplicity │ +│ 21 _atom_site.occupancy │ +│ 22 _atom_site.adp_iso │ +│ 23 _atom_site.adp_type │ +│ 24 La La 0. 0. 0. a 1 0.5 0.505(28) Biso │ +│ 25 Ba Ba 0. 0. 0. a 1 0.5 0.50514168 Biso │ +│ 26 Co Co 0.5 0.5 0.5 b 1 1. 0.237(56) Biso │ +│ 27 O O 0. 0.5 0.5 c 3 1. 1.394(16) Biso │ +└──────────────────────────────────────────────┘ ``` ## Viewing a Structure in 3D @@ -216,13 +221,13 @@ project.structure_style.adp_probability = 0.5 # ADP ellipsoid probability level ``` Bonds are generated automatically between atoms whose separation falls -within the per-structure cutoffs stored on `structure.geom` (the -standard cif_core `_geom` parameters): +within the per-structure cutoffs stored on `structure.geom` (the Edi +`_geom` parameters): ```python # Tune the per-structure bond-generation cutoffs (angstrom) project.structures['lbco'].geom.min_bond_distance_cutoff = 0.5 -project.structures['lbco'].geom.bond_distance_incr = 0.25 +project.structures['lbco'].geom.bond_distance_inc = 0.25 ``` Draw the structure through `project.display`, mirroring @@ -250,11 +255,11 @@ section). ## Saving a Structure Saving the project, as described in the [Project](project.md) section, -will also save the structure. Each structure is saved as a separate CIF -file in the `structures` subdirectory of the project directory. The -project file contains references to these files. +will also save the structure. Each structure is saved as a separate +`.edi` 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` structure: +Below is an example of the saved Edi file for the `lbco` structure: <!-- prettier-ignore-start --> @@ -262,8 +267,8 @@ Below is an example of the saved CIF file for the `lbco` structure: <pre> data_<span class="red"><b>lbco</b></span> -<span class="blue"><b>_space_group</b>.name_H-M_alt</span> "P m -3 m" -<span class="blue"><b>_space_group</b>.IT_coordinate_system_code</span> 1 +<span class="blue"><b>_space_group</b>.name_h_m</span> "P m -3 m" +<span class="blue"><b>_space_group</b>.coord_system_code</span> 1 <span class="blue"><b>_cell</b>.length_a</span> 3.8909 <span class="blue"><b>_cell</b>.length_b</span> 3.8909 @@ -273,15 +278,15 @@ data_<span class="red"><b>lbco</b></span> <span class="blue"><b>_cell</b>.angle_gamma</span> 90 loop_ -<span class="green"><b>_atom_site</b>.label</span> +<span class="green"><b>_atom_site</b>.id</span> <span class="green"><b>_atom_site</b>.type_symbol</span> <span class="green"><b>_atom_site</b>.fract_x</span> <span class="green"><b>_atom_site</b>.fract_y</span> <span class="green"><b>_atom_site</b>.fract_z</span> -<span class="green"><b>_atom_site</b>.Wyckoff_symbol</span> +<span class="green"><b>_atom_site</b>.wyckoff_letter</span> <span class="green"><b>_atom_site</b>.occupancy</span> -<span class="green"><b>_atom_site</b>.ADP_type</span> -<span class="green"><b>_atom_site</b>.B_iso_or_equiv</span> +<span class="green"><b>_atom_site</b>.adp_type</span> +<span class="green"><b>_atom_site</b>.adp_iso</span> La La 0 0 0 a 0.5 Biso 0.4958 Ba Ba 0 0 0 a 0.5 Biso 0.4943 Co Co 0.5 0.5 0.5 b 1 Biso 0.2567 diff --git a/docs/docs/user-guide/analysis-workflow/project.md b/docs/docs/user-guide/analysis-workflow/project.md index e9f133d3e..f9d467653 100644 --- a/docs/docs/user-guide/analysis-workflow/project.md +++ b/docs/docs/user-guide/analysis-workflow/project.md @@ -1,4 +1,5 @@ --- +title: Project icon: material/archive --- @@ -27,11 +28,11 @@ You can manually create a new project and specify its short **name**, ```py # Create a new project -project = ed.Project(name='lbco_hrpt') +project = edi.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 +# Define project metadata +project.metadata.title = 'La0.5Ba0.5CoO3 from neutron diffraction at HRPT@PSI' +project.metadata.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).""" @@ -63,7 +64,7 @@ useful for continuing a previous session or reusing a downloaded saved project. ```python -project = ed.Project.load('lbco_hrpt') +project = edi.Project.load('lbco_hrpt') ``` ## Project Structure @@ -76,15 +77,15 @@ The example below illustrates a typical **project structure** for a <div class="cif"> <pre> 📁 <span class="red"><b>La0.5Ba0.5CoO3</b></span> - project root -├── 📄 <span class="orange"><b>project.cif</b></span> - project configuration +├── 📄 <span class="orange"><b>project.edi</b></span> - project configuration ├── 📁 structures - structures -│ ├── 📄 <span class="orange"><b>lbco.cif</b></span> - LBCO +│ ├── 📄 <span class="orange"><b>lbco.edi</b></span> - LBCO │ └── ... ├── 📁 experiments - experiments -│ ├── 📄 <span class="orange"><b>hrpt.cif</b></span> - HRPT pattern +│ ├── 📄 <span class="orange"><b>hrpt.edi</b></span> - HRPT pattern │ └── ... ├── 📁 analysis - analysis -│ ├── 📄 <span class="orange"><b>analysis.cif</b></span> - fit state +│ ├── 📄 <span class="orange"><b>analysis.edi</b></span> - fit state │ └── 📄 <span class="orange"><b>results.h5</b></span> - Bayesian arrays └── 📁 reports - reports ├── 📄 <span class="orange"><b>La0.5Ba0.5CoO3.cif</b></span> - IUCr @@ -102,13 +103,13 @@ directory, showing the main files created by a typical workflow. !!! 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 + only contain the `project.edi` file. The other folders and files will be created as you add structures, experiments, and set up the analysis. The reports folder is created only when at least one of `project.report.cif`, `project.report.html`, `project.report.tex`, or `project.report.pdf` is set to `True` before `project.save()`. -### 1. <span class="orange">project.cif</span> +### 1. <span class="orange">project.edi</span> This file stores project-level metadata and display configuration. @@ -116,12 +117,13 @@ This file stores project-level metadata and display configuration. <div class="cif"> <pre> -<span class="blue"><b>_project</b>.id</span> lbco_hrpt -<span class="blue"><b>_project</b>.title</span> "La0.5Ba0.5CoO3 from neutron diffraction at HRPT@PSI" -<span class="blue"><b>_project</b>.description</span> "neutrons, powder, constant wavelength, HRPT@PSI" +<span class="blue"><b>_edi</b>.schema_version</span> 1 -<span class="blue"><b>_project</b>.created</span> "18 May 2026 10:15:00" -<span class="blue"><b>_project</b>.last_modified</span> "18 May 2026 10:20:00" +<span class="blue"><b>_metadata</b>.name</span> lbco_hrpt +<span class="blue"><b>_metadata</b>.title</span> "La0.5Ba0.5CoO3 from neutron diffraction at HRPT@PSI" +<span class="blue"><b>_metadata</b>.description</span> "neutrons, powder, constant wavelength, HRPT@PSI" +<span class="blue"><b>_metadata</b>.created</span> "18 May 2026 10:15:00" +<span class="blue"><b>_metadata</b>.last_modified</span> "18 May 2026 10:20:00" <span class="blue"><b>_rendering_plot</b>.type</span> auto <span class="blue"><b>_report</b>.cif</span> false @@ -140,7 +142,7 @@ This file stores project-level metadata and display configuration. <!-- prettier-ignore-end --> -### 2. structures / <span class="orange">lbco.cif</span> +### 2. structures / <span class="orange">lbco.edi</span> This file contains crystallographic information associated with the structure model, including **space group**, **unit cell parameters**, @@ -163,7 +165,7 @@ data_<span class="red"><b>lbco</b></span> <span class="blue"><b>_cell</b>.angle_gamma</span> 90 loop_ -<span class="green"><b>_atom_site</b>.label</span> +<span class="green"><b>_atom_site</b>.id</span> <span class="green"><b>_atom_site</b>.type_symbol</span> <span class="green"><b>_atom_site</b>.fract_x</span> <span class="green"><b>_atom_site</b>.fract_y</span> @@ -181,7 +183,7 @@ O O 0 0.5 0.5 c 1 Biso 1.4041 <!-- prettier-ignore-end --> -### 3. experiments / <span class="orange">hrpt.cif</span> +### 3. experiments / <span class="orange">hrpt.edi</span> This file contains the **experiment type**, **calculation engine**, **instrumental parameters**, **peak parameters**, **associated phases**, @@ -193,15 +195,15 @@ This file contains the **experiment type**, **calculation engine**, <pre> data_<span class="red"><b>hrpt</b></span> -<span class="blue"><b>_expt_type</b>.beam_mode</span> "constant wavelength" -<span class="blue"><b>_expt_type</b>.radiation_probe</span> neutron -<span class="blue"><b>_expt_type</b>.sample_form</span> powder -<span class="blue"><b>_expt_type</b>.scattering_type</span> bragg +<span class="blue"><b>_experiment_type</b>.beam_mode</span> "constant wavelength" +<span class="blue"><b>_experiment_type</b>.radiation_probe</span> neutron +<span class="blue"><b>_experiment_type</b>.sample_form</span> powder +<span class="blue"><b>_experiment_type</b>.scattering_type</span> bragg <span class="blue"><b>_calculator</b>.type</span> cryspy -<span class="blue"><b>_instr</b>.wavelength</span> 1.494 -<span class="blue"><b>_instr</b>.2theta_offset</span> 0.6225(4) +<span class="blue"><b>_instrument</b>.setup_wavelength</span> 1.494 +<span class="blue"><b>_instrument</b>.calib_twotheta_offset</span> 0.6225(4) <span class="blue"><b>_peak</b>.broad_gauss_u</span> 0.0834 <span class="blue"><b>_peak</b>.broad_gauss_v</span> -0.1168 @@ -210,35 +212,32 @@ data_<span class="red"><b>hrpt</b></span> <span class="blue"><b>_peak</b>.broad_lorentz_y</span> 0.0797 loop_ -<span class="green"><b>_pd_phase_block</b>.id</span> -<span class="green"><b>_pd_phase_block</b>.scale</span> +<span class="green"><b>_linked_structure</b>.structure_id</span> +<span class="green"><b>_linked_structure</b>.scale</span> lbco 9.0976(3) loop_ -<span class="green"><b>_pd_background</b>.line_segment_X</span> -<span class="green"><b>_pd_background</b>.line_segment_intensity</span> -<span class="green"><b>_pd_background</b>.X_coordinate</span> - 10 174.3 2theta - 20 159.8 2theta - 30 167.9 2theta - 50 166.1 2theta - 70 172.3 2theta - 90 171.1 2theta -110 172.4 2theta -130 182.5 2theta -150 173.0 2theta -165 171.1 2theta +<span class="green"><b>_background</b>.id</span> +<span class="green"><b>_background</b>.position</span> +<span class="green"><b>_background</b>.intensity</span> +1 10 174.3 +2 20 159.8 +3 30 167.9 +4 50 166.1 +5 70 172.3 +6 90 171.1 loop_ -<span class="green"><b>_pd_meas</b>.2theta_scan</span> -<span class="green"><b>_pd_meas</b>.intensity_total</span> -<span class="green"><b>_pd_meas</b>.intensity_total_su</span> - 10.00 167 12.6 - 10.05 157 12.5 - 10.10 187 13.3 - 10.15 197 14.0 - 10.20 164 12.5 - 10.25 171 13.0 +<span class="green"><b>_data</b>.id</span> +<span class="green"><b>_data</b>.two_theta</span> +<span class="green"><b>_data</b>.intensity_meas</span> +<span class="green"><b>_data</b>.intensity_meas_su</span> +1 10.00 167 12.6 +2 10.05 157 12.5 +3 10.10 187 13.3 +4 10.15 197 14.0 +5 10.20 164 12.5 +6 10.25 171 13.0 ... 164.60 153 20.7 164.65 173 30.1 @@ -251,7 +250,7 @@ loop_ <!-- prettier-ignore-end --> -### 4. analysis / <span class="orange">analysis.cif</span> +### 4. analysis / <span class="orange">analysis.edi</span> This file contains settings used for data analysis, including the choice of **calculation** and **fitting** engines, as well as user defined @@ -265,8 +264,8 @@ of **calculation** and **fitting** engines, as well as user defined <span class="blue"><b>_minimizer</b>.type</span> lmfit loop_ -<span class="green"><b>_alias</b>.label</span> -<span class="green"><b>_alias</b>.param_unique_name</span> +<span class="green"><b>_alias</b>.id</span> +<span class="green"><b>_alias</b>.parameter_unique_name</span> biso_La lbco.atom_site.La.B_iso_or_equiv biso_Ba lbco.atom_site.Ba.B_iso_or_equiv occ_La lbco.atom_site.La.occupancy diff --git a/docs/docs/user-guide/analysis-workflow/report.md b/docs/docs/user-guide/analysis-workflow/report.md index 46af2623e..86bcf9eaa 100644 --- a/docs/docs/user-guide/analysis-workflow/report.md +++ b/docs/docs/user-guide/analysis-workflow/report.md @@ -1,4 +1,5 @@ --- +title: Report icon: material/clipboard-text --- @@ -34,7 +35,7 @@ project.report.save_html() ## Configuring Saved Reports Report output is controlled by `project.report`, a project-level -configuration category that is saved in `project.cif`. Regular +configuration category that is saved in `project.edi`. Regular `project.save()` calls read this configuration and write the selected report formats. @@ -91,7 +92,7 @@ when a fit writes the project back to disk: python -m easydiffraction path/to/project fit ``` -If `project.cif` contains `_report.html true`, `_report.tex true`, or +If `project.edi` contains `_report.html true`, `_report.tex true`, or another enabled report flag, `fit` writes those reports as part of the normal project save. Use the Python per-format methods above for one-off exports without changing the saved configuration. diff --git a/docs/docs/user-guide/data-format.md b/docs/docs/user-guide/data-format.md index 9b8b505e1..5d797d512 100644 --- a/docs/docs/user-guide/data-format.md +++ b/docs/docs/user-guide/data-format.md @@ -3,14 +3,27 @@ Before starting the data analysis workflow, it is important to define the **data formats** used in EasyDiffraction. -## Crystallographic Information File +## Edi Projects And CIF Data 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. +**parameter names** for storing and sharing data. EasyDiffraction uses +**Edi** for saved project state and **Crystallographic Information File +(CIF)** for crystallographic input and strict report export. + +!!! note "Pronunciation" + + **Edi** is the short name for EasyDiffraction, pronounced **"eddie"** + (/ˈɛdi/). It is the same short form you use in code + (`import easydiffraction as edi`) and the suffix on saved files + (`.edi`). Write it as a word — lowercase `edi` for the extension and + `_edi.*` keys, capitalised `Edi` at the start of a sentence — not as + an all-caps acronym. + +Edi uses CIF-like syntax, but its keys are chosen for EasyDiffraction's +Python-facing project model. CIF remains the standard exchange format +used by crystallography and materials science. It provides both a +human-readable syntax and dictionaries that define the meaning of each +parameter. These dictionaries are maintained by the [International Union of Crystallography (IUCr)](https://www.iucr.org). @@ -18,21 +31,19 @@ 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. -As most parameters needed for diffraction data analysis are already -covered by IUCr dictionaries, EasyDiffraction uses **CIF** as its -project persistence format and follows these dictionaries where they fit -the day-to-day project files. Some EasyDiffraction-owned settings, such -as minimizer choices and report configuration, use project-specific CIF -categories. +As most crystallographic parameters needed for diffraction data analysis +are already covered by IUCr dictionaries, EasyDiffraction follows those +dictionaries for CIF import and report output where they fit. Edi uses +the same names when they are already clear, and uses +EasyDiffraction-owned names where the project API is clearer. -The key advantage of CIF is the standardized naming of parameters and -categories, which promotes interoperability and familiarity among -researchers. +The key advantage of CIF is standardized naming for scientific exchange. +The key advantage of Edi is that saved projects round-trip the +EasyDiffraction project model without overloading report CIF as project +state. -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. +The [Parameters](parameters.md) section lists Python access paths, Edi +keys, and CIF keys side by side. ## Format Comparison @@ -179,52 +190,49 @@ better suited for human-readable crystallographic data. 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 saved in Edi. For example, line-segment background +intensity in a powder diffraction experiment is represented as: <!-- prettier-ignore-start --> <div class="cif"> <pre> loop_ -<span class="green"><b>_pd_background</b>.line_segment_X</span> -<span class="green"><b>_pd_background</b>.line_segment_intensity</span> -<span class="green"><b>_pd_background</b>.X_coordinate</span> +<span class="green"><b>_background</b>.position</span> +<span class="green"><b>_background</b>.intensity</span> - 10.0 174.3 2theta - 20.0 159.8 2theta - 30.0 167.9 2theta + 10.0 174.3 + 20.0 159.8 + 30.0 167.9 ... </pre> </div> <!-- prettier-ignore-end --> -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 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 saves projects as a directory of Edi files and sidecars: -- **project**: contains the project information -- **structure**: defines the structure -- **experiment**: contains the experiment setup and measured data -- **analysis**: stores fitting and analysis parameters -- **reports**: stores generated HTML, CIF, TeX, and PDF reports when - enabled through `project.report` +- `project.edi`: project metadata and display/report configuration +- `structures/<structure>.edi`: structure models +- `experiments/<experiment>.edi`: experiment setup and data +- `analysis/analysis.edi`: fitting and analysis settings +- `analysis/results.csv` and `analysis/results.h5`: fit result sidecars +- `reports/<project>.*`: generated reports when enabled through + `project.report` -Example CIF files for each block are provided in the +Examples for each block are provided in the [Analysis Workflow](analysis-workflow/index.md) and [Tutorials](../tutorials/index.md). ## 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. +EasyDiffraction also supports plain text files for importing measured +data. The meaning of the columns depends on the experiment type. For example, in a standard constant-wavelength powder diffraction experiment: diff --git a/docs/docs/user-guide/first-steps.md b/docs/docs/user-guide/first-steps.md index e8f235e46..f696b02e2 100644 --- a/docs/docs/user-guide/first-steps.md +++ b/docs/docs/user-guide/first-steps.md @@ -20,19 +20,19 @@ Alternatively, you can import it with an alias to avoid naming conflicts and for convenience: ```python -import easydiffraction as ed +import easydiffraction as edi ``` 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 +within the package using the `edi` prefix. For example, you can create a project instance like this: ```python -project = ed.Project() +project = edi.Project() ``` A complete tutorial using the `import` syntax can be found -[here](../tutorials/ed-3.ipynb). +[here](../tutorials/refine-lbco-hrpt-report.ipynb). ### Importing specific parts @@ -57,31 +57,32 @@ project = Project() ``` A complete tutorial using the `from` syntax can be found -[here](../tutorials/ed-4.ipynb). +[here](../tutorials/refine-pbso4-joint.ipynb). ## Utility functions EasyDiffraction also provides several utility functions that can simplify your workflow. One of them is the `download_data` function, -which allows you to download example datasets by their numeric ID from -our remote repository, making it easy to access and use them while +which allows you to download example datasets by their slug from our +remote repository, making it easy to access and use them while experimenting with EasyDiffraction. -You can list the available datasets and their IDs with `list_data()`, +You can list the available datasets and their slugs with `list_data()`, then download one like this: ```python -import easydiffraction as ed +import easydiffraction as edi -ed.list_data() +edi.list_data() -data_path = ed.download_data(id=3, destination='data') +data_path = edi.download_data('meas-lbco-hrpt', destination='data') ``` -This command downloads the dataset with ID `3` and saves it in the -`data` directory of your current working directory, returning the full -path to the downloaded file. This is particularly useful for quickly -accessing example datasets without having to manually download them. +This command downloads the `measured/lbco-hrpt` dataset and saves it in +the `data` directory of your current working directory, returning the +full path to the downloaded file. This is particularly useful for +quickly accessing example datasets without having to manually download +them. ## Help methods diff --git a/docs/docs/user-guide/glossary.md b/docs/docs/user-guide/glossary.md index 16bf0e4f2..0cb3e9e56 100644 --- a/docs/docs/user-guide/glossary.md +++ b/docs/docs/user-guide/glossary.md @@ -13,8 +13,8 @@ dictionaries: [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. +- [coreCIF][0]{:.label-cif} – Custom CIF dictionary developed for + EasyDiffraction. For more information about CIF, see the [Data Format](data-format.md) section. diff --git a/docs/docs/user-guide/index.md b/docs/docs/user-guide/index.md index 546e69316..f5d1ac5f3 100644 --- a/docs/docs/user-guide/index.md +++ b/docs/docs/user-guide/index.md @@ -1,4 +1,5 @@ --- +title: User Guide icon: material/book-open-variant --- diff --git a/docs/docs/user-guide/parameters.md b/docs/docs/user-guide/parameters.md index aa1d8d32e..4828627ce 100644 --- a/docs/docs/user-guide/parameters.md +++ b/docs/docs/user-guide/parameters.md @@ -1,87 +1,48 @@ # Parameters The data analysis process, introduced in the [Concept](concept.md) -section, assumes that you mainly work with different parameters. The -parameters are used to describe the structure and the experiment and are -required to set up the analysis. - -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 - `adp_iso` instead of CIF `B_iso_or_equiv` / `U_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. +section, uses parameters to describe structures, experiments, and the +analysis state. + +Each parameter has: + +- a Python access path used in notebooks and scripts, +- an Edi key used when EasyDiffraction saves a project, and +- where applicable, a CIF key used for importing crystallographic data + or writing IUCr/pdCIF reports. + +Edi is the regular EasyDiffraction project persistence format. CIF +remains important for crystallographic input and for strict report +exports. The tables below therefore separate code access, Edi keys, and +CIF keys instead of treating one name as universal. ## 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 -[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`. - -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. +Parameters in EasyDiffraction are objects. Alongside `name` and `value`, +they can carry attributes such as uncertainty, unit, minimum and maximum +allowed values, and fit bounds. The most important day-to-day attribute +is `free`, which controls whether a parameter is refined during fitting. +It is `False` by default. + +Users normally access parameters through top-level objects such as +`project`, `structures`, and `experiments`; parameters are created when +the project objects are created or loaded. !!! warning "Important" - Remember that parameters are accessed in code through their parent objects, - 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: + Parameters are accessed through their parent objects. For example, if a + structure has the ID `nacl`, the space-group name is accessed as: ```python project.structures['nacl'].space_group.name_h_m ``` -In the example above, `space_group` is a structure category, and -`name_h_m` is the parameter. For simplicity, only the last part -(`category.parameter`) of the full access name will be shown in the -tables below. - -In addition, the CIF names are also provided for each parameter, which -are used to serialize the parameters in the CIF format. +For compactness, the code tables show only the last part of the access +path, such as `space_group.name_h_m`. -Tags defining the corresponding experiment type are also given before -the table. +## Structure Parameters -## Structure parameters - -Below is a list of parameters used to describe the structure in -EasyDiffraction. - -### Crystal structure parameters +### Crystal Structure Parameters [pd-neut-cwl][3]{:.label-experiment} [pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} @@ -89,56 +50,76 @@ EasyDiffraction. === "How to access in the code" - | Category | Parameter | How to access in the code | - |-----------------------------------------------------|--------------------------------------------------------------|------------------------------------| - | :material-space-station: [space_group][space_group] | :material-tag: [name_h_m][space_group] | space_group.name_h_m | - | | :material-numeric: [it_coordinate_system_code][space_group] | space_group.it_coordinate_system_code | - | :material-cube-outline: [cell][cell] | :material-ruler: [length_a][cell] | cell.length_a | - | | :material-ruler: [length_b][cell] | cell.length_b | - | | :material-ruler: [length_c][cell] | cell.length_c | - | | :material-angle-acute: [angle_alpha][cell] | cell.angle_alpha | - | | :material-angle-acute: [angle_beta][cell] | cell.angle_beta | - | | :material-angle-acute: [angle_gamma][cell] | cell.angle_gamma | - | :material-atom: [atom_site][atom_site] | :material-tag: [label][atom_site] | atom_sites['ID'].label | - | | :material-periodic-table: [type_symbol][atom_site] | atom_sites['ID'].type_symbol | - | | :material-map-marker: [fract_x][atom_site] | atom_sites['ID'].fract_x | - | | :material-map-marker: [fract_y][atom_site] | atom_sites['ID'].fract_y | - | | :material-map-marker: [fract_z][atom_site] | atom_sites['ID'].fract_z | - | | :material-format-color-fill: [occupancy][atom_site] | atom_sites['ID'].occupancy | - | | :material-cursor-move: [adp_type][atom_site] | atom_sites['ID'].adp_type | - | | :material-cursor-move: [adp_iso][atom_site] | atom_sites['ID'].adp_iso | - | | :material-reflect-horizontal: [multiplicity][atom_site] | atom_sites['ID'].multiplicity | - | | :material-reflect-horizontal: [wyckoff_letter][atom_site] | atom_sites['ID'].wyckoff_letter | - -=== "CIF name for serialization" - - | Category | Parameter | CIF name for serialization | CIF dictionary | - |-----------------------------------------------------|--------------------------------------------------------------|-----------------------------------------|---------------------------| - | :material-space-station: [space_group][space_group] | :material-tag: [name_h_m][space_group] | \_space_group.name_H-M_alt | [coreCIF][1]{:.label-cif} | - | | :material-numeric: [it_coordinate_system_code][space_group] | \_space_group.IT_coordinate_system_code | [coreCIF][1]{:.label-cif} | - | :material-cube-outline: [cell][cell] | :material-ruler: [length_a][cell] | \_cell.length_a | [coreCIF][1]{:.label-cif} | - | | :material-ruler: [length_b][cell] | \_cell.length_b | [coreCIF][1]{:.label-cif} | - | | :material-ruler: [length_c][cell] | \_cell.length_c | [coreCIF][1]{:.label-cif} | - | | :material-angle-acute: [angle_alpha][cell] | \_cell.angle_alpha | [coreCIF][1]{:.label-cif} | - | | :material-angle-acute: [angle_beta][cell] | \_cell.angle_beta | [coreCIF][1]{:.label-cif} | - | | :material-angle-acute: [angle_gamma][cell] | \_cell.angle_gamma | [coreCIF][1]{:.label-cif} | - | :material-atom: [atom_site][atom_site] | :material-tag: [label][atom_site] | \_atom_site.label | [coreCIF][1]{:.label-cif} | - | | :material-periodic-table: [type_symbol][atom_site] | \_atom_site.type_symbol | [coreCIF][1]{:.label-cif} | - | | :material-map-marker: [fract_x][atom_site] | \_atom_site.fract_x | [coreCIF][1]{:.label-cif} | - | | :material-map-marker: [fract_y][atom_site] | \_atom_site.fract_y | [coreCIF][1]{:.label-cif} | - | | :material-map-marker: [fract_z][atom_site] | \_atom_site.fract_z | [coreCIF][1]{:.label-cif} | - | | :material-format-color-fill: [occupancy][atom_site] | \_atom_site.occupancy | [coreCIF][1]{:.label-cif} | - | | :material-cursor-move: [adp_type][atom_site] | \_atom_site.ADP_type | [coreCIF][1]{:.label-cif} | - | | :material-cursor-move: [adp_iso][atom_site] | \_atom_site.B_iso_or_equiv | [coreCIF][1]{:.label-cif} | - | | :material-reflect-horizontal: [multiplicity][atom_site] | \_atom_site.site_symmetry_multiplicity | [coreCIF][1]{:.label-cif} | - | | :material-reflect-horizontal: [wyckoff_letter][atom_site] | \_atom_site.Wyckoff_symbol | [coreCIF][1]{:.label-cif} | - -## Experiment parameters - -Below is a list of parameters used to describe the experiment in -EasyDiffraction. - -### Common parameters + | Category | Parameter | How to access in the code | + | --- | --- | --- | + | :material-space-station: [space_group][space_group] | :material-tag: [name_h_m](parameters/structure/space_group.md#space-group-name-h-m) | space_group.name_h_m | + | | :material-numeric: [coord_system_code](parameters/structure/space_group.md#space-group-coord-system-code) | space_group.coord_system_code | + | :material-cube-outline: [cell][cell] | :material-ruler: [length_a](parameters/structure/cell.md#cell-length-a) | cell.length_a | + | | :material-ruler: [length_b](parameters/structure/cell.md#cell-length-b) | cell.length_b | + | | :material-ruler: [length_c](parameters/structure/cell.md#cell-length-c) | cell.length_c | + | | :material-angle-acute: [angle_alpha](parameters/structure/cell.md#cell-angle-alpha) | cell.angle_alpha | + | | :material-angle-acute: [angle_beta](parameters/structure/cell.md#cell-angle-beta) | cell.angle_beta | + | | :material-angle-acute: [angle_gamma](parameters/structure/cell.md#cell-angle-gamma) | cell.angle_gamma | + | :material-atom: [atom_site][atom_site] | :material-tag: [id](parameters/structure/atom_site.md#atom-site-id) | atom_sites['ID'].id | + | | :material-periodic-table: [type_symbol](parameters/structure/atom_site.md#atom-site-type-symbol) | atom_sites['ID'].type_symbol | + | | :material-map-marker: [fract_x](parameters/structure/atom_site.md#atom-site-fract-x) | atom_sites['ID'].fract_x | + | | :material-map-marker: [fract_y](parameters/structure/atom_site.md#atom-site-fract-y) | atom_sites['ID'].fract_y | + | | :material-map-marker: [fract_z](parameters/structure/atom_site.md#atom-site-fract-z) | atom_sites['ID'].fract_z | + | | :material-format-color-fill: [occupancy](parameters/structure/atom_site.md#atom-site-occupancy) | atom_sites['ID'].occupancy | + | | :material-cursor-move: [adp_type](parameters/structure/atom_site.md#atom-site-adp-type) | atom_sites['ID'].adp_type | + | | :material-cursor-move: [adp_iso](parameters/structure/atom_site.md#atom-site-adp-iso) | atom_sites['ID'].adp_iso | + | | :material-reflect-horizontal: [multiplicity](parameters/structure/atom_site.md#atom-site-multiplicity) | atom_sites['ID'].multiplicity | + | | :material-reflect-horizontal: [wyckoff_letter](parameters/structure/atom_site.md#atom-site-wyckoff-letter) | atom_sites['ID'].wyckoff_letter | + +=== "Keys in Edi" + + | Category | Parameter | Key in Edi | + | --- | --- | --- | + | :material-space-station: [space_group][space_group] | :material-tag: [name_h_m](parameters/structure/space_group.md#space-group-name-h-m) | `_space_group.name_h_m` | + | | :material-numeric: [coord_system_code](parameters/structure/space_group.md#space-group-coord-system-code) | `_space_group.coord_system_code` | + | :material-cube-outline: [cell][cell] | :material-ruler: [length_a](parameters/structure/cell.md#cell-length-a) | `_cell.length_a` | + | | :material-ruler: [length_b](parameters/structure/cell.md#cell-length-b) | `_cell.length_b` | + | | :material-ruler: [length_c](parameters/structure/cell.md#cell-length-c) | `_cell.length_c` | + | | :material-angle-acute: [angle_alpha](parameters/structure/cell.md#cell-angle-alpha) | `_cell.angle_alpha` | + | | :material-angle-acute: [angle_beta](parameters/structure/cell.md#cell-angle-beta) | `_cell.angle_beta` | + | | :material-angle-acute: [angle_gamma](parameters/structure/cell.md#cell-angle-gamma) | `_cell.angle_gamma` | + | :material-atom: [atom_site][atom_site] | :material-tag: [id](parameters/structure/atom_site.md#atom-site-id) | `_atom_site.id` | + | | :material-periodic-table: [type_symbol](parameters/structure/atom_site.md#atom-site-type-symbol) | `_atom_site.type_symbol` | + | | :material-map-marker: [fract_x](parameters/structure/atom_site.md#atom-site-fract-x) | `_atom_site.fract_x` | + | | :material-map-marker: [fract_y](parameters/structure/atom_site.md#atom-site-fract-y) | `_atom_site.fract_y` | + | | :material-map-marker: [fract_z](parameters/structure/atom_site.md#atom-site-fract-z) | `_atom_site.fract_z` | + | | :material-format-color-fill: [occupancy](parameters/structure/atom_site.md#atom-site-occupancy) | `_atom_site.occupancy` | + | | :material-cursor-move: [adp_type](parameters/structure/atom_site.md#atom-site-adp-type) | `_atom_site.adp_type` | + | | :material-cursor-move: [adp_iso](parameters/structure/atom_site.md#atom-site-adp-iso) | `_atom_site.adp_iso` | + | | :material-reflect-horizontal: [multiplicity](parameters/structure/atom_site.md#atom-site-multiplicity) | `_atom_site.multiplicity` | + | | :material-reflect-horizontal: [wyckoff_letter](parameters/structure/atom_site.md#atom-site-wyckoff-letter) | `_atom_site.wyckoff_letter` | + +=== "Keys in CIF" + + | Category | Parameter | Key in CIF | CIF dictionary | + | --- | --- | --- | --- | + | :material-space-station: [space_group][space_group] | :material-tag: [name_h_m](parameters/structure/space_group.md#space-group-name-h-m) | `_space_group.name_H-M_alt` | [coreCIF][1]{:.label-cif} | + | | :material-numeric: [coord_system_code](parameters/structure/space_group.md#space-group-coord-system-code) | `_space_group.IT_coordinate_system_code` | [coreCIF][1]{:.label-cif} | + | :material-cube-outline: [cell][cell] | :material-ruler: [length_a](parameters/structure/cell.md#cell-length-a) | `_cell.length_a` | [coreCIF][1]{:.label-cif} | + | | :material-ruler: [length_b](parameters/structure/cell.md#cell-length-b) | `_cell.length_b` | [coreCIF][1]{:.label-cif} | + | | :material-ruler: [length_c](parameters/structure/cell.md#cell-length-c) | `_cell.length_c` | [coreCIF][1]{:.label-cif} | + | | :material-angle-acute: [angle_alpha](parameters/structure/cell.md#cell-angle-alpha) | `_cell.angle_alpha` | [coreCIF][1]{:.label-cif} | + | | :material-angle-acute: [angle_beta](parameters/structure/cell.md#cell-angle-beta) | `_cell.angle_beta` | [coreCIF][1]{:.label-cif} | + | | :material-angle-acute: [angle_gamma](parameters/structure/cell.md#cell-angle-gamma) | `_cell.angle_gamma` | [coreCIF][1]{:.label-cif} | + | :material-atom: [atom_site][atom_site] | :material-tag: [id](parameters/structure/atom_site.md#atom-site-id) | `_atom_site.label` | [coreCIF][1]{:.label-cif} | + | | :material-periodic-table: [type_symbol](parameters/structure/atom_site.md#atom-site-type-symbol) | `_atom_site.type_symbol` | [coreCIF][1]{:.label-cif} | + | | :material-map-marker: [fract_x](parameters/structure/atom_site.md#atom-site-fract-x) | `_atom_site.fract_x` | [coreCIF][1]{:.label-cif} | + | | :material-map-marker: [fract_y](parameters/structure/atom_site.md#atom-site-fract-y) | `_atom_site.fract_y` | [coreCIF][1]{:.label-cif} | + | | :material-map-marker: [fract_z](parameters/structure/atom_site.md#atom-site-fract-z) | `_atom_site.fract_z` | [coreCIF][1]{:.label-cif} | + | | :material-format-color-fill: [occupancy](parameters/structure/atom_site.md#atom-site-occupancy) | `_atom_site.occupancy` | [coreCIF][1]{:.label-cif} | + | | :material-cursor-move: [adp_type](parameters/structure/atom_site.md#atom-site-adp-type) | `_atom_site.ADP_type` | [coreCIF][1]{:.label-cif} | + | | :material-cursor-move: [adp_iso](parameters/structure/atom_site.md#atom-site-adp-iso) | `_atom_site.B_iso_or_equiv` | [coreCIF][1]{:.label-cif} | + | | :material-reflect-horizontal: [multiplicity](parameters/structure/atom_site.md#atom-site-multiplicity) | `_atom_site.site_symmetry_multiplicity` | [coreCIF][1]{:.label-cif} | + | | :material-reflect-horizontal: [wyckoff_letter](parameters/structure/atom_site.md#atom-site-wyckoff-letter) | `_atom_site.Wyckoff_symbol` | [coreCIF][1]{:.label-cif} | + +## Experiment Parameters + +### Common Parameters [pd-neut-cwl][3]{:.label-experiment} [pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} @@ -146,163 +127,238 @@ EasyDiffraction. === "How to access in the code" - | Category | Parameter | How to access in the code | - |----------------------------------------------|---------------------------------------------------------------|----------------------------| - | :material-flask: [expt_type][expt_type] | :material-sawtooth-wave: [beam_mode][expt_type] | expt_type.beam_mode | - | | :material-radiology-box-outline: [radiation_probe][expt_type] | expt_type.radiation_probe | - | | :material-diamond-stone: [sample_form][expt_type] | expt_type.sample_form | - | | :material-chart-bell-curve: [scattering_type][expt_type] | expt_type.scattering_type | + | Category | Parameter | How to access in the code | + | --- | --- | --- | + | :material-flask: [experiment_type][experiment_type] | :material-sawtooth-wave: [beam_mode](parameters/experiment/experiment_type.md#experiment-type-beam-mode) | experiment_type.beam_mode | + | | :material-radiology-box-outline: [radiation_probe](parameters/experiment/experiment_type.md#experiment-type-radiation-probe) | experiment_type.radiation_probe | + | | :material-diamond-stone: [sample_form](parameters/experiment/experiment_type.md#experiment-type-sample-form) | experiment_type.sample_form | + | | :material-chart-bell-curve: [scattering_type](parameters/experiment/experiment_type.md#experiment-type-scattering-type) | experiment_type.scattering_type | + +=== "Keys in Edi" + + | Category | Parameter | Key in Edi | + | --- | --- | --- | + | :material-flask: [experiment_type][experiment_type] | :material-sawtooth-wave: [beam_mode](parameters/experiment/experiment_type.md#experiment-type-beam-mode) | `_experiment_type.beam_mode` | + | | :material-radiology-box-outline: [radiation_probe](parameters/experiment/experiment_type.md#experiment-type-radiation-probe) | `_experiment_type.radiation_probe` | + | | :material-diamond-stone: [sample_form](parameters/experiment/experiment_type.md#experiment-type-sample-form) | `_experiment_type.sample_form` | + | | :material-chart-bell-curve: [scattering_type](parameters/experiment/experiment_type.md#experiment-type-scattering-type) | `_experiment_type.scattering_type` | -=== "CIF name for serialization" +=== "Keys in CIF" - | Category | Parameter | CIF name for serialization | CIF dictionary | - |----------------------------------------------|---------------------------------------------------------------|------------------------------|--------------------------------------| - | :material-flask: [expt_type][expt_type] | :material-sawtooth-wave: [beam_mode][expt_type] | \_expt_type.beam_mode | [easydiffractionCIF][0]{:.label-cif} | - | | :material-radiology-box-outline: [radiation_probe][expt_type] | \_expt_type.radiation_probe | [easydiffractionCIF][0]{:.label-cif} | - | | :material-diamond-stone: [sample_form][expt_type] | \_expt_type.sample_form | [easydiffractionCIF][0]{:.label-cif} | - | | :material-chart-bell-curve: [scattering_type][expt_type] | \_expt_type.scattering_type | [easydiffractionCIF][0]{:.label-cif} | + | Category | Parameter | Key in CIF | CIF dictionary | + | --- | --- | --- | --- | + | :material-flask: [experiment_type][experiment_type] | :material-sawtooth-wave: [beam_mode](parameters/experiment/experiment_type.md#experiment-type-beam-mode) | `_easydiffraction_experiment_type.beam_mode` | [coreCIF][0]{:.label-cif} | + | | :material-radiology-box-outline: [radiation_probe](parameters/experiment/experiment_type.md#experiment-type-radiation-probe) | `_easydiffraction_experiment_type.radiation_probe` | [coreCIF][0]{:.label-cif} | + | | :material-diamond-stone: [sample_form](parameters/experiment/experiment_type.md#experiment-type-sample-form) | `_easydiffraction_experiment_type.sample_form` | [coreCIF][0]{:.label-cif} | + | | :material-chart-bell-curve: [scattering_type](parameters/experiment/experiment_type.md#experiment-type-scattering-type) | `_easydiffraction_experiment_type.scattering_type` | [coreCIF][0]{:.label-cif} | -### Standard powder diffraction +### Standard Powder Diffraction [pd-neut-cwl][3]{:.label-experiment} [pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} === "How to access in the code" - | Category | Parameter | How to access in the code | - |--------------------------------------------------|------------------------------------------------------------|----------------------------------| - | :material-waveform: [background][background] | :material-arrow-collapse-right: [x][background] | background.x | - | | :material-arrow-collapse-up: [y][background] | background.y | - | | :material-format-superscript: [order][background] | background.order | - | | :material-arrow-collapse-up: [coef][background] | background.coef | - | :material-puzzle: [linked_phases][linked_phases] | :material-scale: [scale][linked_phases] | linked_phases['ID'].scale | - | :material-compass-outline: [pref_orient][pref_orient] | :material-chart-bell-curve-cumulative: [march_r][pref_orient] | preferred_orientation['ID'].march_r | - | | :material-shuffle-variant: [march_random_fract][pref_orient] | preferred_orientation['ID'].march_random_fract | - | | :material-axis-arrow: [index_h][pref_orient] | preferred_orientation['ID'].index_h | - | | :material-axis-arrow: [index_k][pref_orient] | preferred_orientation['ID'].index_k | - | | :material-axis-arrow: [index_l][pref_orient] | preferred_orientation['ID'].index_l | - | :material-blur: [absorption][absorption] | :material-format-list-bulleted-type: [type][absorption] | absorption.type | - | | :material-circle-opacity: [mu_r][absorption] | absorption.mu_r | - -=== "CIF name for serialization" - - | Category | Parameter | CIF name for serialization | CIF dictionary | - |--------------------------------------------------|------------------------------------------------------------|----------------------------------------|-------------------------| - | :material-waveform: [background][background] | :material-arrow-collapse-right: [x][background] | \_pd_background.line_segment_X | [pdCIF][0]{:.label-cif} | - | | :material-arrow-collapse-up: [y][background] | \_pd_background.line_segment_intensity | [pdCIF][0]{:.label-cif} | - | | :material-format-superscript: [order][background] | \_pd_background.chebyshev_order | [pdCIF][0]{:.label-cif} | - | | :material-arrow-collapse-up: [coef][background] | \_pd_background.chebyshev_coef | [pdCIF][0]{:.label-cif} | - | :material-puzzle: [linked_phases][linked_phases] | :material-scale: [scale][linked_phases] | \_pd_phase_block.scale | [pdCIF][0]{:.label-cif} | - | :material-compass-outline: [pref_orient][pref_orient] | :material-chart-bell-curve-cumulative: [march_r][pref_orient] | \_pref_orient.march_r | [easydiffractionCIF][0]{:.label-cif} | - | | :material-shuffle-variant: [march_random_fract][pref_orient] | \_pref_orient.march_random_fract | [easydiffractionCIF][0]{:.label-cif} | - | | :material-axis-arrow: [index_h][pref_orient] | \_pref_orient.index_h | [easydiffractionCIF][0]{:.label-cif} | - | | :material-axis-arrow: [index_k][pref_orient] | \_pref_orient.index_k | [easydiffractionCIF][0]{:.label-cif} | - | | :material-axis-arrow: [index_l][pref_orient] | \_pref_orient.index_l | [easydiffractionCIF][0]{:.label-cif} | - | :material-blur: [absorption][absorption] | :material-format-list-bulleted-type: [type][absorption] | \_absorption.type | [easydiffractionCIF][0]{:.label-cif} | - | | :material-circle-opacity: [mu_r][absorption] | \_absorption.mu_r | [easydiffractionCIF][0]{:.label-cif} | + | Category | Parameter | How to access in the code | + | --- | --- | --- | + | :material-waveform: [background][background] | :material-arrow-collapse-right: [position](parameters/experiment/background.md#background-position) | background.position | + | | :material-arrow-collapse-up: [intensity](parameters/experiment/background.md#background-intensity) | background.intensity | + | | :material-format-superscript: [order](parameters/experiment/background.md#background-order) | background.order | + | | :material-arrow-collapse-up: [coef](parameters/experiment/background.md#background-coef) | background.coef | + | :material-puzzle: [linked_structure][linked_structure] | :material-identifier: [structure_id](parameters/experiment/linked_structure.md#linked-structure-structure-id) | linked_structures['ID'].structure_id | + | | :material-scale: [scale](parameters/experiment/linked_structure.md#linked-structure-scale) | linked_structures['ID'].scale | + | :material-compass-outline: [preferred_orientation][preferred_orientation] | :material-identifier: [structure_id](parameters/experiment/preferred_orientation.md#preferred-orientation-structure-id) | preferred_orientation['ID'].structure_id | + | | :material-chart-bell-curve-cumulative: [march_r](parameters/experiment/preferred_orientation.md#preferred-orientation-march-r) | preferred_orientation['ID'].march_r | + | | :material-shuffle-variant: [march_random_fract](parameters/experiment/preferred_orientation.md#preferred-orientation-march-random-fract) | preferred_orientation['ID'].march_random_fract | + | | :material-axis-arrow: [index_h](parameters/experiment/preferred_orientation.md#preferred-orientation-index-h) | preferred_orientation['ID'].index_h | + | | :material-axis-arrow: [index_k](parameters/experiment/preferred_orientation.md#preferred-orientation-index-k) | preferred_orientation['ID'].index_k | + | | :material-axis-arrow: [index_l](parameters/experiment/preferred_orientation.md#preferred-orientation-index-l) | preferred_orientation['ID'].index_l | + +=== "Keys in Edi" + + | Category | Parameter | Key in Edi | + | --- | --- | --- | + | :material-waveform: [background][background] | :material-arrow-collapse-right: [position](parameters/experiment/background.md#background-position) | `_background.position` | + | | :material-arrow-collapse-up: [intensity](parameters/experiment/background.md#background-intensity) | `_background.intensity` | + | | :material-format-superscript: [order](parameters/experiment/background.md#background-order) | `_background.order` | + | | :material-arrow-collapse-up: [coef](parameters/experiment/background.md#background-coef) | `_background.coef` | + | :material-puzzle: [linked_structure][linked_structure] | :material-identifier: [structure_id](parameters/experiment/linked_structure.md#linked-structure-structure-id) | `_linked_structure.structure_id` | + | | :material-scale: [scale](parameters/experiment/linked_structure.md#linked-structure-scale) | `_linked_structure.scale` | + | :material-compass-outline: [preferred_orientation][preferred_orientation] | :material-identifier: [structure_id](parameters/experiment/preferred_orientation.md#preferred-orientation-structure-id) | `_preferred_orientation.structure_id` | + | | :material-chart-bell-curve-cumulative: [march_r](parameters/experiment/preferred_orientation.md#preferred-orientation-march-r) | `_preferred_orientation.march_r` | + | | :material-shuffle-variant: [march_random_fract](parameters/experiment/preferred_orientation.md#preferred-orientation-march-random-fract) | `_preferred_orientation.march_random_fract` | + | | :material-axis-arrow: [index_h](parameters/experiment/preferred_orientation.md#preferred-orientation-index-h) | `_preferred_orientation.index_h` | + | | :material-axis-arrow: [index_k](parameters/experiment/preferred_orientation.md#preferred-orientation-index-k) | `_preferred_orientation.index_k` | + | | :material-axis-arrow: [index_l](parameters/experiment/preferred_orientation.md#preferred-orientation-index-l) | `_preferred_orientation.index_l` | + +=== "Keys in CIF" + + | Category | Parameter | Key in CIF | CIF dictionary | + | --- | --- | --- | --- | + | :material-waveform: [background][background] | :material-arrow-collapse-right: [position](parameters/experiment/background.md#background-position) | `_pd_background.line_segment_X` | [pdCIF][2]{:.label-cif} | + | | :material-arrow-collapse-up: [intensity](parameters/experiment/background.md#background-intensity) | `_pd_background.line_segment_intensity` | [pdCIF][2]{:.label-cif} | + | | :material-format-superscript: [order](parameters/experiment/background.md#background-order) | `_pd_background.Chebyshev_order` | [pdCIF][2]{:.label-cif} | + | | :material-arrow-collapse-up: [coef](parameters/experiment/background.md#background-coef) | `_pd_background.Chebyshev_coef` | [pdCIF][2]{:.label-cif} | + | :material-puzzle: [linked_structure][linked_structure] | :material-identifier: [structure_id](parameters/experiment/linked_structure.md#linked-structure-structure-id) | `_pd_phase_block.id` | [pdCIF][2]{:.label-cif} | + | | :material-scale: [scale](parameters/experiment/linked_structure.md#linked-structure-scale) | `_pd_phase_block.scale` | [pdCIF][2]{:.label-cif} | + | :material-compass-outline: [preferred_orientation][preferred_orientation] | :material-identifier: [structure_id](parameters/experiment/preferred_orientation.md#preferred-orientation-structure-id) | `_pd_pref_orient_March_Dollase.phase_id` | [pdCIF][2]{:.label-cif} | + | | :material-chart-bell-curve-cumulative: [march_r](parameters/experiment/preferred_orientation.md#preferred-orientation-march-r) | `_pd_pref_orient_March_Dollase.r` | [pdCIF][2]{:.label-cif} | + | | :material-shuffle-variant: [march_random_fract](parameters/experiment/preferred_orientation.md#preferred-orientation-march-random-fract) | `_easydiffraction_pref_orient.march_random_fract` | [coreCIF][0]{:.label-cif} | + | | :material-axis-arrow: [index_h](parameters/experiment/preferred_orientation.md#preferred-orientation-index-h) | `_pd_pref_orient_March_Dollase.index_h` | [pdCIF][2]{:.label-cif} | + | | :material-axis-arrow: [index_k](parameters/experiment/preferred_orientation.md#preferred-orientation-index-k) | `_pd_pref_orient_March_Dollase.index_k` | [pdCIF][2]{:.label-cif} | + | | :material-axis-arrow: [index_l](parameters/experiment/preferred_orientation.md#preferred-orientation-index-l) | `_pd_pref_orient_March_Dollase.index_l` | [pdCIF][2]{:.label-cif} | [pd-neut-cwl][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} === "How to access in the code" - | Category | Parameter | How to access in the code | - |------------------------------------------------|------------------------------------------------------------|----------------------------------| - | :material-microscope: [instrument][instrument] | :material-wrench: [setup_wavelength][instrument] | instrument.setup_wavelength | - | | :material-tune: [calib_twotheta_offset][instrument] | instrument.calib_twotheta_offset | - | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_u][peak] | peak.broad_gauss_u | - | | :material-arrow-expand-horizontal: [broad_gauss_v][peak] | peak.broad_gauss_v | - | | :material-arrow-expand-horizontal: [broad_gauss_w][peak] | peak.broad_gauss_w | - | | :material-arrow-expand-horizontal: [broad_lorentz_x][peak] | peak.broad_lorentz_x | - | | :material-arrow-expand-horizontal: [broad_lorentz_y][peak] | peak.broad_lorentz_y | - -=== "CIF name for serialization" - - | Category | Parameter | CIF name for serialization | CIF dictionary | - |------------------------------------------------|------------------------------------------------------------|------------------------------------|--------------------------------------| - | :material-microscope: [instrument][instrument] | :material-wrench: [setup_wavelength][instrument] | \_instr.wavelength | [easydiffractionCIF][0]{:.label-cif} | - | | :material-tune: [calib_twotheta_offset][instrument] | \_instr.2theta_offset | [easydiffractionCIF][0]{:.label-cif} | - | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_u][peak] | \_peak.broad_gauss_u | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [broad_gauss_v][peak] | \_peak.broad_gauss_v | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [broad_gauss_w][peak] | \_peak.broad_gauss_w | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [broad_lorentz_x][peak] | \_peak.broad_lorentz_x | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [broad_lorentz_y][peak] | \_peak.broad_lorentz_y | [easydiffractionCIF][0]{:.label-cif} | + | Category | Parameter | How to access in the code | + | --- | --- | --- | + | :material-microscope: [instrument][instrument] | :material-wrench: [setup_wavelength](parameters/experiment/instrument.md#instrument-setup-wavelength) | instrument.setup_wavelength | + | | :material-tune: [calib_twotheta_offset](parameters/experiment/instrument.md#instrument-calib-twotheta-offset) | instrument.calib_twotheta_offset | + | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_u](parameters/experiment/peak.md#peak-broad-gauss-u) | peak.broad_gauss_u | + | | :material-arrow-expand-horizontal: [broad_gauss_v](parameters/experiment/peak.md#peak-broad-gauss-v) | peak.broad_gauss_v | + | | :material-arrow-expand-horizontal: [broad_gauss_w](parameters/experiment/peak.md#peak-broad-gauss-w) | peak.broad_gauss_w | + | | :material-arrow-expand-horizontal: [broad_lorentz_x](parameters/experiment/peak.md#peak-broad-lorentz-x) | peak.broad_lorentz_x | + | | :material-arrow-expand-horizontal: [broad_lorentz_y](parameters/experiment/peak.md#peak-broad-lorentz-y) | peak.broad_lorentz_y | + +=== "Keys in Edi" + + | Category | Parameter | Key in Edi | + | --- | --- | --- | + | :material-microscope: [instrument][instrument] | :material-wrench: [setup_wavelength](parameters/experiment/instrument.md#instrument-setup-wavelength) | `_instrument.setup_wavelength` | + | | :material-tune: [calib_twotheta_offset](parameters/experiment/instrument.md#instrument-calib-twotheta-offset) | `_instrument.calib_twotheta_offset` | + | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_u](parameters/experiment/peak.md#peak-broad-gauss-u) | `_peak.broad_gauss_u` | + | | :material-arrow-expand-horizontal: [broad_gauss_v](parameters/experiment/peak.md#peak-broad-gauss-v) | `_peak.broad_gauss_v` | + | | :material-arrow-expand-horizontal: [broad_gauss_w](parameters/experiment/peak.md#peak-broad-gauss-w) | `_peak.broad_gauss_w` | + | | :material-arrow-expand-horizontal: [broad_lorentz_x](parameters/experiment/peak.md#peak-broad-lorentz-x) | `_peak.broad_lorentz_x` | + | | :material-arrow-expand-horizontal: [broad_lorentz_y](parameters/experiment/peak.md#peak-broad-lorentz-y) | `_peak.broad_lorentz_y` | + +=== "Keys in CIF" + + | Category | Parameter | Key in CIF | CIF dictionary | + | --- | --- | --- | --- | + | :material-microscope: [instrument][instrument] | :material-wrench: [setup_wavelength](parameters/experiment/instrument.md#instrument-setup-wavelength) | `_diffrn_radiation_wavelength.value` | [coreCIF][1]{:.label-cif} | + | | :material-tune: [calib_twotheta_offset](parameters/experiment/instrument.md#instrument-calib-twotheta-offset) | `_pd_calib.2theta_offset` | [pdCIF][2]{:.label-cif} | + | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_u](parameters/experiment/peak.md#peak-broad-gauss-u) | `_easydiffraction_peak.broad_gauss_u` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_gauss_v](parameters/experiment/peak.md#peak-broad-gauss-v) | `_easydiffraction_peak.broad_gauss_v` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_gauss_w](parameters/experiment/peak.md#peak-broad-gauss-w) | `_easydiffraction_peak.broad_gauss_w` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_lorentz_x](parameters/experiment/peak.md#peak-broad-lorentz-x) | `_easydiffraction_peak.broad_lorentz_x` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_lorentz_y](parameters/experiment/peak.md#peak-broad-lorentz-y) | `_easydiffraction_peak.broad_lorentz_y` | [coreCIF][0]{:.label-cif} | [pd-neut-tof][3]{:.label-experiment} === "How to access in the code" - | Category | Parameter | How to access in the code | - |------------------------------------------------|----------------------------------------------------------------|----------------------------------| - | :material-microscope: [instrument][instrument] | :material-wrench: [setup_twotheta_bank][instrument] | instrument.setup_twotheta_bank | - | | :material-tune: [calib_d_to_tof_recip][instrument] | instrument.calib_d_to_tof_recip | - | | :material-tune: [calib_d_to_tof_offset][instrument] | instrument.calib_d_to_tof_offset | - | | :material-tune: [calib_d_to_tof_linear][instrument] | instrument.calib_d_to_tof_linear | - | | :material-tune: [calib_d_to_tof_quad][instrument] | instrument.calib_d_to_tof_quad | - | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_sigma_0][peak] | peak.broad_gauss_sigma_0 | - | | :material-arrow-expand-horizontal: [broad_gauss_sigma_1][peak] | peak.broad_gauss_sigma_1 | - | | :material-arrow-expand-horizontal: [broad_gauss_sigma_2][peak] | peak.broad_gauss_sigma_2 | - | | :material-arrow-expand-horizontal: [exp_decay_beta_0][peak] | peak.exp_decay_beta_0 | - | | :material-arrow-expand-horizontal: [exp_decay_beta_1][peak] | peak.exp_decay_beta_1 | - | | :material-scale-unbalanced: [exp_rise_alpha_0][peak] | peak.exp_rise_alpha_0 | - | | :material-scale-unbalanced: [exp_rise_alpha_1][peak] | peak.exp_rise_alpha_1 | - -=== "CIF name for serialization" - - | Category | Parameter | CIF name for serialization | CIF dictionary | - |------------------------------------------------|----------------------------------------------------------------|------------------------------------|--------------------------------------| - | :material-microscope: [instrument][instrument] | :material-wrench: [setup_twotheta_bank][instrument] | \_instr.2theta_bank | [easydiffractionCIF][0]{:.label-cif} | - | | :material-tune: [calib_d_to_tof_recip][instrument] | \_instr.d_to_tof_recip | [easydiffractionCIF][0]{:.label-cif} | - | | :material-tune: [calib_d_to_tof_offset][instrument] | \_instr.d_to_tof_offset | [easydiffractionCIF][0]{:.label-cif} | - | | :material-tune: [calib_d_to_tof_linear][instrument] | \_instr.d_to_tof_linear | [easydiffractionCIF][0]{:.label-cif} | - | | :material-tune: [calib_d_to_tof_quad][instrument] | \_instr.d_to_tof_quad | [easydiffractionCIF][0]{:.label-cif} | - | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_sigma_0][peak] | \_peak.gauss_sigma_0 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [broad_gauss_sigma_1][peak] | \_peak.gauss_sigma_1 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [broad_gauss_sigma_2][peak] | \_peak.gauss_sigma_2 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [exp_decay_beta_0][peak] | \_peak.decay_beta_0 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [exp_decay_beta_1][peak] | \_peak.decay_beta_1 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-scale-unbalanced: [exp_rise_alpha_0][peak] | \_peak.rise_alpha_0 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-scale-unbalanced: [exp_rise_alpha_1][peak] | \_peak.rise_alpha_1 | [easydiffractionCIF][0]{:.label-cif} | - -### Total scattering + | Category | Parameter | How to access in the code | + | --- | --- | --- | + | :material-microscope: [instrument][instrument] | :material-wrench: [setup_twotheta_bank](parameters/experiment/instrument.md#instrument-setup-twotheta-bank) | instrument.setup_twotheta_bank | + | | :material-tune: [calib_d_to_tof_reciprocal](parameters/experiment/instrument.md#instrument-calib-d-to-tof-reciprocal) | instrument.calib_d_to_tof_reciprocal | + | | :material-tune: [calib_d_to_tof_offset](parameters/experiment/instrument.md#instrument-calib-d-to-tof-offset) | instrument.calib_d_to_tof_offset | + | | :material-tune: [calib_d_to_tof_linear](parameters/experiment/instrument.md#instrument-calib-d-to-tof-linear) | instrument.calib_d_to_tof_linear | + | | :material-tune: [calib_d_to_tof_quadratic](parameters/experiment/instrument.md#instrument-calib-d-to-tof-quadratic) | instrument.calib_d_to_tof_quadratic | + | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_sigma_0](parameters/experiment/peak.md#peak-broad-gauss-sigma-0) | peak.broad_gauss_sigma_0 | + | | :material-arrow-expand-horizontal: [broad_gauss_sigma_1](parameters/experiment/peak.md#peak-broad-gauss-sigma-1) | peak.broad_gauss_sigma_1 | + | | :material-arrow-expand-horizontal: [broad_gauss_sigma_2](parameters/experiment/peak.md#peak-broad-gauss-sigma-2) | peak.broad_gauss_sigma_2 | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_0](parameters/experiment/peak.md#peak-broad-lorentz-gamma-0) | peak.broad_lorentz_gamma_0 | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_1](parameters/experiment/peak.md#peak-broad-lorentz-gamma-1) | peak.broad_lorentz_gamma_1 | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_2](parameters/experiment/peak.md#peak-broad-lorentz-gamma-2) | peak.broad_lorentz_gamma_2 | + | | :material-arrow-bottom-right: [decay_beta_0](parameters/experiment/peak.md#peak-decay-beta-0) | peak.decay_beta_0 | + | | :material-arrow-bottom-right: [decay_beta_1](parameters/experiment/peak.md#peak-decay-beta-1) | peak.decay_beta_1 | + | | :material-scale-unbalanced: [rise_alpha_0](parameters/experiment/peak.md#peak-rise-alpha-0) | peak.rise_alpha_0 | + | | :material-scale-unbalanced: [rise_alpha_1](parameters/experiment/peak.md#peak-rise-alpha-1) | peak.rise_alpha_1 | + +=== "Keys in Edi" + + | Category | Parameter | Key in Edi | + | --- | --- | --- | + | :material-microscope: [instrument][instrument] | :material-wrench: [setup_twotheta_bank](parameters/experiment/instrument.md#instrument-setup-twotheta-bank) | `_instrument.setup_twotheta_bank` | + | | :material-tune: [calib_d_to_tof_reciprocal](parameters/experiment/instrument.md#instrument-calib-d-to-tof-reciprocal) | `_instrument.calib_d_to_tof_reciprocal` | + | | :material-tune: [calib_d_to_tof_offset](parameters/experiment/instrument.md#instrument-calib-d-to-tof-offset) | `_instrument.calib_d_to_tof_offset` | + | | :material-tune: [calib_d_to_tof_linear](parameters/experiment/instrument.md#instrument-calib-d-to-tof-linear) | `_instrument.calib_d_to_tof_linear` | + | | :material-tune: [calib_d_to_tof_quadratic](parameters/experiment/instrument.md#instrument-calib-d-to-tof-quadratic) | `_instrument.calib_d_to_tof_quadratic` | + | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_sigma_0](parameters/experiment/peak.md#peak-broad-gauss-sigma-0) | `_peak.broad_gauss_sigma_0` | + | | :material-arrow-expand-horizontal: [broad_gauss_sigma_1](parameters/experiment/peak.md#peak-broad-gauss-sigma-1) | `_peak.broad_gauss_sigma_1` | + | | :material-arrow-expand-horizontal: [broad_gauss_sigma_2](parameters/experiment/peak.md#peak-broad-gauss-sigma-2) | `_peak.broad_gauss_sigma_2` | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_0](parameters/experiment/peak.md#peak-broad-lorentz-gamma-0) | `_peak.broad_lorentz_gamma_0` | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_1](parameters/experiment/peak.md#peak-broad-lorentz-gamma-1) | `_peak.broad_lorentz_gamma_1` | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_2](parameters/experiment/peak.md#peak-broad-lorentz-gamma-2) | `_peak.broad_lorentz_gamma_2` | + | | :material-arrow-bottom-right: [decay_beta_0](parameters/experiment/peak.md#peak-decay-beta-0) | `_peak.decay_beta_0` | + | | :material-arrow-bottom-right: [decay_beta_1](parameters/experiment/peak.md#peak-decay-beta-1) | `_peak.decay_beta_1` | + | | :material-scale-unbalanced: [rise_alpha_0](parameters/experiment/peak.md#peak-rise-alpha-0) | `_peak.rise_alpha_0` | + | | :material-scale-unbalanced: [rise_alpha_1](parameters/experiment/peak.md#peak-rise-alpha-1) | `_peak.rise_alpha_1` | + +=== "Keys in CIF" + + | Category | Parameter | Key in CIF | CIF dictionary | + | --- | --- | --- | --- | + | :material-microscope: [instrument][instrument] | :material-wrench: [setup_twotheta_bank](parameters/experiment/instrument.md#instrument-setup-twotheta-bank) | `_instr.2theta_bank` | [coreCIF][0]{:.label-cif} | + | | :material-tune: [calib_d_to_tof_reciprocal](parameters/experiment/instrument.md#instrument-calib-d-to-tof-reciprocal) | `_instr.d_to_tof_recip` | [coreCIF][0]{:.label-cif} | + | | :material-tune: [calib_d_to_tof_offset](parameters/experiment/instrument.md#instrument-calib-d-to-tof-offset) | `_instr.d_to_tof_offset` | [coreCIF][0]{:.label-cif} | + | | :material-tune: [calib_d_to_tof_linear](parameters/experiment/instrument.md#instrument-calib-d-to-tof-linear) | `_instr.d_to_tof_linear` | [coreCIF][0]{:.label-cif} | + | | :material-tune: [calib_d_to_tof_quadratic](parameters/experiment/instrument.md#instrument-calib-d-to-tof-quadratic) | `_instr.d_to_tof_quad` | [coreCIF][0]{:.label-cif} | + | :material-shape: [peak][peak] | :material-arrow-expand-horizontal: [broad_gauss_sigma_0](parameters/experiment/peak.md#peak-broad-gauss-sigma-0) | `_easydiffraction_peak.gauss_sigma_0` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_gauss_sigma_1](parameters/experiment/peak.md#peak-broad-gauss-sigma-1) | `_easydiffraction_peak.gauss_sigma_1` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_gauss_sigma_2](parameters/experiment/peak.md#peak-broad-gauss-sigma-2) | `_easydiffraction_peak.gauss_sigma_2` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_0](parameters/experiment/peak.md#peak-broad-lorentz-gamma-0) | `_easydiffraction_peak.lorentz_gamma_0` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_1](parameters/experiment/peak.md#peak-broad-lorentz-gamma-1) | `_easydiffraction_peak.lorentz_gamma_1` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_lorentz_gamma_2](parameters/experiment/peak.md#peak-broad-lorentz-gamma-2) | `_easydiffraction_peak.lorentz_gamma_2` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-bottom-right: [decay_beta_0](parameters/experiment/peak.md#peak-decay-beta-0) | `_easydiffraction_peak.decay_beta_0` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-bottom-right: [decay_beta_1](parameters/experiment/peak.md#peak-decay-beta-1) | `_easydiffraction_peak.decay_beta_1` | [coreCIF][0]{:.label-cif} | + | | :material-scale-unbalanced: [rise_alpha_0](parameters/experiment/peak.md#peak-rise-alpha-0) | `_easydiffraction_peak.rise_alpha_0` | [coreCIF][0]{:.label-cif} | + | | :material-scale-unbalanced: [rise_alpha_1](parameters/experiment/peak.md#peak-rise-alpha-1) | `_easydiffraction_peak.rise_alpha_1` | [coreCIF][0]{:.label-cif} | + +### Total Scattering [pd-neut-total][3]{:.label-experiment} [pd-xray-total][3]{:.label-experiment} === "How to access in the code" - | Category | Parameter | How to access in the code | - |------------------------------------------------|--------------------------------------------------------------|----------------------------------| - | :material-shape: [peak][peak] | :material-content-cut: [cutoff_q][peak] | peak.cutoff_q | - | | :material-arrow-expand-horizontal: [broad_q][peak] | peak.broad_q | - | | :material-knife: [sharp_delta_1][peak] | peak.sharp_delta_1 | - | | :material-knife: [sharp_delta_2][peak] | peak.sharp_delta_2 | - | | :material-arrow-bottom-right: [damp_q][peak] | peak.damp_q | - | | :material-arrow-bottom-right: [damp_particle_diameter][peak] | peak.damp_particle_diameter | - -=== "CIF name for serialization" - - | Category | Parameter | CIF name for serialization | CIF dictionary | - |------------------------------------------------|--------------------------------------------------------------|-------------------------------|--------------------------------------| - | :material-shape: [peak][peak] | :material-content-cut: [cutoff_q][peak] | \_peak.cutoff_q | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-expand-horizontal: [broad_q][peak] | \_peak.broad_q | [easydiffractionCIF][0]{:.label-cif} | - | | :material-knife: [sharp_delta_1][peak] | \_peak.sharp_delta_1 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-knife: [sharp_delta_2][peak] | \_peak.sharp_delta_2 | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-bottom-right: [damp_q][peak] | \_peak.damp_q | [easydiffractionCIF][0]{:.label-cif} | - | | :material-arrow-bottom-right: [damp_particle_diameter][peak] | \_peak.damp_particle_diameter | [easydiffractionCIF][0]{:.label-cif} | + | Category | Parameter | How to access in the code | + | --- | --- | --- | + | :material-shape: [peak][peak] | :material-content-cut: [cutoff_q](parameters/experiment/peak.md#peak-cutoff-q) | peak.cutoff_q | + | | :material-arrow-expand-horizontal: [broad_q](parameters/experiment/peak.md#peak-broad-q) | peak.broad_q | + | | :material-knife: [sharp_delta_1](parameters/experiment/peak.md#peak-sharp-delta-1) | peak.sharp_delta_1 | + | | :material-knife: [sharp_delta_2](parameters/experiment/peak.md#peak-sharp-delta-2) | peak.sharp_delta_2 | + | | :material-arrow-bottom-right: [damp_q](parameters/experiment/peak.md#peak-damp-q) | peak.damp_q | + | | :material-arrow-bottom-right: [damp_particle_diameter](parameters/experiment/peak.md#peak-damp-particle-diameter) | peak.damp_particle_diameter | + +=== "Keys in Edi" + + | Category | Parameter | Key in Edi | + | --- | --- | --- | + | :material-shape: [peak][peak] | :material-content-cut: [cutoff_q](parameters/experiment/peak.md#peak-cutoff-q) | `_peak.cutoff_q` | + | | :material-arrow-expand-horizontal: [broad_q](parameters/experiment/peak.md#peak-broad-q) | `_peak.broad_q` | + | | :material-knife: [sharp_delta_1](parameters/experiment/peak.md#peak-sharp-delta-1) | `_peak.sharp_delta_1` | + | | :material-knife: [sharp_delta_2](parameters/experiment/peak.md#peak-sharp-delta-2) | `_peak.sharp_delta_2` | + | | :material-arrow-bottom-right: [damp_q](parameters/experiment/peak.md#peak-damp-q) | `_peak.damp_q` | + | | :material-arrow-bottom-right: [damp_particle_diameter](parameters/experiment/peak.md#peak-damp-particle-diameter) | `_peak.damp_particle_diameter` | + +=== "Keys in CIF" + + | Category | Parameter | Key in CIF | CIF dictionary | + | --- | --- | --- | --- | + | :material-shape: [peak][peak] | :material-content-cut: [cutoff_q](parameters/experiment/peak.md#peak-cutoff-q) | `_easydiffraction_peak.cutoff_q` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-expand-horizontal: [broad_q](parameters/experiment/peak.md#peak-broad-q) | `_easydiffraction_peak.broad_q` | [coreCIF][0]{:.label-cif} | + | | :material-knife: [sharp_delta_1](parameters/experiment/peak.md#peak-sharp-delta-1) | `_easydiffraction_peak.sharp_delta_1` | [coreCIF][0]{:.label-cif} | + | | :material-knife: [sharp_delta_2](parameters/experiment/peak.md#peak-sharp-delta-2) | `_easydiffraction_peak.sharp_delta_2` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-bottom-right: [damp_q](parameters/experiment/peak.md#peak-damp-q) | `_easydiffraction_peak.damp_q` | [coreCIF][0]{:.label-cif} | + | | :material-arrow-bottom-right: [damp_particle_diameter](parameters/experiment/peak.md#peak-damp-particle-diameter) | `_easydiffraction_peak.damp_particle_diameter` | [coreCIF][0]{:.label-cif} | <!-- prettier-ignore-start --> [0]: # [1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core [2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd [3]: glossary.md#experiment-type-labels -[space_group]: parameters/space_group.md -[cell]: parameters/cell.md -[atom_site]: parameters/atom_site.md -[expt_type]: parameters/expt_type.md -[instrument]: parameters/instrument.md -[peak]: parameters/peak.md -[background]: parameters/background.md -[linked_phases]: parameters/linked_phases.md -[pref_orient]: parameters/pref_orient.md -[absorption]: parameters/absorption.md +[space_group]: parameters/structure/space_group.md +[cell]: parameters/structure/cell.md +[atom_site]: parameters/structure/atom_site.md +[experiment_type]: parameters/experiment/experiment_type.md +[instrument]: parameters/experiment/instrument.md +[peak]: parameters/experiment/peak.md +[background]: parameters/experiment/background.md +[pd_background]: parameters/experiment/pd_background.md +[linked_structure]: parameters/experiment/linked_structure.md +[preferred_orientation]: parameters/experiment/preferred_orientation.md <!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/_diffrn_radiation.md b/docs/docs/user-guide/parameters/_diffrn_radiation.md deleted file mode 100644 index 6e2f0a062..000000000 --- a/docs/docs/user-guide/parameters/_diffrn_radiation.md +++ /dev/null @@ -1,21 +0,0 @@ -[coreCIF][1]{:.label-cif} - -# \_diffrn_radiation - -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. - -## [\_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). - -Supported values: `neutron` and `x-ray` - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md b/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md deleted file mode 100644 index 3b750b220..000000000 --- a/docs/docs/user-guide/parameters/_diffrn_radiation_wavelength.md +++ /dev/null @@ -1,18 +0,0 @@ -[coreCIF][1]{:.label-cif} - -# \_diffrn_radiation_wavelength - -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. - -## [\_diffrn_radiation_wavelength.wavelength](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) - -Wavelength of the radiation used to measure the unit cell. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/_exptl_crystal.md b/docs/docs/user-guide/parameters/_exptl_crystal.md deleted file mode 100644 index 8d568b2bf..000000000 --- a/docs/docs/user-guide/parameters/_exptl_crystal.md +++ /dev/null @@ -1,7 +0,0 @@ -[customCIF][0]{:.label-cif} - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/_extinction.md b/docs/docs/user-guide/parameters/_extinction.md deleted file mode 100644 index 8d568b2bf..000000000 --- a/docs/docs/user-guide/parameters/_extinction.md +++ /dev/null @@ -1,7 +0,0 @@ -[customCIF][0]{:.label-cif} - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/_pd_calib.md b/docs/docs/user-guide/parameters/_pd_calib.md deleted file mode 100644 index 407c5f73d..000000000 --- a/docs/docs/user-guide/parameters/_pd_calib.md +++ /dev/null @@ -1,17 +0,0 @@ -[customCIF][0]{:.label-cif} - -# \_pd_calib - -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) - -An offset angle (in degrees) used to calibrate 2θ. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/absorption.md b/docs/docs/user-guide/parameters/absorption.md deleted file mode 100644 index 5ce9103ac..000000000 --- a/docs/docs/user-guide/parameters/absorption.md +++ /dev/null @@ -1,40 +0,0 @@ -[easydiffractionCIF][0]{:.label-cif} - -# \_absorption - -**Sample-absorption** correction for powder samples. A sample in a -cylindrical (Debye–Scherrer) holder absorbs the incident and diffracted -beams by an amount that depends on scattering angle, attenuating -low-angle reflections more than high-angle ones. Correcting for it -removes an angle-dependent distortion of the measured intensities. - -The correction is a switchable category: `type = 'none'` (the default) -applies no correction, while `type = 'cylinder-hewat'` applies the -cylindrical Hewat model. It is applied identically on the **CrysPy** and -**CrysFML** engines for constant-wavelength Bragg powder experiments. - -The absorption coefficient `mu_r` is normally entered as a known, -**fixed** value (it is strongly correlated with the atomic displacement -parameters and the scale). There is no IUCr standard data name for -`mu_r`, so it is serialised under the EasyDiffraction custom dictionary. - -## \_absorption.type - -Active absorption correction type. One of `none` (no correction) or -`cylinder-hewat` (cylindrical Debye–Scherrer, Hewat model). Use -`experiment.absorption.show_supported()` to list the types available for -the current experiment. - -## \_absorption.mu_r - -Linear absorption coefficient times the sample radius, `μR` -(dimensionless). Only present for `type = 'cylinder-hewat'`. The Hewat -expansion is validated to four decimals for `μR ≲ 1.5`; larger values -are still computed but emit a warning, as a Lobanov-type form is -preferable in that range. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/constraint.md b/docs/docs/user-guide/parameters/analysis/constraint.md new file mode 100644 index 000000000..3b006910e --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/constraint.md @@ -0,0 +1,29 @@ +--- +title: constraint +--- + +# :material-function-variant: constraint + +## :material-tag: expression { #constraint-expression } + +| Access | Source | +| --------------------------------------- | ------------------------- | +| constraints['ID'].expression | [code][0]{:.label-cif} | +| \_constraint.expression | [Edi][0]{:.label-cif} | +| \_easydiffraction_constraint.expression | [coreCIF][0]{:.label-cif} | + +Constraint equation, e.g. "occ_Ba = 1 - occ_La". + +## :material-tag: id { #constraint-id } + +| Access | Source | +| ------------------------------- | ------------------------- | +| constraints['ID'].id | [code][0]{:.label-cif} | +| \_constraint.id | [Edi][0]{:.label-cif} | +| \_easydiffraction_constraint.id | [coreCIF][0]{:.label-cif} | + +Explicit identifier for this constraint row. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/fit_parameter.md b/docs/docs/user-guide/parameters/analysis/fit_parameter.md new file mode 100644 index 000000000..2e2e68d3b --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/fit_parameter.md @@ -0,0 +1,144 @@ +--- +title: fit_parameter +--- + +# :material-tune-variant: fit_parameter + +## :material-tag: bounds_uncertainty_multiplier { #fit-parameter-bounds-uncertainty-multiplier } + +| Access | Source | +| -------------------------------------------------- | ---------------------- | +| fit_parameters['ID'].bounds_uncertainty_multiplier | [code][0]{:.label-cif} | +| \_fit_parameter.bounds_uncertainty_multiplier | [Edi][0]{:.label-cif} | + +Multiplier used to derive fit bounds from uncertainty. + +## :material-arrow-collapse-right: fit_max { #fit-parameter-fit-max } + +| Access | Source | +| ---------------------------- | ---------------------- | +| fit_parameters['ID'].fit_max | [code][0]{:.label-cif} | +| \_fit_parameter.fit_max | [Edi][0]{:.label-cif} | + +Persisted upper fit bound. + +## :material-arrow-collapse-left: fit_min { #fit-parameter-fit-min } + +| Access | Source | +| ---------------------------- | ---------------------- | +| fit_parameters['ID'].fit_min | [code][0]{:.label-cif} | +| \_fit_parameter.fit_min | [Edi][0]{:.label-cif} | + +Persisted lower fit bound. + +## :material-form-textbox: parameter_unique_name { #fit-parameter-parameter-unique-name } + +| Access | Source | +| ------------------------------------------ | ---------------------- | +| fit_parameters['ID'].parameter_unique_name | [code][0]{:.label-cif} | +| \_fit_parameter.parameter_unique_name | [Edi][0]{:.label-cif} | + +Unique name of the referenced live parameter. + +## :material-numeric: posterior_best_sample_value { #fit-parameter-posterior-best-sample-value } + +| Access | Source | +| ------------------------------------------------ | ---------------------- | +| fit_parameters['ID'].posterior_best_sample_value | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_best_sample_value | [Edi][0]{:.label-cif} | + +Highest-posterior sampled parameter value. + +## :material-tag: posterior_effective_sample_size_bulk { #fit-parameter-posterior-effective-sample-size-bulk } + +| Access | Source | +| --------------------------------------------------------- | ---------------------- | +| fit_parameters['ID'].posterior_effective_sample_size_bulk | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_effective_sample_size_bulk | [Edi][0]{:.label-cif} | + +Bulk effective sample size when available. + +## :material-tag: posterior_gelman_rubin { #fit-parameter-posterior-gelman-rubin } + +| Access | Source | +| ------------------------------------------- | ---------------------- | +| fit_parameters['ID'].posterior_gelman_rubin | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_gelman_rubin | [Edi][0]{:.label-cif} | + +Rank-normalized split-R-hat when available. + +## :material-tag: posterior_interval_68_high { #fit-parameter-posterior-interval-68-high } + +| Access | Source | +| ----------------------------------------------- | ---------------------- | +| fit_parameters['ID'].posterior_interval_68_high | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_interval_68_high | [Edi][0]{:.label-cif} | + +Upper bound of the 68% credible interval. + +## :material-tag: posterior_interval_68_low { #fit-parameter-posterior-interval-68-low } + +| Access | Source | +| ---------------------------------------------- | ---------------------- | +| fit_parameters['ID'].posterior_interval_68_low | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_interval_68_low | [Edi][0]{:.label-cif} | + +Lower bound of the 68% credible interval. + +## :material-tag: posterior_interval_95_high { #fit-parameter-posterior-interval-95-high } + +| Access | Source | +| ----------------------------------------------- | ---------------------- | +| fit_parameters['ID'].posterior_interval_95_high | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_interval_95_high | [Edi][0]{:.label-cif} | + +Upper bound of the 95% credible interval. + +## :material-tag: posterior_interval_95_low { #fit-parameter-posterior-interval-95-low } + +| Access | Source | +| ---------------------------------------------- | ---------------------- | +| fit_parameters['ID'].posterior_interval_95_low | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_interval_95_low | [Edi][0]{:.label-cif} | + +Lower bound of the 95% credible interval. + +## :material-tag: posterior_median { #fit-parameter-posterior-median } + +| Access | Source | +| ------------------------------------- | ---------------------- | +| fit_parameters['ID'].posterior_median | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_median | [Edi][0]{:.label-cif} | + +Posterior median value. + +## :material-tag: posterior_uncertainty { #fit-parameter-posterior-uncertainty } + +| Access | Source | +| ------------------------------------------ | ---------------------- | +| fit_parameters['ID'].posterior_uncertainty | [code][0]{:.label-cif} | +| \_fit_parameter.posterior_uncertainty | [Edi][0]{:.label-cif} | + +Posterior standard deviation. + +## :material-play: start_uncertainty { #fit-parameter-start-uncertainty } + +| Access | Source | +| -------------------------------------- | ---------------------- | +| fit_parameters['ID'].start_uncertainty | [code][0]{:.label-cif} | +| \_fit_parameter.start_uncertainty | [Edi][0]{:.label-cif} | + +Persisted pre-fit uncertainty snapshot. + +## :material-play: start_value { #fit-parameter-start-value } + +| Access | Source | +| -------------------------------- | ---------------------- | +| fit_parameters['ID'].start_value | [code][0]{:.label-cif} | +| \_fit_parameter.start_value | [Edi][0]{:.label-cif} | + +Persisted pre-fit value snapshot. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/fit_parameter_correlation.md b/docs/docs/user-guide/parameters/analysis/fit_parameter_correlation.md new file mode 100644 index 000000000..02fd98d6d --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/fit_parameter_correlation.md @@ -0,0 +1,55 @@ +--- +title: fit_parameter_correlation +--- + +# :material-vector-link: fit_parameter_correlation + +## :material-tag: correlation { #fit-parameter-correlation-correlation } + +| Access | Source | +| -------------------------------------------- | ---------------------- | +| fit_parameter_correlations['ID'].correlation | [code][0]{:.label-cif} | +| \_fit_parameter_correlation.correlation | [Edi][0]{:.label-cif} | + +Persisted correlation coefficient for the parameter pair. + +## :material-tag: id { #fit-parameter-correlation-id } + +| Access | Source | +| ----------------------------------- | ---------------------- | +| fit_parameter_correlations['ID'].id | [code][0]{:.label-cif} | +| \_fit_parameter_correlation.id | [Edi][0]{:.label-cif} | + +Stable identifier for the persisted correlation row. + +## :material-form-textbox: parameter_unique_name_i { #fit-parameter-correlation-parameter-unique-name-i } + +| Access | Source | +| -------------------------------------------------------- | ---------------------- | +| fit_parameter_correlations['ID'].parameter_unique_name_i | [code][0]{:.label-cif} | +| \_fit_parameter_correlation.parameter_unique_name_i | [Edi][0]{:.label-cif} | + +First unique parameter name in the persisted pair. + +## :material-form-textbox: parameter_unique_name_j { #fit-parameter-correlation-parameter-unique-name-j } + +| Access | Source | +| -------------------------------------------------------- | ---------------------- | +| fit_parameter_correlations['ID'].parameter_unique_name_j | [code][0]{:.label-cif} | +| \_fit_parameter_correlation.parameter_unique_name_j | [Edi][0]{:.label-cif} | + +Second unique parameter name in the persisted pair. + +## :material-tag: source_kind { #fit-parameter-correlation-source-kind } + +| Access | Source | +| -------------------------------------------- | ---------------------- | +| fit_parameter_correlations['ID'].source_kind | [code][0]{:.label-cif} | +| \_fit_parameter_correlation.source_kind | [Edi][0]{:.label-cif} | + +Origin of the persisted correlation summary. Supported values include +`deterministic` and `posterior`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/fit_result.md b/docs/docs/user-guide/parameters/analysis/fit_result.md new file mode 100644 index 000000000..350dbc9c8 --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/fit_result.md @@ -0,0 +1,379 @@ +--- +title: fit_result +--- + +# :material-check-decagram: fit_result + +## :material-tag: R_factor_all { #fit-result-r-factor-all } + +| Access | Source | +| ------------------------- | ---------------------- | +| fit_result.R_factor_all | [code][0]{:.label-cif} | +| \_fit_result.R_factor_all | [Edi][0]{:.label-cif} | + +R factor for all observed data in the deterministic fit. + +## :material-tag: R_factor_gt { #fit-result-r-factor-gt } + +| Access | Source | +| ------------------------ | ---------------------- | +| fit_result.R_factor_gt | [code][0]{:.label-cif} | +| \_fit_result.R_factor_gt | [Edi][0]{:.label-cif} | + +R factor for observations above the threshold. + +## :material-tag: acceptance_rate_mean { #fit-result-acceptance-rate-mean } + +| Access | Source | +| ------------------------------------------------- | ------------------------- | +| fit_result.acceptance_rate_mean | [code][0]{:.label-cif} | +| \_fit_result.acceptance_rate_mean | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.acceptance_rate_mean | [coreCIF][0]{:.label-cif} | + +Mean sampler acceptance rate. + +## :material-tag: background_function { #fit-result-background-function } + +| Access | Source | +| -------------------------------- | ---------------------- | +| fit_result.background_function | [code][0]{:.label-cif} | +| \_fit_result.background_function | [Edi][0]{:.label-cif} | + +Active background function names for the deterministic fit. + +## :material-tag: best_log_posterior { #fit-result-best-log-posterior } + +| Access | Source | +| ----------------------------------------------- | ------------------------- | +| fit_result.best_log_posterior | [code][0]{:.label-cif} | +| \_fit_result.best_log_posterior | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.best_log_posterior | [coreCIF][0]{:.label-cif} | + +Best log-posterior value found. + +## :material-tag: correlation_available { #fit-result-correlation-available } + +| Access | Source | +| ---------------------------------- | ---------------------- | +| fit_result.correlation_available | [code][0]{:.label-cif} | +| \_fit_result.correlation_available | [Edi][0]{:.label-cif} | + +Whether correlations were available for the persisted deterministic fit. + +## :material-tag: covariance_available { #fit-result-covariance-available } + +| Access | Source | +| --------------------------------- | ---------------------- | +| fit_result.covariance_available | [code][0]{:.label-cif} | +| \_fit_result.covariance_available | [Edi][0]{:.label-cif} | + +Whether covariance was available for the persisted deterministic fit. + +## :material-tag: credible_interval_inner { #fit-result-credible-interval-inner } + +| Access | Source | +| ---------------------------------------------------- | ------------------------- | +| fit_result.credible_interval_inner | [code][0]{:.label-cif} | +| \_fit_result.credible_interval_inner | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.credible_interval_inner | [coreCIF][0]{:.label-cif} | + +Inner credible-interval level used in summaries. + +## :material-tag: credible_interval_outer { #fit-result-credible-interval-outer } + +| Access | Source | +| ---------------------------------------------------- | ------------------------- | +| fit_result.credible_interval_outer | [code][0]{:.label-cif} | +| \_fit_result.credible_interval_outer | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.credible_interval_outer | [coreCIF][0]{:.label-cif} | + +Outer credible-interval level used in summaries. + +## :material-tag: degrees_of_freedom { #fit-result-degrees-of-freedom } + +| Access | Source | +| ------------------------------- | ---------------------- | +| fit_result.degrees_of_freedom | [code][0]{:.label-cif} | +| \_fit_result.degrees_of_freedom | [Edi][0]{:.label-cif} | + +Degrees of freedom for the persisted deterministic fit. + +## :material-arrow-collapse-left: effective_sample_size_min { #fit-result-effective-sample-size-min } + +| Access | Source | +| ------------------------------------------------------ | ------------------------- | +| fit_result.effective_sample_size_min | [code][0]{:.label-cif} | +| \_fit_result.effective_sample_size_min | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.effective_sample_size_min | [coreCIF][0]{:.label-cif} | + +Minimum bulk effective sample size. + +## :material-tag: exit_reason { #fit-result-exit-reason } + +| Access | Source | +| ------------------------ | ---------------------- | +| fit_result.exit_reason | [code][0]{:.label-cif} | +| \_fit_result.exit_reason | [Edi][0]{:.label-cif} | + +Backend exit reason for the persisted deterministic fit. + +## :material-tag: fitting_time { #fit-result-fitting-time } + +| Access | Source | +| ------------------------- | ---------------------- | +| fit_result.fitting_time | [code][0]{:.label-cif} | +| \_fit_result.fitting_time | [Edi][0]{:.label-cif} | + +Fitting time in seconds for the latest persisted projection. + +## :material-arrow-collapse-right: gelman_rubin_max { #fit-result-gelman-rubin-max } + +| Access | Source | +| --------------------------------------------- | ------------------------- | +| fit_result.gelman_rubin_max | [code][0]{:.label-cif} | +| \_fit_result.gelman_rubin_max | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.gelman_rubin_max | [coreCIF][0]{:.label-cif} | + +Maximum rank-normalized split R-hat. + +## :material-tag: iterations { #fit-result-iterations } + +| Access | Source | +| ----------------------- | ---------------------- | +| fit_result.iterations | [code][0]{:.label-cif} | +| \_fit_result.iterations | [Edi][0]{:.label-cif} | + +Iteration count for the latest persisted fit-result projection. + +## :material-tag: message { #fit-result-message } + +| Access | Source | +| -------------------- | ---------------------- | +| fit_result.message | [code][0]{:.label-cif} | +| \_fit_result.message | [Edi][0]{:.label-cif} | + +Status message for the latest persisted fit-result projection. + +## :material-tag: n_data_points { #fit-result-n-data-points } + +| Access | Source | +| -------------------------- | ---------------------- | +| fit_result.n_data_points | [code][0]{:.label-cif} | +| \_fit_result.n_data_points | [Edi][0]{:.label-cif} | + +Number of data points used in the persisted deterministic fit. + +## :material-tag: n_free_parameters { #fit-result-n-free-parameters } + +| Access | Source | +| ------------------------------ | ---------------------- | +| fit_result.n_free_parameters | [code][0]{:.label-cif} | +| \_fit_result.n_free_parameters | [Edi][0]{:.label-cif} | + +Number of free parameters in the persisted deterministic fit. + +## :material-tag: n_parameters { #fit-result-n-parameters } + +| Access | Source | +| ------------------------- | ---------------------- | +| fit_result.n_parameters | [code][0]{:.label-cif} | +| \_fit_result.n_parameters | [Edi][0]{:.label-cif} | + +Number of parameters considered in the persisted deterministic fit. + +## :material-tag: number_constraints { #fit-result-number-constraints } + +| Access | Source | +| ------------------------------- | ---------------------- | +| fit_result.number_constraints | [code][0]{:.label-cif} | +| \_fit_result.number_constraints | [Edi][0]{:.label-cif} | + +Number of constraints used in the deterministic fit. + +## :material-tag: number_reflns_gt { #fit-result-number-reflns-gt } + +| Access | Source | +| ----------------------------- | ---------------------- | +| fit_result.number_reflns_gt | [code][0]{:.label-cif} | +| \_fit_result.number_reflns_gt | [Edi][0]{:.label-cif} | + +Number of reflections above the observed threshold. + +## :material-tag: number_reflns_total { #fit-result-number-reflns-total } + +| Access | Source | +| -------------------------------- | ---------------------- | +| fit_result.number_reflns_total | [code][0]{:.label-cif} | +| \_fit_result.number_reflns_total | [Edi][0]{:.label-cif} | + +Total number of reflections represented in the fit. + +## :material-tag: number_restraints { #fit-result-number-restraints } + +| Access | Source | +| ------------------------------ | ---------------------- | +| fit_result.number_restraints | [code][0]{:.label-cif} | +| \_fit_result.number_restraints | [Edi][0]{:.label-cif} | + +Number of restraints used in the deterministic fit. + +## :material-form-textbox: objective_name { #fit-result-objective-name } + +| Access | Source | +| --------------------------- | ---------------------- | +| fit_result.objective_name | [code][0]{:.label-cif} | +| \_fit_result.objective_name | [Edi][0]{:.label-cif} | + +Objective function name for the persisted deterministic fit. + +## :material-numeric: objective_value { #fit-result-objective-value } + +| Access | Source | +| ---------------------------- | ---------------------- | +| fit_result.objective_value | [code][0]{:.label-cif} | +| \_fit_result.objective_value | [Edi][0]{:.label-cif} | + +Objective value for the persisted deterministic fit. + +## :material-form-textbox: point_estimate_name { #fit-result-point-estimate-name } + +| Access | Source | +| ------------------------------------------------ | ------------------------- | +| fit_result.point_estimate_name | [code][0]{:.label-cif} | +| \_fit_result.point_estimate_name | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.point_estimate_name | [coreCIF][0]{:.label-cif} | + +Committed sampled point estimate name. + +## :material-tag: prof_R_factor { #fit-result-prof-r-factor } + +| Access | Source | +| -------------------------- | ---------------------- | +| fit_result.prof_R_factor | [code][0]{:.label-cif} | +| \_fit_result.prof_R_factor | [Edi][0]{:.label-cif} | + +Profile R factor for powder deterministic fits. + +## :material-tag: prof_wR_expected { #fit-result-prof-wr-expected } + +| Access | Source | +| ----------------------------- | ---------------------- | +| fit_result.prof_wR_expected | [code][0]{:.label-cif} | +| \_fit_result.prof_wR_expected | [Edi][0]{:.label-cif} | + +Expected weighted profile R factor for powder fits. + +## :material-tag: prof_wR_factor { #fit-result-prof-wr-factor } + +| Access | Source | +| --------------------------- | ---------------------- | +| fit_result.prof_wR_factor | [code][0]{:.label-cif} | +| \_fit_result.prof_wR_factor | [Edi][0]{:.label-cif} | + +Weighted profile R factor for powder deterministic fits. + +## :material-tag: profile_function { #fit-result-profile-function } + +| Access | Source | +| ----------------------------- | ---------------------- | +| fit_result.profile_function | [code][0]{:.label-cif} | +| \_fit_result.profile_function | [Edi][0]{:.label-cif} | + +Active profile function names for the deterministic fit. + +## :material-tag: reduced_chi_square { #fit-result-reduced-chi-square } + +| Access | Source | +| ------------------------------- | ---------------------- | +| fit_result.reduced_chi_square | [code][0]{:.label-cif} | +| \_fit_result.reduced_chi_square | [Edi][0]{:.label-cif} | + +Reduced chi-square for the latest persisted projection. + +## :material-tag: resolved_random_seed { #fit-result-resolved-random-seed } + +| Access | Source | +| ------------------------------------------------- | ------------------------- | +| fit_result.resolved_random_seed | [code][0]{:.label-cif} | +| \_fit_result.resolved_random_seed | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.resolved_random_seed | [coreCIF][0]{:.label-cif} | + +Runtime random seed used by the sampler. + +## :material-tag: result_kind { #fit-result-result-kind } + +| Access | Source | +| ------------------------ | ---------------------- | +| fit_result.result_kind | [code][0]{:.label-cif} | +| \_fit_result.result_kind | [Edi][0]{:.label-cif} | + +Kind of the latest persisted fit-result projection. Supported values +include `deterministic` and `bayesian`. + +## :material-tag: sampler_completed { #fit-result-sampler-completed } + +| Access | Source | +| ---------------------------------------------- | ------------------------- | +| fit_result.sampler_completed | [code][0]{:.label-cif} | +| \_fit_result.sampler_completed | [Edi][0]{:.label-cif} | +| \_easydiffraction_fit_result.sampler_completed | [coreCIF][0]{:.label-cif} | + +Whether the sampler completed and returned posterior data. + +## :material-arrow-collapse-right: shift_over_su_max { #fit-result-shift-over-su-max } + +| Access | Source | +| ------------------------------ | ---------------------- | +| fit_result.shift_over_su_max | [code][0]{:.label-cif} | +| \_fit_result.shift_over_su_max | [Edi][0]{:.label-cif} | + +Maximum absolute parameter shift divided by s.u. + +## :material-tag: shift_over_su_mean { #fit-result-shift-over-su-mean } + +| Access | Source | +| ------------------------------- | ---------------------- | +| fit_result.shift_over_su_mean | [code][0]{:.label-cif} | +| \_fit_result.shift_over_su_mean | [Edi][0]{:.label-cif} | + +Mean absolute parameter shift divided by s.u. + +## :material-tag: success { #fit-result-success } + +| Access | Source | +| -------------------- | ---------------------- | +| fit_result.success | [code][0]{:.label-cif} | +| \_fit_result.success | [Edi][0]{:.label-cif} | + +Whether the latest persisted fit-result projection succeeded. + +## :material-tag: threshold_expression { #fit-result-threshold-expression } + +| Access | Source | +| --------------------------------- | ---------------------- | +| fit_result.threshold_expression | [code][0]{:.label-cif} | +| \_fit_result.threshold_expression | [Edi][0]{:.label-cif} | + +Expression defining the observed-reflection threshold. + +## :material-tag: wR_factor_all { #fit-result-wr-factor-all } + +| Access | Source | +| -------------------------- | ---------------------- | +| fit_result.wR_factor_all | [code][0]{:.label-cif} | +| \_fit_result.wR_factor_all | [Edi][0]{:.label-cif} | + +Weighted R factor for all observed data in the fit. + +## :material-tag: wR_factor_gt { #fit-result-wr-factor-gt } + +| Access | Source | +| ------------------------- | ---------------------- | +| fit_result.wR_factor_gt | [code][0]{:.label-cif} | +| \_fit_result.wR_factor_gt | [Edi][0]{:.label-cif} | + +Weighted R factor for observations above the threshold. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/fitting_mode.md b/docs/docs/user-guide/parameters/analysis/fitting_mode.md new file mode 100644 index 000000000..e03316d1f --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/fitting_mode.md @@ -0,0 +1,20 @@ +--- +title: fitting_mode +--- + +# :material-playlist-check: fitting_mode + +## :material-shape: type { #fitting-mode-type } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| fitting_mode.type | [code][0]{:.label-cif} | +| \_fitting_mode.type | [Edi][0]{:.label-cif} | +| \_easydiffraction_fitting_mode.type | [coreCIF][0]{:.label-cif} | + +Active fitting mode. Supported values include `single`, `joint`, and +`sequential`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/joint_fit.md b/docs/docs/user-guide/parameters/analysis/joint_fit.md new file mode 100644 index 000000000..0a04daa13 --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/joint_fit.md @@ -0,0 +1,29 @@ +--- +title: joint_fit +--- + +# :material-link-box: joint_fit + +## :material-tag: experiment_id { #joint-fit-experiment-id } + +| Access | Source | +| ----------------------------------------- | ------------------------- | +| joint_fit['ID'].experiment_id | [code][0]{:.label-cif} | +| \_joint_fit.experiment_id | [Edi][0]{:.label-cif} | +| \_easydiffraction_joint_fit.experiment_id | [coreCIF][0]{:.label-cif} | + +Experiment identifier. + +## :material-tag: weight { #joint-fit-weight } + +| Access | Source | +| ---------------------------------- | ------------------------- | +| joint_fit['ID'].weight | [code][0]{:.label-cif} | +| \_joint_fit.weight | [Edi][0]{:.label-cif} | +| \_easydiffraction_joint_fit.weight | [coreCIF][0]{:.label-cif} | + +Weight factor. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/minimizer.md b/docs/docs/user-guide/parameters/analysis/minimizer.md new file mode 100644 index 000000000..1ca912256 --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/minimizer.md @@ -0,0 +1,114 @@ +--- +title: minimizer +--- + +# :material-graph-outline: minimizer + +## :material-tag: burn_in_steps { #minimizer-burn-in-steps } + +| Access | Source | +| ----------------------------------------- | ------------------------- | +| minimizer.burn_in_steps | [code][0]{:.label-cif} | +| \_minimizer.burn_in_steps | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.burn_in_steps | [coreCIF][0]{:.label-cif} | + +Sampler iterations discarded as warm-up. + +## :material-tag: initialization_method { #minimizer-initialization-method } + +| Access | Source | +| ------------------------------------------------- | ------------------------- | +| minimizer.initialization_method | [code][0]{:.label-cif} | +| \_minimizer.initialization_method | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.initialization_method | [coreCIF][0]{:.label-cif} | + +Sampler initialization method. Supported values depend on the minimizer; +available values include `latin_hypercube`, `ball`, `uniform`, and +`prior`. + +## :material-arrow-collapse-right: max_iterations { #minimizer-max-iterations } + +| Access | Source | +| ------------------------------------------ | ------------------------- | +| minimizer.max_iterations | [code][0]{:.label-cif} | +| \_minimizer.max_iterations | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.max_iterations | [coreCIF][0]{:.label-cif} | + +Maximum solver iterations. + +## :material-tag: parallel_workers { #minimizer-parallel-workers } + +| Access | Source | +| -------------------------------------------- | ------------------------- | +| minimizer.parallel_workers | [code][0]{:.label-cif} | +| \_minimizer.parallel_workers | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.parallel_workers | [coreCIF][0]{:.label-cif} | + +Worker count; 0 uses all available CPUs. + +## :material-tag: population_size { #minimizer-population-size } + +| Access | Source | +| ------------------------------------------- | ------------------------- | +| minimizer.population_size | [code][0]{:.label-cif} | +| \_minimizer.population_size | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.population_size | [coreCIF][0]{:.label-cif} | + +Number of chains or walkers. + +## :material-tag: proposal_moves { #minimizer-proposal-moves } + +| Access | Source | +| ------------------------------------------ | ------------------------- | +| minimizer.proposal_moves | [code][0]{:.label-cif} | +| \_minimizer.proposal_moves | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.proposal_moves | [coreCIF][0]{:.label-cif} | + +Single emcee proposal move; move mixtures are not persisted in v1. + +## :material-tag: random_seed { #minimizer-random-seed } + +| Access | Source | +| --------------------------------------- | ------------------------- | +| minimizer.random_seed | [code][0]{:.label-cif} | +| \_minimizer.random_seed | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.random_seed | [coreCIF][0]{:.label-cif} | + +Random seed; None uses a system-derived seed. + +## :material-tag: sampling_steps { #minimizer-sampling-steps } + +| Access | Source | +| ------------------------------------------ | ------------------------- | +| minimizer.sampling_steps | [code][0]{:.label-cif} | +| \_minimizer.sampling_steps | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.sampling_steps | [coreCIF][0]{:.label-cif} | + +Total sampler iterations per chain. + +## :material-tag: thinning_interval { #minimizer-thinning-interval } + +| Access | Source | +| --------------------------------------------- | ------------------------- | +| minimizer.thinning_interval | [code][0]{:.label-cif} | +| \_minimizer.thinning_interval | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.thinning_interval | [coreCIF][0]{:.label-cif} | + +Sampler thinning interval. + +## :material-shape: type { #minimizer-type } + +| Access | Source | +| -------------------------------- | ------------------------- | +| minimizer.type | [code][0]{:.label-cif} | +| \_minimizer.type | [Edi][0]{:.label-cif} | +| \_easydiffraction_minimizer.type | [coreCIF][0]{:.label-cif} | + +Minimizer category type. Supported values include `lmfit`, +`lmfit (leastsq)`, `lmfit (least_squares)`, `dfols`, `bumps`, +`bumps (lm)`, `bumps (dream)`, `bumps (amoeba)`, `bumps (de)`, and +`emcee`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/sequential_fit.md b/docs/docs/user-guide/parameters/analysis/sequential_fit.md new file mode 100644 index 000000000..755853dcf --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/sequential_fit.md @@ -0,0 +1,59 @@ +--- +title: sequential_fit +--- + +# :material-skip-next-circle: sequential_fit + +## :material-tag: chunk_size { #sequential-fit-chunk-size } + +| Access | Source | +| ------------------------------------------- | ------------------------- | +| sequential_fit.chunk_size | [code][0]{:.label-cif} | +| \_sequential_fit.chunk_size | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit.chunk_size | [coreCIF][0]{:.label-cif} | + +Chunk-size token for sequential fitting. + +## :material-tag: data_dir { #sequential-fit-data-dir } + +| Access | Source | +| ----------------------------------------- | ------------------------- | +| sequential_fit.data_dir | [code][0]{:.label-cif} | +| \_sequential_fit.data_dir | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit.data_dir | [coreCIF][0]{:.label-cif} | + +Directory containing sequential-fit data files. + +## :material-tag: file_pattern { #sequential-fit-file-pattern } + +| Access | Source | +| --------------------------------------------- | ------------------------- | +| sequential_fit.file_pattern | [code][0]{:.label-cif} | +| \_sequential_fit.file_pattern | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit.file_pattern | [coreCIF][0]{:.label-cif} | + +Glob pattern selecting sequential-fit files. + +## :material-arrow-collapse-right: max_workers { #sequential-fit-max-workers } + +| Access | Source | +| -------------------------------------------- | ------------------------- | +| sequential_fit.max_workers | [code][0]{:.label-cif} | +| \_sequential_fit.max_workers | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit.max_workers | [coreCIF][0]{:.label-cif} | + +Worker-count token for sequential fitting. + +## :material-tag: reverse { #sequential-fit-reverse } + +| Access | Source | +| ---------------------------------------- | ------------------------- | +| sequential_fit.reverse | [code][0]{:.label-cif} | +| \_sequential_fit.reverse | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit.reverse | [coreCIF][0]{:.label-cif} | + +Whether to process sequential-fit files in reverse. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/sequential_fit_extract.md b/docs/docs/user-guide/parameters/analysis/sequential_fit_extract.md new file mode 100644 index 000000000..70194d05e --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/sequential_fit_extract.md @@ -0,0 +1,49 @@ +--- +title: sequential_fit_extract +--- + +# :material-text-search: sequential_fit_extract + +## :material-tag: id { #sequential-fit-extract-id } + +| Access | Source | +| ------------------------------------------- | ------------------------- | +| sequential_fit_extract['ID'].id | [code][0]{:.label-cif} | +| \_sequential_fit_extract.id | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit_extract.id | [coreCIF][0]{:.label-cif} | + +Identifier for this extract rule. + +## :material-tag: pattern { #sequential-fit-extract-pattern } + +| Access | Source | +| ------------------------------------------------ | ------------------------- | +| sequential_fit_extract['ID'].pattern | [code][0]{:.label-cif} | +| \_sequential_fit_extract.pattern | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit_extract.pattern | [coreCIF][0]{:.label-cif} | + +Regex used to extract one numeric capture group. + +## :material-tag: required { #sequential-fit-extract-required } + +| Access | Source | +| ------------------------------------------------- | ------------------------- | +| sequential_fit_extract['ID'].required | [code][0]{:.label-cif} | +| \_sequential_fit_extract.required | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit_extract.required | [coreCIF][0]{:.label-cif} | + +Whether this extract rule must match every file. + +## :material-tag: target { #sequential-fit-extract-target } + +| Access | Source | +| ----------------------------------------------- | ------------------------- | +| sequential_fit_extract['ID'].target | [code][0]{:.label-cif} | +| \_sequential_fit_extract.target | [Edi][0]{:.label-cif} | +| \_easydiffraction_sequential_fit_extract.target | [coreCIF][0]{:.label-cif} | + +diffrn attribute updated by this extract rule. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/analysis/software.md b/docs/docs/user-guide/parameters/analysis/software.md new file mode 100644 index 000000000..21d70c6c0 --- /dev/null +++ b/docs/docs/user-guide/parameters/analysis/software.md @@ -0,0 +1,46 @@ +--- +title: software +--- + +# :material-application-cog: software + +## :material-tag: id { #software-id } + +| Access | Source | +| ----------------- | ---------------------- | +| software['ID'].id | [code][0]{:.label-cif} | +| \_software.id | [Edi][0]{:.label-cif} | + +Software role. Supported values include `framework`, `calculator`, and +`minimizer`. + +## :material-form-textbox: name { #software-name } + +| Access | Source | +| ------------------- | ---------------------- | +| software['ID'].name | [code][0]{:.label-cif} | +| \_software.name | [Edi][0]{:.label-cif} | + +Software package name. + +## :material-link: url { #software-url } + +| Access | Source | +| ------------------ | ---------------------- | +| software['ID'].url | [code][0]{:.label-cif} | +| \_software.url | [Edi][0]{:.label-cif} | + +Software project URL. + +## :material-numeric: version { #software-version } + +| Access | Source | +| ---------------------- | ---------------------- | +| software['ID'].version | [code][0]{:.label-cif} | +| \_software.version | [Edi][0]{:.label-cif} | + +Software package version. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/atom_site.md b/docs/docs/user-guide/parameters/atom_site.md deleted file mode 100644 index 7acdb6505..000000000 --- a/docs/docs/user-guide/parameters/atom_site.md +++ /dev/null @@ -1,63 +0,0 @@ -[coreCIF][1]{:.label-cif} - -# \_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 -[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. - -## [\_atom_site.type_symbol](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.type_symbol.html) - -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.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) -- [\_atom_site.fract_z](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.fract_z.html) - -## [\_atom_site.occupancy](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.occupancy.html) - -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. - -## [\_atom_site.B_iso_or_equiv](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.B_iso_or_equiv.html) - -Isotropic atomic displacement parameter, or equivalent isotropic atomic -displacement parameter, in angstroms squared. - -## [\_atom_site.site_symmetry_multiplicity](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.site_symmetry_multiplicity.html) - -`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). - -## [\_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. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/background.md b/docs/docs/user-guide/parameters/background.md deleted file mode 100644 index 45e890b0e..000000000 --- a/docs/docs/user-guide/parameters/background.md +++ /dev/null @@ -1,38 +0,0 @@ -[pdCIF][2]{:.label-cif} - -# \_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. - -!!! tip "Automatic background estimation" - - Line-segment background points can be detected automatically from the - measured pattern with - [`background.auto_estimate()`](../analysis-workflow/experiment.md#background-category), - instead of entering them by hand. - -## [\_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. - -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. - -## [\_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. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/cell.md b/docs/docs/user-guide/parameters/cell.md deleted file mode 100644 index 806df516f..000000000 --- a/docs/docs/user-guide/parameters/cell.md +++ /dev/null @@ -1,30 +0,0 @@ -[coreCIF][1]{:.label-cif} - -# \_cell - -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. - -## \_cell.angle - -The angles between the bounding cell axes in degrees. - -- [\_cell.angle_alpha](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.angle_alpha.html) -- [\_cell.angle_beta](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.angle_beta.html) -- [\_cell.angle_gamma](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.angle_gamma.html) - -## \_cell.length - -The lengths of each cell axis in angstroms. - -- [\_cell.length_a](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.length_a.html) -- [\_cell.length_b](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.length_b.html) -- [\_cell.length_c](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.length_c.html) - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/absorption.md b/docs/docs/user-guide/parameters/experiment/absorption.md new file mode 100644 index 000000000..d00b9954c --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/absorption.md @@ -0,0 +1,41 @@ +--- +title: absorption +--- + +# :material-blur: absorption + +[pd-neut-cwl][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} + +## :material-tag: mu_r { #absorption-mu-r } + +| Access | Source | +| --------------------------------- | ------------------------- | +| absorption.mu_r | [code][0]{:.label-cif} | +| \_absorption.mu_r | [Edi][0]{:.label-cif} | +| \_easydiffraction_absorption.mu_r | [coreCIF][0]{:.label-cif} | + +Linear absorption coefficient times the sample radius, `μR` +(dimensionless). Only present for `type = 'cylinder-hewat'`. It is +normally entered as a known, fixed value, since it is strongly +correlated with the displacement parameters and the scale. The Hewat +expansion is validated to four decimals for `μR ≲ 1.5`; larger values +are still computed but emit a warning. + +## :material-shape: type { #absorption-type } + +| Access | Source | +| --------------------------------- | ------------------------- | +| absorption.type | [code][0]{:.label-cif} | +| \_absorption.type | [Edi][0]{:.label-cif} | +| \_easydiffraction_absorption.type | [coreCIF][0]{:.label-cif} | + +Active absorption correction type. One of `none` (no correction) or +`cylinder-hewat` (cylindrical Debye–Scherrer, Hewat model). Use +`experiment.absorption.show_supported()` to list the types available for +the current experiment. This category is available for +constant-wavelength Bragg powder experiments. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/background.md b/docs/docs/user-guide/parameters/experiment/background.md new file mode 100644 index 000000000..e3e7af5eb --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/background.md @@ -0,0 +1,73 @@ +--- +title: background +--- + +# :material-waveform: background + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} + +## :material-tag: id { #background-id } + +| Access | Source | +| ------------------- | ---------------------- | +| background['ID'].id | [code][0]{:.label-cif} | +| \_background.id | [Edi][0]{:.label-cif} | + +Identifier for this background line segment. + +## :material-arrow-collapse-right: position { #background-position } + +| Access | Source | +| ------------------------------ | ----------------------- | +| background.position | [code][0]{:.label-cif} | +| \_background.position | [Edi][0]{:.label-cif} | +| \_pd_background.line_segment_X | [pdCIF][0]{:.label-cif} | + +Position used to create many straight-line segments. + +## :material-arrow-collapse-up: intensity { #background-intensity } + +| Access | Source | +| -------------------------------------- | ----------------------- | +| background.intensity | [code][0]{:.label-cif} | +| \_background.intensity | [Edi][0]{:.label-cif} | +| \_pd_background.line_segment_intensity | [pdCIF][0]{:.label-cif} | + +Intensity used to create many straight-line segments. + +## :material-format-superscript: order { #background-order } + +| Access | Source | +| ------------------------------- | ----------------------- | +| background.order | [code][0]{:.label-cif} | +| \_background.order | [Edi][0]{:.label-cif} | +| \_pd_background.Chebyshev_order | [pdCIF][0]{:.label-cif} | + +Order used in a Chebyshev polynomial background term. + +## :material-arrow-collapse-up: coef { #background-coef } + +| Access | Source | +| ------------------------------ | ----------------------- | +| background.coef | [code][0]{:.label-cif} | +| \_background.coef | [Edi][0]{:.label-cif} | +| \_pd_background.Chebyshev_coef | [pdCIF][0]{:.label-cif} | + +Coefficient used in a Chebyshev polynomial background term. + +## :material-shape: type { #background-type } + +| Access | Source | +| --------------------------------- | ------------------------- | +| background.type | [code][0]{:.label-cif} | +| \_background.type | [Edi][0]{:.label-cif} | +| \_easydiffraction_background.type | [coreCIF][0]{:.label-cif} | + +Active background type tag. Supported values include `line-segment` and +`chebyshev`. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/calculator.md b/docs/docs/user-guide/parameters/experiment/calculator.md new file mode 100644 index 000000000..b54fbb55f --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/calculator.md @@ -0,0 +1,27 @@ +--- +title: calculator +--- + +# :material-calculator: calculator + +[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-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +## :material-shape: type { #calculator-type } + +| Access | Source | +| --------------------------------- | ------------------------- | +| calculator.type | [code][0]{:.label-cif} | +| \_calculator.type | [Edi][0]{:.label-cif} | +| \_easydiffraction_calculator.type | [coreCIF][0]{:.label-cif} | + +Calculator backend type. Supported values include `cryspy`, `crysfml`, +and `pdffit`. Available choices depend on the experiment type. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/data.md b/docs/docs/user-guide/parameters/experiment/data.md new file mode 100644 index 000000000..8717555c7 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/data.md @@ -0,0 +1,146 @@ +--- +title: data +--- + +# :material-chart-line: data + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[pd-neut-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +## :material-tag: id { #data-id } + +| Access | Source | +| ------------------ | ----------------------- | +| data['ID'].id | [code][0]{:.label-cif} | +| \_data.id | [Edi][0]{:.label-cif} | +| \_pd_data.point_id | [pdCIF][0]{:.label-cif} | + +Identifier for this data point in the dataset. + +## :material-angle-acute: two_theta { #data-two-theta } + +| Access | Source | +| --------------------- | ----------------------- | +| data['ID'].two_theta | [code][0]{:.label-cif} | +| \_data.two_theta | [Edi][0]{:.label-cif} | +| \_pd_proc.2theta_scan | [pdCIF][0]{:.label-cif} | + +Measured 2θ diffraction angle. + +## :material-timer-outline: time_of_flight { #data-time-of-flight } + +| Access | Source | +| ------------------------- | ----------------------- | +| data['ID'].time_of_flight | [code][0]{:.label-cif} | +| \_data.time_of_flight | [Edi][0]{:.label-cif} | +| \_pd_meas.time_of_flight | [pdCIF][0]{:.label-cif} | + +Measured time for time-of-flight neutron measurement. + +## :material-tag: d_spacing { #data-d-spacing } + +| Access | Source | +| -------------------- | ----------------------- | +| data['ID'].d_spacing | [code][0]{:.label-cif} | +| \_data.d_spacing | [Edi][0]{:.label-cif} | +| \_pd_proc.d_spacing | [pdCIF][0]{:.label-cif} | + +d-spacing value corresponding to this data point. + +## :material-arrow-collapse-up: intensity_meas { #data-intensity-meas } + +| Access | Source | +| ------------------------- | ----------------------- | +| data['ID'].intensity_meas | [code][0]{:.label-cif} | +| \_data.intensity_meas | [Edi][0]{:.label-cif} | +| \_pd_meas.intensity_total | [pdCIF][0]{:.label-cif} | + +Intensity recorded at each measurement point (angle/time). + +## :material-arrow-collapse-up: intensity_meas_su { #data-intensity-meas-su } + +| Access | Source | +| ---------------------------- | ----------------------- | +| data['ID'].intensity_meas_su | [code][0]{:.label-cif} | +| \_data.intensity_meas_su | [Edi][0]{:.label-cif} | +| \_pd_meas.intensity_total_su | [pdCIF][0]{:.label-cif} | + +Standard uncertainty of the measured intensity at this point. + +## :material-arrow-collapse-up: intensity_calc { #data-intensity-calc } + +| Access | Source | +| ------------------------- | ----------------------- | +| data['ID'].intensity_calc | [code][0]{:.label-cif} | +| \_data.intensity_calc | [Edi][0]{:.label-cif} | +| \_pd_calc.intensity_total | [pdCIF][0]{:.label-cif} | + +Intensity of a computed diffractogram at this point. + +## :material-arrow-collapse-up: intensity_bkg { #data-intensity-bkg } + +| Access | Source | +| ------------------------ | ----------------------- | +| data['ID'].intensity_bkg | [code][0]{:.label-cif} | +| \_data.intensity_bkg | [Edi][0]{:.label-cif} | +| \_pd_calc.intensity_bkg | [pdCIF][0]{:.label-cif} | + +Intensity of a computed background at this point. + +## :material-tag: calc_status { #data-calc-status } + +| Access | Source | +| --------------------------- | ----------------------- | +| data['ID'].calc_status | [code][0]{:.label-cif} | +| \_data.calc_status | [Edi][0]{:.label-cif} | +| \_pd_data.refinement_status | [pdCIF][0]{:.label-cif} | + +Status code of the data point in calculation. Supported values include +`incl` and `excl`. + +## :material-tag: r { #data-r } + +| Access | Source | +| ------------ | ----------------------- | +| data['ID'].r | [code][0]{:.label-cif} | +| \_data.r | [Edi][0]{:.label-cif} | +| \_pd_proc.r | [pdCIF][0]{:.label-cif} | + +Interatomic distance in real space. + +## :material-tag: g_r_meas { #data-g-r-meas } + +| Access | Source | +| ------------------------- | ----------------------- | +| data['ID'].g_r_meas | [code][0]{:.label-cif} | +| \_data.g_r_meas | [Edi][0]{:.label-cif} | +| \_pd_meas.intensity_total | [pdCIF][0]{:.label-cif} | + +Measured pair distribution function G(r). + +## :material-tag: g_r_meas_su { #data-g-r-meas-su } + +| Access | Source | +| ---------------------------- | ----------------------- | +| data['ID'].g_r_meas_su | [code][0]{:.label-cif} | +| \_data.g_r_meas_su | [Edi][0]{:.label-cif} | +| \_pd_meas.intensity_total_su | [pdCIF][0]{:.label-cif} | + +Standard uncertainty of measured G(r). + +## :material-tag: g_r_calc { #data-g-r-calc } + +| Access | Source | +| ------------------------- | ----------------------- | +| data['ID'].g_r_calc | [code][0]{:.label-cif} | +| \_data.g_r_calc | [Edi][0]{:.label-cif} | +| \_pd_calc.intensity_total | [pdCIF][0]{:.label-cif} | + +Calculated pair distribution function G(r). + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/diffrn.md b/docs/docs/user-guide/parameters/experiment/diffrn.md new file mode 100644 index 000000000..b3f8a7cdd --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/diffrn.md @@ -0,0 +1,54 @@ +--- +title: diffrn +--- + +# :material-telescope: diffrn + +[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-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +## :material-flash: ambient_electric_field { #diffrn-ambient-electric-field } + +| Access | Source | +| ----------------------------------------------- | ------------------------- | +| diffrn.ambient_electric_field | [code][0]{:.label-cif} | +| \_diffrn.ambient_electric_field | [Edi][0]{:.label-cif} | +| \_easydiffraction_diffrn.ambient_electric_field | [coreCIF][0]{:.label-cif} | + +Mean electric field during measurement. + +## :material-magnet: ambient_magnetic_field { #diffrn-ambient-magnetic-field } + +| Access | Source | +| ----------------------------------------------- | ------------------------- | +| diffrn.ambient_magnetic_field | [code][0]{:.label-cif} | +| \_diffrn.ambient_magnetic_field | [Edi][0]{:.label-cif} | +| \_easydiffraction_diffrn.ambient_magnetic_field | [coreCIF][0]{:.label-cif} | + +Mean magnetic field during measurement. + +## :material-gauge: ambient_pressure { #diffrn-ambient-pressure } + +| Access | Source | +| ------------------------- | ---------------------- | +| diffrn.ambient_pressure | [code][0]{:.label-cif} | +| \_diffrn.ambient_pressure | [Edi][0]{:.label-cif} | + +Mean hydrostatic pressure during measurement. + +## :material-thermometer: ambient_temperature { #diffrn-ambient-temperature } + +| Access | Source | +| ---------------------------- | ---------------------- | +| diffrn.ambient_temperature | [code][0]{:.label-cif} | +| \_diffrn.ambient_temperature | [Edi][0]{:.label-cif} | + +Mean temperature during measurement. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/excluded_region.md b/docs/docs/user-guide/parameters/experiment/excluded_region.md new file mode 100644 index 000000000..3be27d28a --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/excluded_region.md @@ -0,0 +1,45 @@ +--- +title: excluded_region +--- + +# :material-selection-remove: excluded_region + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[pd-neut-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +## :material-stop: end { #excluded-region-end } + +| Access | Source | +| ------------------------------------- | ------------------------- | +| excluded_regions['ID'].end | [code][0]{:.label-cif} | +| \_excluded_region.end | [Edi][0]{:.label-cif} | +| \_easydiffraction_excluded_region.end | [coreCIF][0]{:.label-cif} | + +End of the excluded region. + +## :material-tag: id { #excluded-region-id } + +| Access | Source | +| ------------------------------------ | ------------------------- | +| excluded_regions['ID'].id | [code][0]{:.label-cif} | +| \_excluded_region.id | [Edi][0]{:.label-cif} | +| \_easydiffraction_excluded_region.id | [coreCIF][0]{:.label-cif} | + +Identifier for this excluded region. + +## :material-play: start { #excluded-region-start } + +| Access | Source | +| --------------------------------------- | ------------------------- | +| excluded_regions['ID'].start | [code][0]{:.label-cif} | +| \_excluded_region.start | [Edi][0]{:.label-cif} | +| \_easydiffraction_excluded_region.start | [coreCIF][0]{:.label-cif} | + +Start of the excluded region. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/experiment_type.md b/docs/docs/user-guide/parameters/experiment/experiment_type.md new file mode 100644 index 000000000..009e00698 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/experiment_type.md @@ -0,0 +1,60 @@ +--- +title: experiment_type +--- + +# :material-flask: experiment_type + +[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-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +## :material-diamond-stone: sample_form { #experiment-type-sample-form } + +| Access | Source | +| --------------------------------------------- | ------------------------- | +| experiment_type.sample_form | [code][0]{:.label-cif} | +| \_experiment_type.sample_form | [Edi][0]{:.label-cif} | +| \_easydiffraction_experiment_type.sample_form | [coreCIF][0]{:.label-cif} | + +Powder diffraction or single crystal diffraction. Supported values +include `powder` and `single crystal`. + +## :material-sawtooth-wave: beam_mode { #experiment-type-beam-mode } + +| Access | Source | +| ------------------------------------------- | ------------------------- | +| experiment_type.beam_mode | [code][0]{:.label-cif} | +| \_experiment_type.beam_mode | [Edi][0]{:.label-cif} | +| \_easydiffraction_experiment_type.beam_mode | [coreCIF][0]{:.label-cif} | + +Constant wavelength (CW) or time-of-flight (TOF) measurement. Supported +values include `constant wavelength` and `time-of-flight`. + +## :material-radiology-box-outline: radiation_probe { #experiment-type-radiation-probe } + +| Access | Source | +| ------------------------------------------------- | ------------------------- | +| experiment_type.radiation_probe | [code][0]{:.label-cif} | +| \_experiment_type.radiation_probe | [Edi][0]{:.label-cif} | +| \_easydiffraction_experiment_type.radiation_probe | [coreCIF][0]{:.label-cif} | + +Neutron or X-ray diffraction measurement. Supported values include +`neutron` and `xray`. + +## :material-chart-bell-curve: scattering_type { #experiment-type-scattering-type } + +| Access | Source | +| ------------------------------------------------- | ------------------------- | +| experiment_type.scattering_type | [code][0]{:.label-cif} | +| \_experiment_type.scattering_type | [Edi][0]{:.label-cif} | +| \_easydiffraction_experiment_type.scattering_type | [coreCIF][0]{:.label-cif} | + +Conventional Bragg diffraction or total scattering (PDF). Supported +values include `bragg` and `total`. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/extinction.md b/docs/docs/user-guide/parameters/experiment/extinction.md new file mode 100644 index 000000000..13ac77555 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/extinction.md @@ -0,0 +1,54 @@ +--- +title: extinction +--- + +# :material-lightbulb-off-outline: extinction + +[sc-neut-cwl][3]{:.label-experiment} + +## :material-tag: model { #extinction-model } + +| Access | Source | +| ---------------------------------- | ------------------------- | +| extinction.model | [code][0]{:.label-cif} | +| \_extinction.model | [Edi][0]{:.label-cif} | +| \_easydiffraction_extinction.model | [coreCIF][0]{:.label-cif} | + +Mosaicity distribution model (gauss or lorentz). Supported values +include `gauss` and `lorentz`. + +## :material-tag: mosaicity { #extinction-mosaicity } + +| Access | Source | +| -------------------------------------- | ------------------------- | +| extinction.mosaicity | [code][0]{:.label-cif} | +| \_extinction.mosaicity | [Edi][0]{:.label-cif} | +| \_easydiffraction_extinction.mosaicity | [coreCIF][0]{:.label-cif} | + +Mosaicity of the crystal. + +## :material-tag: radius { #extinction-radius } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| extinction.radius | [code][0]{:.label-cif} | +| \_extinction.radius | [Edi][0]{:.label-cif} | +| \_easydiffraction_extinction.radius | [coreCIF][0]{:.label-cif} | + +Mean radius of the crystal. + +## :material-shape: type { #extinction-type } + +| Access | Source | +| --------------------------------- | ------------------------- | +| extinction.type | [code][0]{:.label-cif} | +| \_extinction.type | [Edi][0]{:.label-cif} | +| \_easydiffraction_extinction.type | [coreCIF][0]{:.label-cif} | + +Active extinction type tag. Supported values include `becker-coppens`. +This type is available for single-crystal experiments. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/instrument.md b/docs/docs/user-guide/parameters/experiment/instrument.md new file mode 100644 index 000000000..71a29dc44 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/instrument.md @@ -0,0 +1,106 @@ +--- +title: instrument +--- + +# :material-microscope: instrument + +[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-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +### :material-wrench: setup_wavelength { #instrument-setup-wavelength } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| instrument.setup_wavelength | [code][0]{:.label-cif} | +| \_instrument.setup_wavelength | [Edi][0]{:.label-cif} | +| \_diffrn_radiation_wavelength.value [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Idiffrn_radiation_wavelength.value.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Incident neutron or X-ray wavelength. + +### :material-tune: calib_twotheta_offset { #instrument-calib-twotheta-offset } + +| Access | Source | +| ---------------------------------- | ----------------------- | +| instrument.calib_twotheta_offset | [code][0]{:.label-cif} | +| \_instrument.calib_twotheta_offset | [Edi][0]{:.label-cif} | +| \_pd_calib.2theta_offset | [pdCIF][0]{:.label-cif} | + +Instrument misalignment offset. + +### :material-wrench: setup_twotheta_bank { #instrument-setup-twotheta-bank } + +| Access | Source | +| -------------------------------- | ------------------------- | +| instrument.setup_twotheta_bank | [code][0]{:.label-cif} | +| \_instrument.setup_twotheta_bank | [Edi][0]{:.label-cif} | +| \_instr.2theta_bank | [coreCIF][0]{:.label-cif} | + +Detector bank position. + +### :material-tune: calib_d_to_tof_reciprocal { #instrument-calib-d-to-tof-reciprocal } + +| Access | Source | +| -------------------------------------- | ------------------------- | +| instrument.calib_d_to_tof_reciprocal | [code][0]{:.label-cif} | +| \_instrument.calib_d_to_tof_reciprocal | [Edi][0]{:.label-cif} | +| \_instr.d_to_tof_recip | [coreCIF][0]{:.label-cif} | + +TOF reciprocal velocity correction. + +### :material-tune: calib_d_to_tof_offset { #instrument-calib-d-to-tof-offset } + +| Access | Source | +| ---------------------------------- | ------------------------- | +| instrument.calib_d_to_tof_offset | [code][0]{:.label-cif} | +| \_instrument.calib_d_to_tof_offset | [Edi][0]{:.label-cif} | +| \_instr.d_to_tof_offset | [coreCIF][0]{:.label-cif} | + +TOF offset. + +### :material-tune: calib_d_to_tof_linear { #instrument-calib-d-to-tof-linear } + +| Access | Source | +| ---------------------------------- | ------------------------- | +| instrument.calib_d_to_tof_linear | [code][0]{:.label-cif} | +| \_instrument.calib_d_to_tof_linear | [Edi][0]{:.label-cif} | +| \_instr.d_to_tof_linear | [coreCIF][0]{:.label-cif} | + +TOF linear conversion. + +### :material-tune: calib_d_to_tof_quadratic { #instrument-calib-d-to-tof-quadratic } + +| Access | Source | +| ------------------------------------- | ------------------------- | +| instrument.calib_d_to_tof_quadratic | [code][0]{:.label-cif} | +| \_instrument.calib_d_to_tof_quadratic | [Edi][0]{:.label-cif} | +| \_instr.d_to_tof_quad | [coreCIF][0]{:.label-cif} | + +TOF quadratic correction. + +### :material-tune: calib_sample_displacement { #instrument-calib-sample-displacement } + +| Access | Source | +| -------------------------------------- | ------------------------- | +| instrument.calib_sample_displacement | [code][0]{:.label-cif} | +| \_instrument.calib_sample_displacement | [Edi][0]{:.label-cif} | +| \_instr.sample_displacement | [coreCIF][0]{:.label-cif} | + +Specimen displacement from the diffractometer axis. + +### :material-tune: calib_sample_transparency { #instrument-calib-sample-transparency } + +| Access | Source | +| -------------------------------------- | ------------------------- | +| instrument.calib_sample_transparency | [code][0]{:.label-cif} | +| \_instrument.calib_sample_transparency | [Edi][0]{:.label-cif} | +| \_instr.sample_transparency | [coreCIF][0]{:.label-cif} | + +Sample transparency (beam penetration) shift. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/linked_structure.md b/docs/docs/user-guide/parameters/experiment/linked_structure.md new file mode 100644 index 000000000..6c76c3448 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/linked_structure.md @@ -0,0 +1,38 @@ +--- +title: linked_structure +--- + +# :material-puzzle: linked_structure + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-identifier: structure_id { #linked-structure-structure-id } + +| Access | Source | +| ------------------------------------ | ----------------------- | +| linked_structures['ID'].structure_id | [code][0]{:.label-cif} | +| linked_structure.structure_id | [code][0]{:.label-cif} | +| \_linked_structure.structure_id | [Edi][0]{:.label-cif} | +| \_pd_phase_block.id | [pdCIF][0]{:.label-cif} | + +Identifier of the linked structure. The plural `linked_structures['ID']` +form is used for powder experiments (one or more linked phases); the +scalar `linked_structure` form is used for single-crystal experiments. + +## :material-scale: scale { #linked-structure-scale } + +| Access | Source | +| ----------------------------- | ----------------------- | +| linked_structures['ID'].scale | [code][0]{:.label-cif} | +| linked_structure.scale | [code][0]{:.label-cif} | +| \_linked_structure.scale | [Edi][0]{:.label-cif} | +| \_pd_phase_block.scale | [pdCIF][0]{:.label-cif} | + +Scale factor of the linked structure. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/pd_background.md b/docs/docs/user-guide/parameters/experiment/pd_background.md new file mode 100644 index 000000000..ad7f6cc4f --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/pd_background.md @@ -0,0 +1,37 @@ +--- +title: pd_background +--- + +# :material-waveform: pd_background + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} + +## :material-tag: Chebyshev_order { #pd-background-chebyshev-order } + +| Access | Source | +| ------------------------------- | ----------------------- | +| \_pd_background.Chebyshev_order | [pdCIF][0]{:.label-cif} | + +Order used in a Chebyshev polynomial background term. + +## :material-tag: Chebyshev_coef { #pd-background-chebyshev-coef } + +| Access | Source | +| ------------------------------ | ----------------------- | +| \_pd_background.Chebyshev_coef | [pdCIF][0]{:.label-cif} | + +Coefficient used in a Chebyshev polynomial background term. + +## :material-tag: id { #pd-background-id } + +| Access | Source | +| ------------------ | ----------------------- | +| \_pd_background.id | [pdCIF][0]{:.label-cif} | + +Identifier for this background line segment. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/pd_meas.md b/docs/docs/user-guide/parameters/experiment/pd_meas.md new file mode 100644 index 000000000..b74b6a91a --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/pd_meas.md @@ -0,0 +1,69 @@ +--- +title: pd_meas +--- + +# :material-chart-scatter-plot: pd_meas + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[pd-neut-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +## :material-arrow-left-right: 2theta_range_inc { #pd-meas-2theta-range-inc } + +| Access | Source | +| -------------------------- | ---------------------- | +| data_range.two_theta_inc | [code][0]{:.label-cif} | +| \_pd_meas.2theta_range_inc | [Edi][0]{:.label-cif} | + +2θ step between calculation points. + +## :material-arrow-left-right: 2theta_range_max { #pd-meas-2theta-range-max } + +| Access | Source | +| -------------------------- | ---------------------- | +| data_range.two_theta_max | [code][0]{:.label-cif} | +| \_pd_meas.2theta_range_max | [Edi][0]{:.label-cif} | + +Upper 2θ bound of the calculation range. + +## :material-arrow-left-right: 2theta_range_min { #pd-meas-2theta-range-min } + +| Access | Source | +| -------------------------- | ---------------------- | +| data_range.two_theta_min | [code][0]{:.label-cif} | +| \_pd_meas.2theta_range_min | [Edi][0]{:.label-cif} | + +Lower 2θ bound of the calculation range. + +## :material-timer-outline: time_of_flight_range_inc { #pd-meas-time-of-flight-range-inc } + +| Access | Source | +| ---------------------------------- | ---------------------- | +| data_range.time_of_flight_inc | [code][0]{:.label-cif} | +| \_pd_meas.time_of_flight_range_inc | [Edi][0]{:.label-cif} | + +Time-of-flight step between calculation points. + +## :material-timer-outline: time_of_flight_range_max { #pd-meas-time-of-flight-range-max } + +| Access | Source | +| ---------------------------------- | ---------------------- | +| data_range.time_of_flight_max | [code][0]{:.label-cif} | +| \_pd_meas.time_of_flight_range_max | [Edi][0]{:.label-cif} | + +Upper time-of-flight bound of the calculation range. + +## :material-timer-outline: time_of_flight_range_min { #pd-meas-time-of-flight-range-min } + +| Access | Source | +| ---------------------------------- | ---------------------- | +| data_range.time_of_flight_min | [code][0]{:.label-cif} | +| \_pd_meas.time_of_flight_range_min | [Edi][0]{:.label-cif} | + +Lower time-of-flight bound of the calculation range. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/peak.md b/docs/docs/user-guide/parameters/experiment/peak.md new file mode 100644 index 000000000..527343aa8 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/peak.md @@ -0,0 +1,392 @@ +--- +title: peak +--- + +# :material-shape: peak + +## Constant-Wavelength Powder Profiles + +[pd-neut-cwl][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} + +### :material-arrow-expand-horizontal: broad_gauss_u { #peak-broad-gauss-u } + +| Access | Source | +| ------------------------------------ | ------------------------- | +| peak.broad_gauss_u | [code][0]{:.label-cif} | +| \_peak.broad_gauss_u | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_gauss_u | [coreCIF][0]{:.label-cif} | + +Gaussian broadening from sample size and resolution. + +### :material-arrow-expand-horizontal: broad_gauss_v { #peak-broad-gauss-v } + +| Access | Source | +| ------------------------------------ | ------------------------- | +| peak.broad_gauss_v | [code][0]{:.label-cif} | +| \_peak.broad_gauss_v | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_gauss_v | [coreCIF][0]{:.label-cif} | + +Gaussian broadening instrumental contribution. + +### :material-arrow-expand-horizontal: broad_gauss_w { #peak-broad-gauss-w } + +| Access | Source | +| ------------------------------------ | ------------------------- | +| peak.broad_gauss_w | [code][0]{:.label-cif} | +| \_peak.broad_gauss_w | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_gauss_w | [coreCIF][0]{:.label-cif} | + +Gaussian broadening instrumental contribution. + +### :material-arrow-expand-horizontal: broad_lorentz_x { #peak-broad-lorentz-x } + +| Access | Source | +| -------------------------------------- | ------------------------- | +| peak.broad_lorentz_x | [code][0]{:.label-cif} | +| \_peak.broad_lorentz_x | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_lorentz_x | [coreCIF][0]{:.label-cif} | + +Lorentzian broadening from sample strain effects. + +### :material-arrow-expand-horizontal: broad_lorentz_y { #peak-broad-lorentz-y } + +| Access | Source | +| -------------------------------------- | ------------------------- | +| peak.broad_lorentz_y | [code][0]{:.label-cif} | +| \_peak.broad_lorentz_y | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_lorentz_y | [coreCIF][0]{:.label-cif} | + +Lorentzian broadening from microstructural defects. + +## Time-Of-Flight Powder Profiles + +[pd-neut-tof][3]{:.label-experiment} + +### :material-arrow-expand-horizontal: broad_gauss_sigma_0 { #peak-broad-gauss-sigma-0 } + +| Access | Source | +| ------------------------------------------ | ------------------------- | +| peak.broad_gauss_sigma_0 | [code][0]{:.label-cif} | +| \_peak.broad_gauss_sigma_0 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_gauss_sigma_0 | [coreCIF][0]{:.label-cif} | + +Gaussian broadening (instrumental resolution). + +### :material-arrow-expand-horizontal: broad_gauss_sigma_1 { #peak-broad-gauss-sigma-1 } + +| Access | Source | +| ------------------------------------------ | ------------------------- | +| peak.broad_gauss_sigma_1 | [code][0]{:.label-cif} | +| \_peak.broad_gauss_sigma_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_gauss_sigma_1 | [coreCIF][0]{:.label-cif} | + +Gaussian broadening (dependent on d-spacing). + +### :material-arrow-expand-horizontal: broad_gauss_sigma_2 { #peak-broad-gauss-sigma-2 } + +| Access | Source | +| ------------------------------------------ | ------------------------- | +| peak.broad_gauss_sigma_2 | [code][0]{:.label-cif} | +| \_peak.broad_gauss_sigma_2 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_gauss_sigma_2 | [coreCIF][0]{:.label-cif} | + +Gaussian broadening (instrument-dependent term). + +### :material-arrow-expand-horizontal: broad_lorentz_gamma_0 { #peak-broad-lorentz-gamma-0 } + +| Access | Source | +| -------------------------------------------- | ------------------------- | +| peak.broad_lorentz_gamma_0 | [code][0]{:.label-cif} | +| \_peak.broad_lorentz_gamma_0 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_lorentz_gamma_0 | [coreCIF][0]{:.label-cif} | + +Lorentzian broadening (microstrain effects). + +### :material-arrow-expand-horizontal: broad_lorentz_gamma_1 { #peak-broad-lorentz-gamma-1 } + +| Access | Source | +| -------------------------------------------- | ------------------------- | +| peak.broad_lorentz_gamma_1 | [code][0]{:.label-cif} | +| \_peak.broad_lorentz_gamma_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_lorentz_gamma_1 | [coreCIF][0]{:.label-cif} | + +Lorentzian broadening (dependent on d-spacing). + +### :material-arrow-expand-horizontal: broad_lorentz_gamma_2 { #peak-broad-lorentz-gamma-2 } + +| Access | Source | +| -------------------------------------------- | ------------------------- | +| peak.broad_lorentz_gamma_2 | [code][0]{:.label-cif} | +| \_peak.broad_lorentz_gamma_2 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_lorentz_gamma_2 | [coreCIF][0]{:.label-cif} | + +Lorentzian broadening (instrument-dependent term). + +### :material-arrow-bottom-right: decay_beta_0 { #peak-decay-beta-0 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.decay_beta_0 | [code][0]{:.label-cif} | +| \_peak.decay_beta_0 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.decay_beta_0 | [coreCIF][0]{:.label-cif} | + +Back-to-back exponential decay β₀. + +### :material-arrow-bottom-right: decay_beta_1 { #peak-decay-beta-1 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.decay_beta_1 | [code][0]{:.label-cif} | +| \_peak.decay_beta_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.decay_beta_1 | [coreCIF][0]{:.label-cif} | + +Back-to-back exponential decay β₁. + +### :material-scale-unbalanced: rise_alpha_0 { #peak-rise-alpha-0 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.rise_alpha_0 | [code][0]{:.label-cif} | +| \_peak.rise_alpha_0 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.rise_alpha_0 | [coreCIF][0]{:.label-cif} | + +Back-to-back exponential rise α₀. + +### :material-scale-unbalanced: rise_alpha_1 { #peak-rise-alpha-1 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.rise_alpha_1 | [code][0]{:.label-cif} | +| \_peak.rise_alpha_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.rise_alpha_1 | [coreCIF][0]{:.label-cif} | + +Back-to-back exponential rise α₁. + +## Total-Scattering Profiles + +[pd-neut-total][3]{:.label-experiment} +[pd-xray-total][3]{:.label-experiment} + +### :material-content-cut: cutoff_q { #peak-cutoff-q } + +| Access | Source | +| ------------------------------- | ------------------------- | +| peak.cutoff_q | [code][0]{:.label-cif} | +| \_peak.cutoff_q | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.cutoff_q | [coreCIF][0]{:.label-cif} | + +Q-value cutoff for Fourier transform. + +### :material-arrow-expand-horizontal: broad_q { #peak-broad-q } + +| Access | Source | +| ------------------------------ | ------------------------- | +| peak.broad_q | [code][0]{:.label-cif} | +| \_peak.broad_q | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.broad_q | [coreCIF][0]{:.label-cif} | + +Quadratic peak broadening from thermal uncertainty. + +### :material-knife: sharp_delta_1 { #peak-sharp-delta-1 } + +| Access | Source | +| ------------------------------------ | ------------------------- | +| peak.sharp_delta_1 | [code][0]{:.label-cif} | +| \_peak.sharp_delta_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.sharp_delta_1 | [coreCIF][0]{:.label-cif} | + +Peak sharpening coefficient (1/r dependence). + +### :material-knife: sharp_delta_2 { #peak-sharp-delta-2 } + +| Access | Source | +| ------------------------------------ | ------------------------- | +| peak.sharp_delta_2 | [code][0]{:.label-cif} | +| \_peak.sharp_delta_2 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.sharp_delta_2 | [coreCIF][0]{:.label-cif} | + +Peak sharpening coefficient (1/r² dependence). + +### :material-arrow-bottom-right: damp_q { #peak-damp-q } + +| Access | Source | +| ----------------------------- | ------------------------- | +| peak.damp_q | [code][0]{:.label-cif} | +| \_peak.damp_q | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.damp_q | [coreCIF][0]{:.label-cif} | + +Q-resolution damping for high-r PDF peak amplitude. + +### :material-arrow-bottom-right: damp_particle_diameter { #peak-damp-particle-diameter } + +| Access | Source | +| --------------------------------------------- | ------------------------- | +| peak.damp_particle_diameter | [code][0]{:.label-cif} | +| \_peak.damp_particle_diameter | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.damp_particle_diameter | [coreCIF][0]{:.label-cif} | + +Particle diameter for spherical envelope damping correction. + +## Additional Edi Keys + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} + +### :material-tag: asym_empir_1 { #peak-asym-empir-1 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.asym_empir_1 | [code][0]{:.label-cif} | +| \_peak.asym_empir_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.asym_empir_1 | [coreCIF][0]{:.label-cif} | + +Empirical asymmetry coefficient p1. + +### :material-tag: asym_empir_2 { #peak-asym-empir-2 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.asym_empir_2 | [code][0]{:.label-cif} | +| \_peak.asym_empir_2 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.asym_empir_2 | [coreCIF][0]{:.label-cif} | + +Empirical asymmetry coefficient p2. + +### :material-tag: asym_empir_3 { #peak-asym-empir-3 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.asym_empir_3 | [code][0]{:.label-cif} | +| \_peak.asym_empir_3 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.asym_empir_3 | [coreCIF][0]{:.label-cif} | + +Empirical asymmetry coefficient p3. + +### :material-tag: asym_empir_4 { #peak-asym-empir-4 } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| peak.asym_empir_4 | [code][0]{:.label-cif} | +| \_peak.asym_empir_4 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.asym_empir_4 | [coreCIF][0]{:.label-cif} | + +Empirical asymmetry coefficient p4. + +### :material-tag: asym_fcj_1 { #peak-asym-fcj-1 } + +| Access | Source | +| --------------------------------- | ------------------------- | +| peak.asym_fcj_1 | [code][0]{:.label-cif} | +| \_peak.asym_fcj_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.asym_fcj_1 | [coreCIF][0]{:.label-cif} | + +Finger-Cox-Jephcoat asymmetry parameter 1. + +### :material-tag: asym_fcj_2 { #peak-asym-fcj-2 } + +| Access | Source | +| --------------------------------- | ------------------------- | +| peak.asym_fcj_2 | [code][0]{:.label-cif} | +| \_peak.asym_fcj_2 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.asym_fcj_2 | [coreCIF][0]{:.label-cif} | + +Finger-Cox-Jephcoat asymmetry parameter 2. + +### :material-tag: dexp_decay_beta_00 { #peak-dexp-decay-beta-00 } + +| Access | Source | +| ----------------------------------------- | ------------------------- | +| peak.dexp_decay_beta_00 | [code][0]{:.label-cif} | +| \_peak.dexp_decay_beta_00 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_decay_beta_00 | [coreCIF][0]{:.label-cif} | + +Double-exp first-regime decay β₀₀. + +### :material-tag: dexp_decay_beta_01 { #peak-dexp-decay-beta-01 } + +| Access | Source | +| ----------------------------------------- | ------------------------- | +| peak.dexp_decay_beta_01 | [code][0]{:.label-cif} | +| \_peak.dexp_decay_beta_01 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_decay_beta_01 | [coreCIF][0]{:.label-cif} | + +Double-exp first-regime decay β₀₁. + +### :material-tag: dexp_decay_beta_10 { #peak-dexp-decay-beta-10 } + +| Access | Source | +| ----------------------------------------- | ------------------------- | +| peak.dexp_decay_beta_10 | [code][0]{:.label-cif} | +| \_peak.dexp_decay_beta_10 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_decay_beta_10 | [coreCIF][0]{:.label-cif} | + +Double-exp second-regime decay β₁₀. + +### :material-tag: dexp_rise_alpha_1 { #peak-dexp-rise-alpha-1 } + +| Access | Source | +| ---------------------------------------- | ------------------------- | +| peak.dexp_rise_alpha_1 | [code][0]{:.label-cif} | +| \_peak.dexp_rise_alpha_1 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_rise_alpha_1 | [coreCIF][0]{:.label-cif} | + +Double-exp rise parameter α₁. + +### :material-tag: dexp_rise_alpha_2 { #peak-dexp-rise-alpha-2 } + +| Access | Source | +| ---------------------------------------- | ------------------------- | +| peak.dexp_rise_alpha_2 | [code][0]{:.label-cif} | +| \_peak.dexp_rise_alpha_2 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_rise_alpha_2 | [coreCIF][0]{:.label-cif} | + +Double-exp rise parameter α₂. + +### :material-tag: dexp_switch_r_01 { #peak-dexp-switch-r-01 } + +| Access | Source | +| --------------------------------------- | ------------------------- | +| peak.dexp_switch_r_01 | [code][0]{:.label-cif} | +| \_peak.dexp_switch_r_01 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_switch_r_01 | [coreCIF][0]{:.label-cif} | + +Double-exp switching function r₀₁. + +### :material-tag: dexp_switch_r_02 { #peak-dexp-switch-r-02 } + +| Access | Source | +| --------------------------------------- | ------------------------- | +| peak.dexp_switch_r_02 | [code][0]{:.label-cif} | +| \_peak.dexp_switch_r_02 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_switch_r_02 | [coreCIF][0]{:.label-cif} | + +Double-exp switching function r₀₂. + +### :material-tag: dexp_switch_r_03 { #peak-dexp-switch-r-03 } + +| Access | Source | +| --------------------------------------- | ------------------------- | +| peak.dexp_switch_r_03 | [code][0]{:.label-cif} | +| \_peak.dexp_switch_r_03 | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.dexp_switch_r_03 | [coreCIF][0]{:.label-cif} | + +Double-exp switching function r₀₃. + +### :material-shape: type { #peak-type } + +| Access | Source | +| --------------------------- | ------------------------- | +| peak.type | [code][0]{:.label-cif} | +| \_peak.type | [Edi][0]{:.label-cif} | +| \_easydiffraction_peak.type | [coreCIF][0]{:.label-cif} | + +Active peak profile type tag. Supported values include +`cwl-pseudo-voigt`, `cwl-pseudo-voigt-empirical-asymmetry`, +`cwl-thompson-cox-hastings`, `tof-pseudo-voigt`, `tof-jorgensen`, +`tof-jorgensen-von-dreele`, `tof-double-jorgensen-von-dreele`, and +`total-gaussian-damped-sinc`. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/preferred_orientation.md b/docs/docs/user-guide/parameters/experiment/preferred_orientation.md new file mode 100644 index 000000000..9cbc087a4 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/preferred_orientation.md @@ -0,0 +1,73 @@ +--- +title: preferred_orientation +--- + +# :material-compass-outline: preferred_orientation + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} + +## :material-identifier: structure_id { #preferred-orientation-structure-id } + +| Access | Source | +| ---------------------------------------- | ----------------------- | +| preferred_orientation['ID'].structure_id | [code][0]{:.label-cif} | +| \_preferred_orientation.structure_id | [Edi][0]{:.label-cif} | +| \_pd_pref_orient_March_Dollase.phase_id | [pdCIF][0]{:.label-cif} | + +Identifier of the corrected structure. + +## :material-chart-bell-curve-cumulative: march_r { #preferred-orientation-march-r } + +| Access | Source | +| ----------------------------------- | ----------------------- | +| preferred_orientation['ID'].march_r | [code][0]{:.label-cif} | +| \_preferred_orientation.march_r | [Edi][0]{:.label-cif} | +| \_pd_pref_orient_March_Dollase.r | [pdCIF][0]{:.label-cif} | + +March coefficient (1 = no preferred orientation). + +## :material-axis-arrow: index_h { #preferred-orientation-index-h } + +| Access | Source | +| -------------------------------------- | ----------------------- | +| preferred_orientation['ID'].index_h | [code][0]{:.label-cif} | +| \_preferred_orientation.index_h | [Edi][0]{:.label-cif} | +| \_pd_pref_orient_March_Dollase.index_h | [pdCIF][0]{:.label-cif} | + +Texture-axis Miller index h. + +## :material-axis-arrow: index_k { #preferred-orientation-index-k } + +| Access | Source | +| -------------------------------------- | ----------------------- | +| preferred_orientation['ID'].index_k | [code][0]{:.label-cif} | +| \_preferred_orientation.index_k | [Edi][0]{:.label-cif} | +| \_pd_pref_orient_March_Dollase.index_k | [pdCIF][0]{:.label-cif} | + +Texture-axis Miller index k. + +## :material-axis-arrow: index_l { #preferred-orientation-index-l } + +| Access | Source | +| -------------------------------------- | ----------------------- | +| preferred_orientation['ID'].index_l | [code][0]{:.label-cif} | +| \_preferred_orientation.index_l | [Edi][0]{:.label-cif} | +| \_pd_pref_orient_March_Dollase.index_l | [pdCIF][0]{:.label-cif} | + +Texture-axis Miller index l. + +## :material-shuffle-variant: march_random_fract { #preferred-orientation-march-random-fract } + +| Access | Source | +| ------------------------------------------------ | ------------------------- | +| preferred_orientation['ID'].march_random_fract | [code][0]{:.label-cif} | +| \_preferred_orientation.march_random_fract | [Edi][0]{:.label-cif} | +| \_easydiffraction_pref_orient.march_random_fract | [coreCIF][0]{:.label-cif} | + +Random (untextured) fraction; 0 = pure March-Dollase. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/experiment/refln.md b/docs/docs/user-guide/parameters/experiment/refln.md new file mode 100644 index 000000000..a31546389 --- /dev/null +++ b/docs/docs/user-guide/parameters/experiment/refln.md @@ -0,0 +1,168 @@ +--- +title: refln +--- + +# :material-diamond-outline: refln + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-tag: d_spacing { #refln-d-spacing } + +| Access | Source | +| --------------------- | ---------------------- | +| refln['ID'].d_spacing | [code][0]{:.label-cif} | +| \_refln.d_spacing | [Edi][0]{:.label-cif} | + +Distance between lattice planes for this reflection. + +## :material-tag: f_calc { #refln-f-calc } + +| Access | Source | +| ------------------ | ---------------------- | +| refln['ID'].f_calc | [code][0]{:.label-cif} | +| \_refln.f_calc | [Edi][0]{:.label-cif} | + +Calculated structure-factor amplitude for this reflection. + +## :material-tag: f_squared_calc { #refln-f-squared-calc } + +| Access | Source | +| -------------------------- | ---------------------- | +| refln['ID'].f_squared_calc | [code][0]{:.label-cif} | +| \_refln.f_squared_calc | [Edi][0]{:.label-cif} | + +Calculated structure-factor amplitude squared for this reflection. + +## :material-tag: id { #refln-id } + +| Access | Source | +| -------------- | ---------------------- | +| refln['ID'].id | [code][0]{:.label-cif} | +| \_refln.id | [Edi][0]{:.label-cif} | + +Identifier of the reflection. + +## :material-tag: index_h { #refln-index-h } + +| Access | Source | +| ------------------- | ---------------------- | +| refln['ID'].index_h | [code][0]{:.label-cif} | +| \_refln.index_h | [Edi][0]{:.label-cif} | + +Miller index h of a measured reflection. + +## :material-tag: index_k { #refln-index-k } + +| Access | Source | +| ------------------- | ---------------------- | +| refln['ID'].index_k | [code][0]{:.label-cif} | +| \_refln.index_k | [Edi][0]{:.label-cif} | + +Miller index k of a measured reflection. + +## :material-tag: index_l { #refln-index-l } + +| Access | Source | +| ------------------- | ---------------------- | +| refln['ID'].index_l | [code][0]{:.label-cif} | +| \_refln.index_l | [Edi][0]{:.label-cif} | + +Miller index l of a measured reflection. + +## :material-arrow-collapse-up: intensity_calc { #refln-intensity-calc } + +| Access | Source | +| -------------------------- | ---------------------- | +| refln['ID'].intensity_calc | [code][0]{:.label-cif} | +| \_refln.intensity_calc | [Edi][0]{:.label-cif} | + +Intensity of the reflection calculated from atom site data. + +## :material-arrow-collapse-up: intensity_meas { #refln-intensity-meas } + +| Access | Source | +| -------------------------- | ---------------------- | +| refln['ID'].intensity_meas | [code][0]{:.label-cif} | +| \_refln.intensity_meas | [Edi][0]{:.label-cif} | + +The intensity of the reflection derived from the measurements. + +## :material-arrow-collapse-up: intensity_meas_su { #refln-intensity-meas-su } + +| Access | Source | +| ----------------------------- | ---------------------- | +| refln['ID'].intensity_meas_su | [code][0]{:.label-cif} | +| \_refln.intensity_meas_su | [Edi][0]{:.label-cif} | + +Standard uncertainty of the measured intensity. + +## :material-tag: sin_theta_over_lambda { #refln-sin-theta-over-lambda } + +| Access | Source | +| --------------------------------- | ---------------------- | +| refln['ID'].sin_theta_over_lambda | [code][0]{:.label-cif} | +| \_refln.sin_theta_over_lambda | [Edi][0]{:.label-cif} | + +The sin(θ)/λ value for this reflection. + +## :material-arrow-left-right: sin_theta_over_lambda_range_max { #refln-sin-theta-over-lambda-range-max } + +| Access | Source | +| --------------------------------------- | ---------------------- | +| data_range.sin_theta_over_lambda_max | [code][0]{:.label-cif} | +| \_refln.sin_theta_over_lambda_range_max | [Edi][0]{:.label-cif} | + +Upper sinθ/λ bound of the calculation range. + +## :material-arrow-left-right: sin_theta_over_lambda_range_min { #refln-sin-theta-over-lambda-range-min } + +| Access | Source | +| --------------------------------------- | ---------------------- | +| data_range.sin_theta_over_lambda_min | [code][0]{:.label-cif} | +| \_refln.sin_theta_over_lambda_range_min | [Edi][0]{:.label-cif} | + +Lower sinθ/λ bound of the calculation range. + +## :material-tag: structure_id { #refln-structure-id } + +| Access | Source | +| ------------------------ | ------------------------- | +| refln['ID'].structure_id | [code][0]{:.label-cif} | +| \_refln.structure_id | [Edi][0]{:.label-cif} | +| \_refln.phase_id | [coreCIF][0]{:.label-cif} | + +Identifier of the linked structure for this reflection. + +## :material-timer-outline: time_of_flight { #refln-time-of-flight } + +| Access | Source | +| -------------------------- | ---------------------- | +| refln['ID'].time_of_flight | [code][0]{:.label-cif} | +| \_refln.time_of_flight | [Edi][0]{:.label-cif} | + +Calculated time-of-flight position for this reflection. + +## :material-angle-acute: two_theta { #refln-two-theta } + +| Access | Source | +| --------------------- | ---------------------- | +| refln['ID'].two_theta | [code][0]{:.label-cif} | +| \_refln.two_theta | [Edi][0]{:.label-cif} | + +Calculated 2theta position for this reflection. + +## :material-sine-wave: wavelength { #refln-wavelength } + +| Access | Source | +| ---------------------- | ---------------------- | +| refln['ID'].wavelength | [code][0]{:.label-cif} | +| \_refln.wavelength | [Edi][0]{:.label-cif} | + +Mean wavelength of radiation for this reflection. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/expt_type.md b/docs/docs/user-guide/parameters/expt_type.md deleted file mode 100644 index c6d3605e9..000000000 --- a/docs/docs/user-guide/parameters/expt_type.md +++ /dev/null @@ -1,33 +0,0 @@ -[customCIF][0]{:.label-cif} - -# \_expt_type - -This EasyDiffraction category records the experiment type. The type -selects compatible experiment categories, calculators, peak profiles, -and data handling. - -## \_expt_type.beam_mode - -Beam mode used by the experiment. Supported values include -`constant wavelength` and `time-of-flight`. - -## \_expt_type.radiation_probe - -Radiation probe used by the experiment. Supported values include -`neutron` and `x-ray`. - -## \_expt_type.sample_form - -Sample form used by the experiment. Supported values include `powder` -and `single crystal`. - -## \_expt_type.scattering_type - -Scattering type used by the experiment. Supported values include `bragg` -and `total`. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/instrument.md b/docs/docs/user-guide/parameters/instrument.md deleted file mode 100644 index fdbfcdacd..000000000 --- a/docs/docs/user-guide/parameters/instrument.md +++ /dev/null @@ -1,57 +0,0 @@ -[customCIF][0]{:.label-cif} - -# \_instr - -This category stores instrument setup and calibration parameters used by -EasyDiffraction experiments. Constant-wavelength and time-of-flight -experiments use different subsets of the category. - -## \_instr.wavelength - -Incident neutron or X-ray wavelength for constant-wavelength -experiments. In Python this is accessed as: - -```python -experiment.instrument.setup_wavelength -``` - -## \_instr.2theta_offset - -Two-theta zero offset for constant-wavelength powder experiments. In -Python this is accessed as: - -```python -experiment.instrument.calib_twotheta_offset -``` - -## \_instr.2theta_bank - -Detector-bank two-theta angle for time-of-flight powder experiments. In -Python this is accessed as: - -```python -experiment.instrument.setup_twotheta_bank -``` - -## \_instr.d_to_tof - -Time-of-flight calibration terms converting d-spacing to time of flight. -In Python these are accessed as: - -- `experiment.instrument.calib_d_to_tof_recip` -- `experiment.instrument.calib_d_to_tof_offset` -- `experiment.instrument.calib_d_to_tof_linear` -- `experiment.instrument.calib_d_to_tof_quad` - -They serialize to the following CIF tags: - -- `_instr.d_to_tof_recip` -- `_instr.d_to_tof_offset` -- `_instr.d_to_tof_linear` -- `_instr.d_to_tof_quad` - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/linked_phases.md b/docs/docs/user-guide/parameters/linked_phases.md deleted file mode 100644 index 1d00f1d2e..000000000 --- a/docs/docs/user-guide/parameters/linked_phases.md +++ /dev/null @@ -1,22 +0,0 @@ -[pdCIF][2]{:.label-cif} - -# \_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. - -## [\_pd_phase_block.id](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) - -A block ID code identifying a block containing phase information. - -## \_pd_phase_block.scale - -Phase scale. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/pd_meas.md b/docs/docs/user-guide/parameters/pd_meas.md deleted file mode 100644 index fcb4db17f..000000000 --- a/docs/docs/user-guide/parameters/pd_meas.md +++ /dev/null @@ -1,29 +0,0 @@ -[pdCIF][2]{:.label-cif} - -# \_pd_meas - -This section contains the measured diffractogram, similar to this -[IUCr section](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd). - -## [\_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. - -## [\_pd_meas.time-of-flight](https://raw.githubusercontent.com/COMCIFS/Powder_Dictionary/master/cif_pow.dic) - -Measured time (in microseconds) for time-of-flight neutron measurements. - -## [\_pd_meas.intensity_total](https://raw.githubusercontent.com/COMCIFS/Powder_Dictionary/master/cif_pow.dic) - -Intensity recorded at each measurement point as a function of angle. - -## [\_pd_meas.intensity_total_su](https://raw.githubusercontent.com/COMCIFS/Powder_Dictionary/master/cif_pow.dic) - -Standard uncertainty of \_pd_meas.2theta_scan. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/peak.md b/docs/docs/user-guide/parameters/peak.md deleted file mode 100644 index e7b94565f..000000000 --- a/docs/docs/user-guide/parameters/peak.md +++ /dev/null @@ -1,69 +0,0 @@ -[customCIF][0]{:.label-cif} - -# \_peak - -This category stores peak-profile parameters used by powder diffraction -and total-scattering calculations. The active peak-profile category is -selected from the experiment: - -```python -experiment.peak.type = 'pseudo-voigt' -``` - -## Constant-wavelength powder profiles - -The constant-wavelength pseudo-Voigt profile uses Gaussian and -Lorentzian broadening parameters: - -- `_peak.broad_gauss_u` -- `_peak.broad_gauss_v` -- `_peak.broad_gauss_w` -- `_peak.broad_lorentz_x` -- `_peak.broad_lorentz_y` - -When empirical asymmetry is enabled, the profile also uses: - -- `_peak.asym_p1` -- `_peak.asym_p2` -- `_peak.asym_p3` -- `_peak.asym_p4` - -## Time-of-flight powder profiles - -The time-of-flight pseudo-Voigt profiles use instrument-dependent -Gaussian broadening, back-to-back exponential rise and decay terms, and -optionally Lorentzian broadening or double-exponential asymmetry terms: - -- `_peak.gauss_sigma_0` -- `_peak.gauss_sigma_1` -- `_peak.gauss_sigma_2` -- `_peak.rise_alpha_0` -- `_peak.rise_alpha_1` -- `_peak.decay_beta_0` -- `_peak.decay_beta_1` -- `_peak.lorentz_gamma_0` -- `_peak.lorentz_gamma_1` -- `_peak.lorentz_gamma_2` -- `_peak.dexp_rise_alpha_1` -- `_peak.dexp_rise_alpha_2` -- `_peak.dexp_decay_beta_00` -- `_peak.dexp_decay_beta_01` -- `_peak.dexp_decay_beta_10` - -## Total-scattering profiles - -Total-scattering peak parameters describe Q-space cutoff, broadening, -sharpening, and damping: - -- `_peak.cutoff_q` -- `_peak.broad_q` -- `_peak.sharp_delta_1` -- `_peak.sharp_delta_2` -- `_peak.damp_q` -- `_peak.damp_particle_diameter` - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/pref_orient.md b/docs/docs/user-guide/parameters/pref_orient.md deleted file mode 100644 index 17f23c2da..000000000 --- a/docs/docs/user-guide/parameters/pref_orient.md +++ /dev/null @@ -1,55 +0,0 @@ -[pdCIF][2]{:.label-cif} - -# \_pref_orient - -Per-phase **preferred-orientation** (texture) correction using the -March–Dollase model. Powder samples whose crystallites pack with a -preferred orientation (platy crystals lying flat, needles aligning) show -Bragg intensities that deviate from the ideal random-powder average; -this correction restores them. - -Each row corrects one linked phase along one crystallographic direction. -The defaults (`march_r = 1`, `march_random_fract = 0`) are a -mathematical no-op, so a freshly added row applies no texture until you -change them. Supported on the **CrysPy** engine for constant-wavelength -Bragg powder experiments. - -See the -[IUCr powder dictionary](https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd) -(`pd_pref_orient_March_Dollase`) for the standard data names. - -## \_pref_orient.phase_id - -Identifier of the linked phase corrected by this row. - -## \_pref_orient.march_r - -March coefficient `r`. `r = 1` means no preferred orientation; `r < 1` -describes disk-/plate-like crystallites and `r > 1` needle-like ones. -This is the standard IUCr/FullProf/GSAS coefficient (serialised as -`_pd_pref_orient_March_Dollase.r`); the CrysPy backend stores its -reciprocal internally. - -## \_pref_orient.index_h - -Texture-axis Miller index _h_. - -## \_pref_orient.index_k - -Texture-axis Miller index _k_. - -## \_pref_orient.index_l - -Texture-axis Miller index _l_. - -## \_pref_orient.march_random_fract - -Random (untextured) fraction of crystallites; `0` is pure March–Dollase. -There is no IUCr standard name for this quantity, so it is serialised -under the EasyDiffraction custom dictionary. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/alias.md b/docs/docs/user-guide/parameters/project/alias.md new file mode 100644 index 000000000..b96e23ef0 --- /dev/null +++ b/docs/docs/user-guide/parameters/project/alias.md @@ -0,0 +1,29 @@ +--- +title: alias +--- + +# :material-link-variant: alias + +## :material-tag: id { #alias-id } + +| Access | Source | +| -------------------------- | ------------------------- | +| aliases['ID'].id | [code][0]{:.label-cif} | +| \_alias.id | [Edi][0]{:.label-cif} | +| \_easydiffraction_alias.id | [coreCIF][0]{:.label-cif} | + +Human-readable alias id for a parameter. + +## :material-form-textbox: parameter_unique_name { #alias-parameter-unique-name } + +| Access | Source | +| --------------------------------------------- | ------------------------- | +| aliases['ID'].parameter_unique_name | [code][0]{:.label-cif} | +| \_alias.parameter_unique_name | [Edi][0]{:.label-cif} | +| \_easydiffraction_alias.parameter_unique_name | [coreCIF][0]{:.label-cif} | + +Unique name of the referenced parameter. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/metadata.md b/docs/docs/user-guide/parameters/project/metadata.md new file mode 100644 index 000000000..bca113c94 --- /dev/null +++ b/docs/docs/user-guide/parameters/project/metadata.md @@ -0,0 +1,69 @@ +--- +title: metadata +--- + +# :material-card-text-outline: metadata + +## :material-tag: created { #metadata-created } + +| Access | Source | +| ------------------ | ------------------------- | +| metadata.created | [code][0]{:.label-cif} | +| \_metadata.created | [Edi][0]{:.label-cif} | +| \_project.created | [coreCIF][0]{:.label-cif} | + +Project creation timestamp. + +## :material-text-box-outline: description { #metadata-description } + +| Access | Source | +| ---------------------- | ------------------------- | +| metadata.description | [code][0]{:.label-cif} | +| \_metadata.description | [Edi][0]{:.label-cif} | +| \_project.description | [coreCIF][0]{:.label-cif} | + +Project description. + +## :material-tag: last_modified { #metadata-last-modified } + +| Access | Source | +| ------------------------ | ------------------------- | +| metadata.last_modified | [code][0]{:.label-cif} | +| \_metadata.last_modified | [Edi][0]{:.label-cif} | +| \_project.last_modified | [coreCIF][0]{:.label-cif} | + +Project last-modified timestamp. + +## :material-form-textbox: name { #metadata-name } + +| Access | Source | +| --------------- | ------------------------- | +| metadata.name | [code][0]{:.label-cif} | +| \_metadata.name | [Edi][0]{:.label-cif} | +| \_project.id | [coreCIF][0]{:.label-cif} | + +Project identifier. + +## :material-tag: timestamp { #metadata-timestamp } + +| Access | Source | +| ----------------------------------- | ------------------------- | +| metadata.timestamp | [code][0]{:.label-cif} | +| \_metadata.timestamp | [Edi][0]{:.label-cif} | +| \_easydiffraction_project.timestamp | [coreCIF][0]{:.label-cif} | + +Project fit timestamp. + +## :material-format-title: title { #metadata-title } + +| Access | Source | +| ---------------- | ------------------------- | +| metadata.title | [code][0]{:.label-cif} | +| \_metadata.title | [Edi][0]{:.label-cif} | +| \_project.title | [coreCIF][0]{:.label-cif} | + +Project title. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/rendering_plot.md b/docs/docs/user-guide/parameters/project/rendering_plot.md new file mode 100644 index 000000000..f82c94f41 --- /dev/null +++ b/docs/docs/user-guide/parameters/project/rendering_plot.md @@ -0,0 +1,19 @@ +--- +title: rendering_plot +--- + +# :material-chart-areaspline: rendering_plot + +## :material-shape: type { #rendering-plot-type } + +| Access | Source | +| --------------------- | ---------------------- | +| rendering_plot.type | [code][0]{:.label-cif} | +| \_rendering_plot.type | [Edi][0]{:.label-cif} | + +RenderingPlot renderer backend type. Supported values include `auto`, +`asciichartpy`, and `plotly`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/rendering_structure.md b/docs/docs/user-guide/parameters/project/rendering_structure.md new file mode 100644 index 000000000..ac2bfcc8e --- /dev/null +++ b/docs/docs/user-guide/parameters/project/rendering_structure.md @@ -0,0 +1,19 @@ +--- +title: rendering_structure +--- + +# :material-molecule: rendering_structure + +## :material-shape: type { #rendering-structure-type } + +| Access | Source | +| -------------------------- | ---------------------- | +| rendering_structure.type | [code][0]{:.label-cif} | +| \_rendering_structure.type | [Edi][0]{:.label-cif} | + +Structure-view renderer backend type. Supported values include `auto`, +`ascii`, and `threejs`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/rendering_table.md b/docs/docs/user-guide/parameters/project/rendering_table.md new file mode 100644 index 000000000..fd621827c --- /dev/null +++ b/docs/docs/user-guide/parameters/project/rendering_table.md @@ -0,0 +1,19 @@ +--- +title: rendering_table +--- + +# :material-table: rendering_table + +## :material-shape: type { #rendering-table-type } + +| Access | Source | +| ---------------------- | ---------------------- | +| rendering_table.type | [code][0]{:.label-cif} | +| \_rendering_table.type | [Edi][0]{:.label-cif} | + +Table renderer backend type. Supported values include `auto`, `rich`, +and `pandas`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/report.md b/docs/docs/user-guide/parameters/project/report.md new file mode 100644 index 000000000..ffc368ef1 --- /dev/null +++ b/docs/docs/user-guide/parameters/project/report.md @@ -0,0 +1,54 @@ +--- +title: report +--- + +# :material-file-chart: report + +## :material-tag: cif { #report-cif } + +| Access | Source | +| ------------ | ---------------------- | +| report.cif | [code][0]{:.label-cif} | +| \_report.cif | [Edi][0]{:.label-cif} | + +Whether to write CIF reports when saving. + +## :material-tag: html { #report-html } + +| Access | Source | +| ------------- | ---------------------- | +| report.html | [code][0]{:.label-cif} | +| \_report.html | [Edi][0]{:.label-cif} | + +Whether to write HTML reports when saving. + +## :material-tag: html_offline { #report-html-offline } + +| Access | Source | +| --------------------- | ---------------------- | +| report.html_offline | [code][0]{:.label-cif} | +| \_report.html_offline | [Edi][0]{:.label-cif} | + +Whether HTML reports should embed assets. + +## :material-tag: pdf { #report-pdf } + +| Access | Source | +| ------------ | ---------------------- | +| report.pdf | [code][0]{:.label-cif} | +| \_report.pdf | [Edi][0]{:.label-cif} | + +Whether to write PDF reports when saving. + +## :material-tag: tex { #report-tex } + +| Access | Source | +| ------------ | ---------------------- | +| report.tex | [code][0]{:.label-cif} | +| \_report.tex | [Edi][0]{:.label-cif} | + +Whether to write TeX reports when saving. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/structure_style.md b/docs/docs/user-guide/parameters/project/structure_style.md new file mode 100644 index 000000000..c7a9a787f --- /dev/null +++ b/docs/docs/user-guide/parameters/project/structure_style.md @@ -0,0 +1,47 @@ +--- +title: structure_style +--- + +# :material-palette: structure_style + +## :material-tag: adp_probability { #structure-style-adp-probability } + +| Access | Source | +| --------------------------------- | ---------------------- | +| structure_style.adp_probability | [code][0]{:.label-cif} | +| \_structure_style.adp_probability | [Edi][0]{:.label-cif} | + +ORTEP probability level, a fraction in (0, 1). + +## :material-scale: atom_scale { #structure-style-atom-scale } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_style.atom_scale | [code][0]{:.label-cif} | +| \_structure_style.atom_scale | [Edi][0]{:.label-cif} | + +Overall ball-atom size factor (square-root compressed). + +## :material-tag: atom_view { #structure-style-atom-view } + +| Access | Source | +| --------------------------- | ---------------------- | +| structure_style.atom_view | [code][0]{:.label-cif} | +| \_structure_style.atom_view | [Edi][0]{:.label-cif} | + +How atoms are sized and shaped in the structure view. Supported values +include `vdw`, `covalent`, `ionic`, and `adp`. + +## :material-tag: color_scheme { #structure-style-color-scheme } + +| Access | Source | +| ------------------------------ | ---------------------- | +| structure_style.color_scheme | [code][0]{:.label-cif} | +| \_structure_style.color_scheme | [Edi][0]{:.label-cif} | + +Standard element colour scheme. Supported values include `jmol` and +`vesta`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/structure_view.md b/docs/docs/user-guide/parameters/project/structure_view.md new file mode 100644 index 000000000..8a53a4975 --- /dev/null +++ b/docs/docs/user-guide/parameters/project/structure_view.md @@ -0,0 +1,81 @@ +--- +title: structure_view +--- + +# :material-cube-scan: structure_view + +## :material-arrow-left-right: range_a_max { #structure-view-range-a-max } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_view.range_a_max | [code][0]{:.label-cif} | +| \_structure_view.range_a_max | [Edi][0]{:.label-cif} | + +Upper fractional bound along the unit-cell a axis. + +## :material-arrow-left-right: range_a_min { #structure-view-range-a-min } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_view.range_a_min | [code][0]{:.label-cif} | +| \_structure_view.range_a_min | [Edi][0]{:.label-cif} | + +Lower fractional bound along the unit-cell a axis. + +## :material-arrow-left-right: range_b_max { #structure-view-range-b-max } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_view.range_b_max | [code][0]{:.label-cif} | +| \_structure_view.range_b_max | [Edi][0]{:.label-cif} | + +Upper fractional bound along the unit-cell b axis. + +## :material-arrow-left-right: range_b_min { #structure-view-range-b-min } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_view.range_b_min | [code][0]{:.label-cif} | +| \_structure_view.range_b_min | [Edi][0]{:.label-cif} | + +Lower fractional bound along the unit-cell b axis. + +## :material-arrow-left-right: range_c_max { #structure-view-range-c-max } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_view.range_c_max | [code][0]{:.label-cif} | +| \_structure_view.range_c_max | [Edi][0]{:.label-cif} | + +Upper fractional bound along the unit-cell c axis. + +## :material-arrow-left-right: range_c_min { #structure-view-range-c-min } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_view.range_c_min | [code][0]{:.label-cif} | +| \_structure_view.range_c_min | [Edi][0]{:.label-cif} | + +Lower fractional bound along the unit-cell c axis. + +## :material-tag: show_labels { #structure-view-show-labels } + +| Access | Source | +| ---------------------------- | ---------------------- | +| structure_view.show_labels | [code][0]{:.label-cif} | +| \_structure_view.show_labels | [Edi][0]{:.label-cif} | + +Show atom labels when the view opens. + +## :material-tag: show_moments { #structure-view-show-moments } + +| Access | Source | +| ----------------------------- | ---------------------- | +| structure_view.show_moments | [code][0]{:.label-cif} | +| \_structure_view.show_moments | [Edi][0]{:.label-cif} | + +Show magnetic-moment arrows where the data exists. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/project/verbosity.md b/docs/docs/user-guide/parameters/project/verbosity.md new file mode 100644 index 000000000..1daf89ece --- /dev/null +++ b/docs/docs/user-guide/parameters/project/verbosity.md @@ -0,0 +1,19 @@ +--- +title: verbosity +--- + +# :material-message-alert-outline: verbosity + +## :material-tag: fit { #verbosity-fit } + +| Access | Source | +| --------------- | ---------------------- | +| verbosity.fit | [code][0]{:.label-cif} | +| \_verbosity.fit | [Edi][0]{:.label-cif} | + +Fitting process output verbosity. Supported values include `full`, +`short`, and `silent`. + +<!-- prettier-ignore-start --> +[0]: # +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/space_group.md b/docs/docs/user-guide/parameters/space_group.md deleted file mode 100644 index ae5bce6b8..000000000 --- a/docs/docs/user-guide/parameters/space_group.md +++ /dev/null @@ -1,25 +0,0 @@ -[coreCIF][1]{:.label-cif} - -# \_space_group - -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. - -## [\_space_group.IT_coordinate_system_code](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Ispace_group.IT_coordinate_system_code.html) - -A qualifier taken from the enumeration list identifying which setting in -International Tables for Crystallography Volume A (2002) (IT) is used. - -<!-- prettier-ignore-start --> -[0]: # -[1]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_core -[2]: https://www.iucr.org/resources/cif/dictionaries/browse/cif_pd -<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/structure/atom_site.md b/docs/docs/user-guide/parameters/structure/atom_site.md new file mode 100644 index 000000000..f586bcb87 --- /dev/null +++ b/docs/docs/user-guide/parameters/structure/atom_site.md @@ -0,0 +1,119 @@ +--- +title: atom_site +--- + +# :material-atom: atom_site + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-tag: id { #atom-site-id } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].id | [code][0]{:.label-cif} | +| \_atom_site.id | [Edi][0]{:.label-cif} | +| \_atom_site.label [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.label.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Unique identifier for the atom site. + +## :material-periodic-table: type_symbol { #atom-site-type-symbol } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].type_symbol | [code][0]{:.label-cif} | +| \_atom_site.type_symbol | [Edi][0]{:.label-cif} | +| \_atom_site.type_symbol [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.type_symbol.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Chemical symbol of the atom at this site. + +## :material-map-marker: fract_x { #atom-site-fract-x } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].fract_x | [code][0]{:.label-cif} | +| \_atom_site.fract_x | [Edi][0]{:.label-cif} | +| \_atom_site.fract_x [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.fract_x.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Fractional x-coordinate of the atom site within the unit cell. + +## :material-map-marker: fract_y { #atom-site-fract-y } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].fract_y | [code][0]{:.label-cif} | +| \_atom_site.fract_y | [Edi][0]{:.label-cif} | +| \_atom_site.fract_y [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.fract_y.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Fractional y-coordinate of the atom site within the unit cell. + +## :material-map-marker: fract_z { #atom-site-fract-z } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].fract_z | [code][0]{:.label-cif} | +| \_atom_site.fract_z | [Edi][0]{:.label-cif} | +| \_atom_site.fract_z [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.fract_z.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Fractional z-coordinate of the atom site within the unit cell. + +## :material-format-color-fill: occupancy { #atom-site-occupancy } + +| Access | Source | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].occupancy | [code][0]{:.label-cif} | +| \_atom_site.occupancy | [Edi][0]{:.label-cif} | +| \_atom_site.occupancy [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.occupancy.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Occupancy of the atom site, representing the fraction of the site +occupied by the atom type. + +## :material-cursor-move: adp_type { #atom-site-adp-type } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].adp_type | [code][0]{:.label-cif} | +| \_atom_site.adp_type | [Edi][0]{:.label-cif} | +| \_atom_site.ADP_type [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.ADP_type.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Type of atomic displacement parameter (ADP) used (e.g., Biso, Uiso, +Uani, Bani). Supported values include `Biso`, `Uiso`, `Bani`, `Uani`, +and `beta`. + +## :material-cursor-move: adp_iso { #atom-site-adp-iso } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].adp_iso | [code][0]{:.label-cif} | +| \_atom_site.adp_iso | [Edi][0]{:.label-cif} | +| \_atom_site.B_iso_or_equiv [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.B_iso_or_equiv.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Isotropic atomic displacement parameter (ADP) for the atom site. + +## :material-reflect-horizontal: multiplicity { #atom-site-multiplicity } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].multiplicity | [code][0]{:.label-cif} | +| \_atom_site.multiplicity | [Edi][0]{:.label-cif} | +| \_atom_site.site_symmetry_multiplicity [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.site_symmetry_multiplicity.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Site multiplicity derived from the Wyckoff position; None for an +untabulated space group. + +## :material-reflect-horizontal: wyckoff_letter { #atom-site-wyckoff-letter } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_sites['ID'].wyckoff_letter | [code][0]{:.label-cif} | +| \_atom_site.wyckoff_letter | [Edi][0]{:.label-cif} | +| \_atom_site.Wyckoff_symbol [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site.Wyckoff_symbol.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Wyckoff letter indicating the symmetry of the atom site within the space +group. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/structure/atom_site_aniso.md b/docs/docs/user-guide/parameters/structure/atom_site_aniso.md new file mode 100644 index 000000000..c80c33d59 --- /dev/null +++ b/docs/docs/user-guide/parameters/structure/atom_site_aniso.md @@ -0,0 +1,84 @@ +--- +title: atom_site_aniso +--- + +# :material-axis-arrow: atom_site_aniso + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-tag: adp_11 { #atom-site-aniso-adp-11 } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_site_aniso['ID'].adp_11 | [code][0]{:.label-cif} | +| \_atom_site_aniso.adp_11 | [Edi][0]{:.label-cif} | +| \_atom_site_aniso.B_11 [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site_aniso.B_11.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Anisotropic ADP tensor component (1,1). + +## :material-tag: adp_12 { #atom-site-aniso-adp-12 } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_site_aniso['ID'].adp_12 | [code][0]{:.label-cif} | +| \_atom_site_aniso.adp_12 | [Edi][0]{:.label-cif} | +| \_atom_site_aniso.B_12 [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site_aniso.B_12.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Anisotropic ADP tensor component (1,2). + +## :material-tag: adp_13 { #atom-site-aniso-adp-13 } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_site_aniso['ID'].adp_13 | [code][0]{:.label-cif} | +| \_atom_site_aniso.adp_13 | [Edi][0]{:.label-cif} | +| \_atom_site_aniso.B_13 [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site_aniso.B_13.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Anisotropic ADP tensor component (1,3). + +## :material-tag: adp_22 { #atom-site-aniso-adp-22 } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_site_aniso['ID'].adp_22 | [code][0]{:.label-cif} | +| \_atom_site_aniso.adp_22 | [Edi][0]{:.label-cif} | +| \_atom_site_aniso.B_22 [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site_aniso.B_22.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Anisotropic ADP tensor component (2,2). + +## :material-tag: adp_23 { #atom-site-aniso-adp-23 } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_site_aniso['ID'].adp_23 | [code][0]{:.label-cif} | +| \_atom_site_aniso.adp_23 | [Edi][0]{:.label-cif} | +| \_atom_site_aniso.B_23 [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site_aniso.B_23.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Anisotropic ADP tensor component (2,3). + +## :material-tag: adp_33 { #atom-site-aniso-adp-33 } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_site_aniso['ID'].adp_33 | [code][0]{:.label-cif} | +| \_atom_site_aniso.adp_33 | [Edi][0]{:.label-cif} | +| \_atom_site_aniso.B_33 [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site_aniso.B_33.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Anisotropic ADP tensor component (3,3). + +## :material-tag: id { #atom-site-aniso-id } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| atom_site_aniso['ID'].id | [code][0]{:.label-cif} | +| \_atom_site_aniso.id | [Edi][0]{:.label-cif} | +| \_atom_site_aniso.label [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Iatom_site_aniso.label.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Atom-site id matching the parent atom_site entry. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/structure/cell.md b/docs/docs/user-guide/parameters/structure/cell.md new file mode 100644 index 000000000..aa0e0bc84 --- /dev/null +++ b/docs/docs/user-guide/parameters/structure/cell.md @@ -0,0 +1,74 @@ +--- +title: cell +--- + +# :material-cube-outline: cell + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-ruler: length_a { #cell-length-a } + +| Access | Source | +| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| cell.length_a | [code][0]{:.label-cif} | +| \_cell.length_a | [Edi][0]{:.label-cif} | +| \_cell.length_a [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.length_a.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Length of the a axis of the unit cell. + +## :material-ruler: length_b { #cell-length-b } + +| Access | Source | +| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| cell.length_b | [code][0]{:.label-cif} | +| \_cell.length_b | [Edi][0]{:.label-cif} | +| \_cell.length_b [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.length_b.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Length of the b axis of the unit cell. + +## :material-ruler: length_c { #cell-length-c } + +| Access | Source | +| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| cell.length_c | [code][0]{:.label-cif} | +| \_cell.length_c | [Edi][0]{:.label-cif} | +| \_cell.length_c [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.length_c.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Length of the c axis of the unit cell. + +## :material-angle-acute: angle_alpha { #cell-angle-alpha } + +| Access | Source | +| --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| cell.angle_alpha | [code][0]{:.label-cif} | +| \_cell.angle_alpha | [Edi][0]{:.label-cif} | +| \_cell.angle_alpha [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.angle_alpha.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Angle between edges b and c. + +## :material-angle-acute: angle_beta { #cell-angle-beta } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| cell.angle_beta | [code][0]{:.label-cif} | +| \_cell.angle_beta | [Edi][0]{:.label-cif} | +| \_cell.angle_beta [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.angle_beta.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Angle between edges a and c. + +## :material-angle-acute: angle_gamma { #cell-angle-gamma } + +| Access | Source | +| --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| cell.angle_gamma | [code][0]{:.label-cif} | +| \_cell.angle_gamma | [Edi][0]{:.label-cif} | +| \_cell.angle_gamma [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Icell.angle_gamma.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Angle between edges a and b. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/structure/geom.md b/docs/docs/user-guide/parameters/structure/geom.md new file mode 100644 index 000000000..fe1910a62 --- /dev/null +++ b/docs/docs/user-guide/parameters/structure/geom.md @@ -0,0 +1,34 @@ +--- +title: geom +--- + +# :material-vector-polyline: geom + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-tag: bond_distance_inc { #geom-bond-distance-inc } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| geom.bond_distance_inc | [code][0]{:.label-cif} | +| \_geom.bond_distance_inc | [Edi][0]{:.label-cif} | +| \_geom.bond_distance_incr [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Igeom.bond_distance_incr.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Increment added to the summed bonding radii (angstrom). + +## :material-arrow-collapse-left: min_bond_distance_cutoff { #geom-min-bond-distance-cutoff } + +| Access | Source | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| geom.min_bond_distance_cutoff | [code][0]{:.label-cif} | +| \_geom.min_bond_distance_cutoff | [Edi][0]{:.label-cif} | +| \_geom.min_bond_distance_cutoff [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Igeom.min_bond_distance_cutoff.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Minimum permitted bonded distance (angstrom). + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/structure/space_group.md b/docs/docs/user-guide/parameters/structure/space_group.md new file mode 100644 index 000000000..f23cc7277 --- /dev/null +++ b/docs/docs/user-guide/parameters/structure/space_group.md @@ -0,0 +1,34 @@ +--- +title: space_group +--- + +# :material-space-station: space_group + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-tag: name_h_m { #space-group-name-h-m } + +| Access | Source | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| space_group.name_h_m | [code][0]{:.label-cif} | +| \_space_group.name_h_m | [Edi][0]{:.label-cif} | +| \_space_group.name_H-M_alt [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Ispace_group.name_H-M_alt.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +Hermann-Mauguin symbol of the space group. + +## :material-numeric: coord_system_code { #space-group-coord-system-code } + +| Access | Source | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| space_group.coord_system_code | [code][0]{:.label-cif} | +| \_space_group.coord_system_code | [Edi][0]{:.label-cif} | +| \_space_group.IT_coordinate_system_code [:material-open-in-new:](https://www.iucr.org/__data/iucr/cifdic_html/3/CORE_DIC/Ispace_group.IT_coordinate_system_code.html 'IUCr definition') | [coreCIF][0]{:.label-cif} | + +A qualifier identifying which setting in IT is used. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/user-guide/parameters/structure/space_group_Wyckoff.md b/docs/docs/user-guide/parameters/structure/space_group_Wyckoff.md new file mode 100644 index 000000000..08bd3e372 --- /dev/null +++ b/docs/docs/user-guide/parameters/structure/space_group_Wyckoff.md @@ -0,0 +1,59 @@ +--- +title: space_group_Wyckoff +--- + +# :material-grid: space_group_Wyckoff + +[pd-neut-cwl][3]{:.label-experiment} +[pd-neut-tof][3]{:.label-experiment} [pd-xray][3]{:.label-experiment} +[sc-neut-cwl][3]{:.label-experiment} + +## :material-tag: coords_xyz { #space-group-wyckoff-coords-xyz } + +| Access | Source | +| ------------------------------------ | ---------------------- | +| space_group_wyckoff['ID'].coords_xyz | [code][0]{:.label-cif} | +| \_space_group_Wyckoff.coords_xyz | [Edi][0]{:.label-cif} | + +Coordinates of the Wyckoff orbit. + +## :material-tag: id { #space-group-wyckoff-id } + +| Access | Source | +| ---------------------------- | ---------------------- | +| space_group_wyckoff['ID'].id | [code][0]{:.label-cif} | +| \_space_group_Wyckoff.id | [Edi][0]{:.label-cif} | + +Identifier of the Wyckoff position. + +## :material-tag: letter { #space-group-wyckoff-letter } + +| Access | Source | +| -------------------------------- | ---------------------- | +| space_group_wyckoff['ID'].letter | [code][0]{:.label-cif} | +| \_space_group_Wyckoff.letter | [Edi][0]{:.label-cif} | + +Wyckoff letter of the position. + +## :material-tag: multiplicity { #space-group-wyckoff-multiplicity } + +| Access | Source | +| -------------------------------------- | ---------------------- | +| space_group_wyckoff['ID'].multiplicity | [code][0]{:.label-cif} | +| \_space_group_Wyckoff.multiplicity | [Edi][0]{:.label-cif} | + +Multiplicity of the Wyckoff position. + +## :material-tag: site_symmetry { #space-group-wyckoff-site-symmetry } + +| Access | Source | +| --------------------------------------- | ---------------------- | +| space_group_wyckoff['ID'].site_symmetry | [code][0]{:.label-cif} | +| \_space_group_Wyckoff.site_symmetry | [Edi][0]{:.label-cif} | + +Site-symmetry symbol of the Wyckoff position. + +<!-- prettier-ignore-start --> +[0]: # +[3]: ../../glossary.md#experiment-type-labels +<!-- prettier-ignore-end --> diff --git a/docs/docs/verification/ci_skip.txt b/docs/docs/verification/ci_skip.txt index da629abd6..34e751fd3 100644 --- a/docs/docs/verification/ci_skip.txt +++ b/docs/docs/verification/ci_skip.txt @@ -15,9 +15,9 @@ # Example (do not leave commented examples as active entries): pd-neut-cwl_pv-asym_empir_pbso4 # Asymmetry params are different. Empirical asymmetry is not in ed-crysfml pd-neut-cwl_tch-fcj-noabs-nosldl_lab6 # needs unreleased cryspy PR #46 (SyCos/SySin, cos2theta convention) -pd-neut-cwl_tch-fcj-noabs_lab6 # FCJ asymmetry (S_L/D_L) not implemented in cryspy; see issues/open.md -pd-neut-cwl_tch-fcj_lab6 # FCJ asymmetry (S_L/D_L) not implemented in cryspy; absorption (muR=0.7, HEWAT) now modelled; see issues/open.md -pd-neut-cwl_tch-fcj-nosldl_lab6 # absorption demo passes on develop cryspy; released cryspy needs PR #46 (SyCos/SySin); see issues/open.md +pd-neut-cwl_tch-fcj-noabs_lab6 # FCJ asymmetry (S_L/D_L) not implemented in cryspy; see docs/dev/issues/index.md +pd-neut-cwl_tch-fcj_lab6 # FCJ asymmetry (S_L/D_L) not implemented in cryspy; absorption (muR=0.7, HEWAT) now modelled; see docs/dev/issues/index.md +pd-neut-cwl_tch-fcj-nosldl_lab6 # absorption demo passes on develop cryspy; released cryspy needs PR #46 (SyCos/SySin); see docs/dev/issues/index.md pd-neut-tof_j_si # ed-crysfml TOF Jorgensen profile ~8.5% off after fitting scale (area ratio 1.09, corr 0.997); cryspy matches FullProf -pd-neut-tof_jvd_si # cryspy TOF Lorentzian discrepancy; see issues/open.md +pd-neut-tof_jvd_si # cryspy TOF Lorentzian discrepancy; see docs/dev/issues/index.md sc-neut-cwl_ext-iso_tbti # Different asymmetry types in cryspy vs FullProf \ No newline at end of file diff --git a/docs/docs/verification/index.md b/docs/docs/verification/index.md index 121fa11ac..a308d3ed4 100644 --- a/docs/docs/verification/index.md +++ b/docs/docs/verification/index.md @@ -1,4 +1,5 @@ --- +title: Verification icon: material/check-decagram --- diff --git a/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.ipynb b/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.ipynb index f1a968fd2..4822c611e 100644 --- a/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.ipynb +++ b/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -82,7 +82,7 @@ "structure.cell.length_c = 6.958973 # FullProf c\n", "\n", "structure.atom_sites.create(\n", - " label='Pb', # FullProf Atom\n", + " id='Pb', # FullProf Atom\n", " type_symbol='Pb', # FullProf Typ\n", " fract_x=0.18752, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -91,7 +91,7 @@ " adp_iso=1.39017, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='S', # FullProf Atom\n", + " id='S', # FullProf Atom\n", " type_symbol='S', # FullProf Typ\n", " fract_x=0.06549, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -100,7 +100,7 @@ " adp_iso=0.39270, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O1', # FullProf Atom\n", + " id='O1', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.90816, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -109,7 +109,7 @@ " adp_iso=1.99307, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O2', # FullProf Atom\n", + " id='O2', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.19355, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -118,7 +118,7 @@ " adp_iso=1.47771, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O3', # FullProf Atom\n", + " id='O3', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.08109, # FullProf X\n", " fract_y=0.02727, # FullProf Y\n", @@ -193,7 +193,7 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='pbso4', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='pbso4', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -215,7 +215,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -246,7 +246,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -255,7 +255,7 @@ "id": "13", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -271,7 +271,7 @@ "# the symmetric profile are correct.\n", "experiment.calculator.type = 'cryspy'\n", "\n", - "experiment.linked_phases['pbso4'].scale.free = True\n", + "experiment.linked_structures['pbso4'].scale.free = True\n", "experiment.peak.asym_empir_1.free = True\n", "experiment.peak.asym_empir_2.free = True\n", "experiment.peak.asym_empir_3.free = True\n", @@ -288,7 +288,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy (refined)',\n", + " candidate_label='edi-cryspy (refined)',\n", ")" ] }, @@ -297,7 +297,7 @@ "id": "15", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -309,7 +309,7 @@ "source": [ "experiment.calculator.type = 'crysfml'\n", "\n", - "experiment.linked_phases['pbso4'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['pbso4'].scale = FULLPROF_SCALE\n", "\n", "experiment.peak.type = 'pseudo-voigt'\n", "experiment.peak.broad_gauss_u = FULLPROF_U\n", @@ -326,7 +326,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, @@ -335,7 +335,7 @@ "id": "17", "metadata": {}, "source": [ - "## Fit ed-crysfml to FullProf" + "## Fit edi-crysfml to FullProf" ] }, { @@ -345,7 +345,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['pbso4'].scale.free = True\n", + "experiment.linked_structures['pbso4'].scale.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True\n", "\n", "project.analysis.fit()\n", @@ -359,7 +359,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml (refined)',\n", + " candidate_label='edi-crysfml (refined)',\n", ")" ] }, diff --git a/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.py b/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.py index f297efcc7..d04f04526 100644 --- a/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.py +++ b/docs/docs/verification/pd-neut-cwl_pv-asym_empir_pbso4.py @@ -2,7 +2,7 @@ # # PbSO₄ — neutron powder, constant wavelength, empirical asymmetry # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -26,7 +26,7 @@ structure.cell.length_c = 6.958973 # FullProf c structure.atom_sites.create( - label='Pb', # FullProf Atom + id='Pb', # FullProf Atom type_symbol='Pb', # FullProf Typ fract_x=0.18752, # FullProf X fract_y=0.25, # FullProf Y @@ -35,7 +35,7 @@ adp_iso=1.39017, # FullProf Biso ) structure.atom_sites.create( - label='S', # FullProf Atom + id='S', # FullProf Atom type_symbol='S', # FullProf Typ fract_x=0.06549, # FullProf X fract_y=0.25, # FullProf Y @@ -44,7 +44,7 @@ adp_iso=0.39270, # FullProf Biso ) structure.atom_sites.create( - label='O1', # FullProf Atom + id='O1', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.90816, # FullProf X fract_y=0.25, # FullProf Y @@ -53,7 +53,7 @@ adp_iso=1.99307, # FullProf Biso ) structure.atom_sites.create( - label='O2', # FullProf Atom + id='O2', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.19355, # FullProf X fract_y=0.25, # FullProf Y @@ -62,7 +62,7 @@ adp_iso=1.47771, # FullProf Biso ) structure.atom_sites.create( - label='O3', # FullProf Atom + id='O3', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.08109, # FullProf X fract_y=0.02727, # FullProf Y @@ -113,7 +113,7 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='pbso4', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='pbso4', scale=FULLPROF_SCALE) experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -130,7 +130,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.peak.type = 'pseudo-voigt + empirical asymmetry' @@ -154,11 +154,11 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% # cryspy and FullProf parameterise the empirical asymmetry differently, so @@ -167,7 +167,7 @@ # the symmetric profile are correct. experiment.calculator.type = 'cryspy' -experiment.linked_phases['pbso4'].scale.free = True +experiment.linked_structures['pbso4'].scale.free = True experiment.peak.asym_empir_1.free = True experiment.peak.asym_empir_2.free = True experiment.peak.asym_empir_3.free = True @@ -184,16 +184,16 @@ reference=calc_fullprof, candidate=calc_ed_cryspy_refined, reference_label='FullProf', - candidate_label='ed-cryspy (refined)', + candidate_label='edi-cryspy (refined)', ) # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' -experiment.linked_phases['pbso4'].scale = FULLPROF_SCALE +experiment.linked_structures['pbso4'].scale = FULLPROF_SCALE experiment.peak.type = 'pseudo-voigt' experiment.peak.broad_gauss_u = FULLPROF_U @@ -210,14 +210,14 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label='FullProf', - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] -# ## Fit ed-crysfml to FullProf +# ## Fit edi-crysfml to FullProf # %% -experiment.linked_phases['pbso4'].scale.free = True +experiment.linked_structures['pbso4'].scale.free = True experiment.instrument.calib_twotheta_offset.free = True project.analysis.fit() @@ -231,7 +231,7 @@ reference=calc_fullprof, candidate=calc_ed_crysfml_refined, reference_label='FullProf', - candidate_label='ed-crysfml (refined)', + candidate_label='edi-crysfml (refined)', ) # %% [markdown] diff --git a/docs/docs/verification/pd-neut-cwl_pv-march_lbco.ipynb b/docs/docs/verification/pd-neut-cwl_pv-march_lbco.ipynb index 9157f5c5c..b3ff4f9e1 100644 --- a/docs/docs/verification/pd-neut-cwl_pv-march_lbco.ipynb +++ b/docs/docs/verification/pd-neut-cwl_pv-march_lbco.ipynb @@ -38,7 +38,7 @@ "`march_r` automatically, and the constant non-normalisation factor is\n", "absorbed by the scale (so the as-calculated pattern below shows an\n", "overall offset before fitting). After refining the two March–Dollase\n", - "parameters and the scale, ed-cryspy recovers `march_r ≈ 1.2` and\n", + "parameters and the scale, edi-cryspy recovers `march_r ≈ 1.2` and\n", "`march_random_fract ≈ 0.3` and the patterns agree." ] }, @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -70,7 +70,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -95,7 +95,7 @@ "structure.cell.length_a = 3.890790 # FullProf a\n", "\n", "structure.atom_sites.create(\n", - " label='La', # FullProf Atom\n", + " id='La', # FullProf Atom\n", " type_symbol='La', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -105,7 +105,7 @@ " adp_iso=0.57511, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='Ba', # FullProf Atom\n", + " id='Ba', # FullProf Atom\n", " type_symbol='Ba', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -115,7 +115,7 @@ " adp_iso=0.57511, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='Co', # FullProf Atom\n", + " id='Co', # FullProf Atom\n", " type_symbol='Co', # FullProf Typ\n", " fract_x=0.5, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -125,7 +125,7 @@ " adp_iso=0.26023, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O', # FullProf Atom\n", + " id='O', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -202,7 +202,7 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='lbco', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='lbco', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -215,7 +215,7 @@ "experiment.peak.broad_lorentz_y = FULLPROF_Y\n", "\n", "experiment.preferred_orientation.create(\n", - " phase_id='lbco',\n", + " structure_id='lbco',\n", " march_r=FULLPROF_PREF_1,\n", " march_random_fract=FULLPROF_PREF_2,\n", " index_h=FULLPROF_PR_1,\n", @@ -231,7 +231,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -251,7 +251,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -260,11 +260,11 @@ "id": "13", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf\n", + "## Fit edi-cryspy to FullProf\n", "\n", "Free the two March–Dollase parameters (`march_r`, `march_random_fract`)\n", "and the scale, then refine. Starting from the FullProf values,\n", - "ed-cryspy converges back to `march_r ≈ Pref1` and\n", + "edi-cryspy converges back to `march_r ≈ Pref1` and\n", "`march_random_fract ≈ Pref2`, and the patterns agree." ] }, @@ -275,7 +275,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lbco'].scale.free = True\n", + "experiment.linked_structures['lbco'].scale.free = True\n", "experiment.preferred_orientation['lbco'].march_r.free = True\n", "experiment.preferred_orientation['lbco'].march_random_fract.free = True\n", "\n", @@ -290,7 +290,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy (refined)',\n", + " candidate_label='edi-cryspy (refined)',\n", ")" ] }, @@ -325,7 +325,7 @@ "`march_random_fract` to `Pref2 = 0.3` (the latter only approximately,\n", "because CrysPy's non-normalised factor makes the random-fraction\n", "correspondence slightly non-linear), with all closeness metrics within\n", - "tolerance. ed-cryspy therefore reproduces the FullProf two-parameter\n", + "tolerance. edi-cryspy therefore reproduces the FullProf two-parameter\n", "March–Dollase correction once its reciprocal/unnormalised convention is\n", "accounted for by the backend mapping and the scale." ] diff --git a/docs/docs/verification/pd-neut-cwl_pv-march_lbco.py b/docs/docs/verification/pd-neut-cwl_pv-march_lbco.py index 6eeb47c94..5e3fa9064 100644 --- a/docs/docs/verification/pd-neut-cwl_pv-march_lbco.py +++ b/docs/docs/verification/pd-neut-cwl_pv-march_lbco.py @@ -13,11 +13,11 @@ # `march_r` automatically, and the constant non-normalisation factor is # absorbed by the scale (so the as-calculated pattern below shows an # overall offset before fitting). After refining the two March–Dollase -# parameters and the scale, ed-cryspy recovers `march_r ≈ 1.2` and +# parameters and the scale, edi-cryspy recovers `march_r ≈ 1.2` and # `march_random_fract ≈ 0.3` and the patterns agree. # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -26,7 +26,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -39,7 +39,7 @@ structure.cell.length_a = 3.890790 # FullProf a structure.atom_sites.create( - label='La', # FullProf Atom + id='La', # FullProf Atom type_symbol='La', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.0, # FullProf Y @@ -49,7 +49,7 @@ adp_iso=0.57511, # FullProf Biso ) structure.atom_sites.create( - label='Ba', # FullProf Atom + id='Ba', # FullProf Atom type_symbol='Ba', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.0, # FullProf Y @@ -59,7 +59,7 @@ adp_iso=0.57511, # FullProf Biso ) structure.atom_sites.create( - label='Co', # FullProf Atom + id='Co', # FullProf Atom type_symbol='Co', # FullProf Typ fract_x=0.5, # FullProf X fract_y=0.5, # FullProf Y @@ -69,7 +69,7 @@ adp_iso=0.26023, # FullProf Biso ) structure.atom_sites.create( - label='O', # FullProf Atom + id='O', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.5, # FullProf Y @@ -122,7 +122,7 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='lbco', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='lbco', scale=FULLPROF_SCALE) experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -135,7 +135,7 @@ experiment.peak.broad_lorentz_y = FULLPROF_Y experiment.preferred_orientation.create( - phase_id='lbco', + structure_id='lbco', march_r=FULLPROF_PREF_1, march_random_fract=FULLPROF_PREF_2, index_h=FULLPROF_PR_1, @@ -146,7 +146,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' @@ -159,19 +159,19 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # # Free the two March–Dollase parameters (`march_r`, `march_random_fract`) # and the scale, then refine. Starting from the FullProf values, -# ed-cryspy converges back to `march_r ≈ Pref1` and +# edi-cryspy converges back to `march_r ≈ Pref1` and # `march_random_fract ≈ Pref2`, and the patterns agree. # %% -experiment.linked_phases['lbco'].scale.free = True +experiment.linked_structures['lbco'].scale.free = True experiment.preferred_orientation['lbco'].march_r.free = True experiment.preferred_orientation['lbco'].march_random_fract.free = True @@ -186,7 +186,7 @@ reference=calc_fullprof, candidate=calc_ed_cryspy_refined, reference_label='FullProf', - candidate_label='ed-cryspy (refined)', + candidate_label='edi-cryspy (refined)', ) # %% [markdown] @@ -204,6 +204,6 @@ # `march_random_fract` to `Pref2 = 0.3` (the latter only approximately, # because CrysPy's non-normalised factor makes the random-fraction # correspondence slightly non-linear), with all closeness metrics within -# tolerance. ed-cryspy therefore reproduces the FullProf two-parameter +# tolerance. edi-cryspy therefore reproduces the FullProf two-parameter # March–Dollase correction once its reciprocal/unnormalised convention is # accounted for by the backend mapping and the scale. diff --git a/docs/docs/verification/pd-neut-cwl_pv_lbco.ipynb b/docs/docs/verification/pd-neut-cwl_pv_lbco.ipynb index 7c8b480fb..90878eb35 100644 --- a/docs/docs/verification/pd-neut-cwl_pv_lbco.ipynb +++ b/docs/docs/verification/pd-neut-cwl_pv_lbco.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -80,7 +80,7 @@ "structure.cell.length_a = 3.890790 # FullProf a\n", "\n", "structure.atom_sites.create(\n", - " label='La', # FullProf Atom\n", + " id='La', # FullProf Atom\n", " type_symbol='La', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -90,7 +90,7 @@ " adp_iso=0.57511, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='Ba', # FullProf Atom\n", + " id='Ba', # FullProf Atom\n", " type_symbol='Ba', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -100,7 +100,7 @@ " adp_iso=0.57511, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='Co', # FullProf Atom\n", + " id='Co', # FullProf Atom\n", " type_symbol='Co', # FullProf Typ\n", " fract_x=0.5, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -110,7 +110,7 @@ " adp_iso=0.26023, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O', # FullProf Atom\n", + " id='O', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -182,7 +182,7 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='lbco', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='lbco', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -202,7 +202,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -222,7 +222,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -231,7 +231,7 @@ "id": "13", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -251,7 +251,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, diff --git a/docs/docs/verification/pd-neut-cwl_pv_lbco.py b/docs/docs/verification/pd-neut-cwl_pv_lbco.py index bc5e43cc4..3274b1245 100644 --- a/docs/docs/verification/pd-neut-cwl_pv_lbco.py +++ b/docs/docs/verification/pd-neut-cwl_pv_lbco.py @@ -2,7 +2,7 @@ # # LBCO — neutron powder, constant wavelength, pseudo-Voigt # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -24,7 +24,7 @@ structure.cell.length_a = 3.890790 # FullProf a structure.atom_sites.create( - label='La', # FullProf Atom + id='La', # FullProf Atom type_symbol='La', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.0, # FullProf Y @@ -34,7 +34,7 @@ adp_iso=0.57511, # FullProf Biso ) structure.atom_sites.create( - label='Ba', # FullProf Atom + id='Ba', # FullProf Atom type_symbol='Ba', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.0, # FullProf Y @@ -44,7 +44,7 @@ adp_iso=0.57511, # FullProf Biso ) structure.atom_sites.create( - label='Co', # FullProf Atom + id='Co', # FullProf Atom type_symbol='Co', # FullProf Typ fract_x=0.5, # FullProf X fract_y=0.5, # FullProf Y @@ -54,7 +54,7 @@ adp_iso=0.26023, # FullProf Biso ) structure.atom_sites.create( - label='O', # FullProf Atom + id='O', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.5, # FullProf Y @@ -102,7 +102,7 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='lbco', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='lbco', scale=FULLPROF_SCALE) experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -117,7 +117,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' @@ -130,11 +130,11 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' @@ -147,7 +147,7 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label='FullProf', - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] diff --git a/docs/docs/verification/pd-neut-cwl_pv_pbso4.ipynb b/docs/docs/verification/pd-neut-cwl_pv_pbso4.ipynb index ac9bc0b57..d52d62417 100644 --- a/docs/docs/verification/pd-neut-cwl_pv_pbso4.ipynb +++ b/docs/docs/verification/pd-neut-cwl_pv_pbso4.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -82,7 +82,7 @@ "structure.cell.length_c = 6.957715 # FullProf c\n", "\n", "structure.atom_sites.create(\n", - " label='Pb', # FullProf Atom\n", + " id='Pb', # FullProf Atom\n", " type_symbol='Pb', # FullProf Typ\n", " fract_x=0.18754, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -91,7 +91,7 @@ " adp_iso=1.38058, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='S', # FullProf Atom\n", + " id='S', # FullProf Atom\n", " type_symbol='S', # FullProf Typ\n", " fract_x=0.06532, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -100,7 +100,7 @@ " adp_iso=0.36192, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O1', # FullProf Atom\n", + " id='O1', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.90822, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -109,7 +109,7 @@ " adp_iso=2.03661, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O2', # FullProf Atom\n", + " id='O2', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.19390, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -118,7 +118,7 @@ " adp_iso=1.50417, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='O3', # FullProf Atom\n", + " id='O3', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.08114, # FullProf X\n", " fract_y=0.02713, # FullProf Y\n", @@ -189,7 +189,7 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='pbso4', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='pbso4', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -209,7 +209,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -229,7 +229,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -238,7 +238,7 @@ "id": "13", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -258,7 +258,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, diff --git a/docs/docs/verification/pd-neut-cwl_pv_pbso4.py b/docs/docs/verification/pd-neut-cwl_pv_pbso4.py index 1d28f3c4d..1a22263cb 100644 --- a/docs/docs/verification/pd-neut-cwl_pv_pbso4.py +++ b/docs/docs/verification/pd-neut-cwl_pv_pbso4.py @@ -2,7 +2,7 @@ # # PbSO₄ — neutron powder, constant wavelength, pseudo-Voigt # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -26,7 +26,7 @@ structure.cell.length_c = 6.957715 # FullProf c structure.atom_sites.create( - label='Pb', # FullProf Atom + id='Pb', # FullProf Atom type_symbol='Pb', # FullProf Typ fract_x=0.18754, # FullProf X fract_y=0.25, # FullProf Y @@ -35,7 +35,7 @@ adp_iso=1.38058, # FullProf Biso ) structure.atom_sites.create( - label='S', # FullProf Atom + id='S', # FullProf Atom type_symbol='S', # FullProf Typ fract_x=0.06532, # FullProf X fract_y=0.25, # FullProf Y @@ -44,7 +44,7 @@ adp_iso=0.36192, # FullProf Biso ) structure.atom_sites.create( - label='O1', # FullProf Atom + id='O1', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.90822, # FullProf X fract_y=0.25, # FullProf Y @@ -53,7 +53,7 @@ adp_iso=2.03661, # FullProf Biso ) structure.atom_sites.create( - label='O2', # FullProf Atom + id='O2', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.19390, # FullProf X fract_y=0.25, # FullProf Y @@ -62,7 +62,7 @@ adp_iso=1.50417, # FullProf Biso ) structure.atom_sites.create( - label='O3', # FullProf Atom + id='O3', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.08114, # FullProf X fract_y=0.02713, # FullProf Y @@ -109,7 +109,7 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='pbso4', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='pbso4', scale=FULLPROF_SCALE) experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -124,7 +124,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' @@ -137,11 +137,11 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' @@ -154,7 +154,7 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label='FullProf', - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] diff --git a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.ipynb b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.ipynb index 7394a3a7f..728dc1126 100644 --- a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.ipynb +++ b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -77,7 +77,7 @@ "structure.space_group.name_h_m = 'P m -3 m' # FullProf Space group symbol\n", "structure.cell.length_a = 4.156885 # FullProf a\n", "structure.atom_sites.create(\n", - " label='La', # FullProf Atom\n", + " id='La', # FullProf Atom\n", " type_symbol='La', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -86,7 +86,7 @@ " adp_iso=0.25812, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='B', # FullProf Atom\n", + " id='B', # FullProf Atom\n", " type_symbol='11B', # FullProf \"B11 0.66500 0.00000 0\"\n", " fract_x=0.19972, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -159,7 +159,7 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='lab6', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='lab6', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -179,7 +179,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -202,7 +202,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -211,7 +211,7 @@ "id": "13", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -231,7 +231,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, @@ -240,7 +240,7 @@ "id": "15", "metadata": {}, "source": [ - "## Fit ed-crysfml to FullProf" + "## Fit edi-crysfml to FullProf" ] }, { @@ -250,7 +250,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lab6'].scale.free = True\n", + "experiment.linked_structures['lab6'].scale.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True\n", "\n", "project.analysis.fit()\n", @@ -264,7 +264,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml (refined)',\n", + " candidate_label='edi-crysfml (refined)',\n", ")" ] }, diff --git a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.py b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.py index 710e5f950..cf56c3dd5 100644 --- a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.py +++ b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs-nosldl_lab6.py @@ -2,7 +2,7 @@ # # LaB₆ — neutron powder, constant wavelength, SyCos/SySin # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -21,7 +21,7 @@ structure.space_group.name_h_m = 'P m -3 m' # FullProf Space group symbol structure.cell.length_a = 4.156885 # FullProf a structure.atom_sites.create( - label='La', # FullProf Atom + id='La', # FullProf Atom type_symbol='La', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.0, # FullProf Y @@ -30,7 +30,7 @@ adp_iso=0.25812, # FullProf Biso ) structure.atom_sites.create( - label='B', # FullProf Atom + id='B', # FullProf Atom type_symbol='11B', # FullProf "B11 0.66500 0.00000 0" fract_x=0.19972, # FullProf X fract_y=0.5, # FullProf Y @@ -79,7 +79,7 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='lab6', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='lab6', scale=FULLPROF_SCALE) experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -94,7 +94,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' @@ -110,11 +110,11 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' @@ -127,14 +127,14 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label='FullProf', - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] -# ## Fit ed-crysfml to FullProf +# ## Fit edi-crysfml to FullProf # %% -experiment.linked_phases['lab6'].scale.free = True +experiment.linked_structures['lab6'].scale.free = True experiment.instrument.calib_twotheta_offset.free = True project.analysis.fit() @@ -148,7 +148,7 @@ reference=calc_fullprof, candidate=calc_ed_crysfml_refined, reference_label='FullProf', - candidate_label='ed-crysfml (refined)', + candidate_label='edi-crysfml (refined)', ) # %% [markdown] diff --git a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.ipynb b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.ipynb index ef2dd5eab..3e2f6999a 100644 --- a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.ipynb +++ b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -77,7 +77,7 @@ "structure.space_group.name_h_m = 'P m -3 m' # FullProf Space group symbol\n", "structure.cell.length_a = 4.156885 # FullProf a\n", "structure.atom_sites.create(\n", - " label='La', # FullProf Atom\n", + " id='La', # FullProf Atom\n", " type_symbol='La', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -86,7 +86,7 @@ " adp_iso=0.32249, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='B', # FullProf Atom\n", + " id='B', # FullProf Atom\n", " type_symbol='11B', # FullProf \"B11\"\n", " fract_x=0.19978, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -161,7 +161,7 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='lab6', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='lab6', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -182,7 +182,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -205,7 +205,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -214,7 +214,7 @@ "id": "13", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -224,7 +224,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lab6'].scale.free = True\n", + "experiment.linked_structures['lab6'].scale.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True\n", "experiment.instrument.calib_sample_displacement.free = True\n", "experiment.instrument.calib_sample_transparency.free = True\n", @@ -240,7 +240,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy (refined)',\n", + " candidate_label='edi-cryspy (refined)',\n", ")" ] }, @@ -249,7 +249,7 @@ "id": "15", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -261,7 +261,7 @@ "source": [ "experiment.calculator.type = 'crysfml'\n", "\n", - "experiment.linked_phases['lab6'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['lab6'].scale = FULLPROF_SCALE\n", "\n", "experiment.peak.type = 'thompson-cox-hastings'\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -281,7 +281,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, @@ -290,7 +290,7 @@ "id": "17", "metadata": {}, "source": [ - "## Fit ed-crysfml to FullProf" + "## Fit edi-crysfml to FullProf" ] }, { @@ -300,7 +300,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lab6'].scale.free = True\n", + "experiment.linked_structures['lab6'].scale.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True\n", "\n", "experiment.instrument.calib_sample_displacement.free = False\n", @@ -317,7 +317,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml (refined)',\n", + " candidate_label='edi-crysfml (refined)',\n", ")" ] }, diff --git a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.py b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.py index 6ee449334..e5aac1edf 100644 --- a/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.py +++ b/docs/docs/verification/pd-neut-cwl_tch-fcj-noabs_lab6.py @@ -2,7 +2,7 @@ # # LaB₆ — neutron powder, constant wavelength, FCJ asymmetry # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -21,7 +21,7 @@ structure.space_group.name_h_m = 'P m -3 m' # FullProf Space group symbol structure.cell.length_a = 4.156885 # FullProf a structure.atom_sites.create( - label='La', # FullProf Atom + id='La', # FullProf Atom type_symbol='La', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.0, # FullProf Y @@ -30,7 +30,7 @@ adp_iso=0.32249, # FullProf Biso ) structure.atom_sites.create( - label='B', # FullProf Atom + id='B', # FullProf Atom type_symbol='11B', # FullProf "B11" fract_x=0.19978, # FullProf X fract_y=0.5, # FullProf Y @@ -81,7 +81,7 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='lab6', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='lab6', scale=FULLPROF_SCALE) experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -97,7 +97,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' @@ -113,14 +113,14 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% -experiment.linked_phases['lab6'].scale.free = True +experiment.linked_structures['lab6'].scale.free = True experiment.instrument.calib_twotheta_offset.free = True experiment.instrument.calib_sample_displacement.free = True experiment.instrument.calib_sample_transparency.free = True @@ -136,16 +136,16 @@ reference=calc_fullprof, candidate=calc_ed_cryspy_refined, reference_label='FullProf', - candidate_label='ed-cryspy (refined)', + candidate_label='edi-cryspy (refined)', ) # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' -experiment.linked_phases['lab6'].scale = FULLPROF_SCALE +experiment.linked_structures['lab6'].scale = FULLPROF_SCALE experiment.peak.type = 'thompson-cox-hastings' experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -165,14 +165,14 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label='FullProf', - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] -# ## Fit ed-crysfml to FullProf +# ## Fit edi-crysfml to FullProf # %% -experiment.linked_phases['lab6'].scale.free = True +experiment.linked_structures['lab6'].scale.free = True experiment.instrument.calib_twotheta_offset.free = True experiment.instrument.calib_sample_displacement.free = False @@ -189,7 +189,7 @@ reference=calc_fullprof, candidate=calc_ed_crysfml_refined, reference_label='FullProf', - candidate_label='ed-crysfml (refined)', + candidate_label='edi-crysfml (refined)', ) # %% [markdown] diff --git a/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.ipynb b/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.ipynb index 6d833be3d..9d8104788 100644 --- a/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.ipynb +++ b/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -77,7 +77,7 @@ "structure.space_group.name_h_m = 'P m -3 m' # FullProf Space group symbol\n", "structure.cell.length_a = 4.156885 # FullProf a\n", "structure.atom_sites.create(\n", - " label='La', # FullProf Atom\n", + " id='La', # FullProf Atom\n", " type_symbol='La', # FullProf Typ\n", " fract_x=0.0, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -86,7 +86,7 @@ " adp_iso=0.59951, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='B', # FullProf Atom\n", + " id='B', # FullProf Atom\n", " type_symbol='11B', # FullProf \"B11\"\n", " fract_x=0.19978, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -162,7 +162,7 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='lab6', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='lab6', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -189,7 +189,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -212,7 +212,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -221,7 +221,7 @@ "id": "13", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -231,7 +231,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lab6'].scale.free = True\n", + "experiment.linked_structures['lab6'].scale.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True\n", "experiment.instrument.calib_sample_displacement.free = True\n", "experiment.instrument.calib_sample_transparency.free = True\n", @@ -247,7 +247,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy (refined)',\n", + " candidate_label='edi-cryspy (refined)',\n", ")" ] }, @@ -256,7 +256,7 @@ "id": "15", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -268,7 +268,7 @@ "source": [ "experiment.calculator.type = 'crysfml'\n", "\n", - "experiment.linked_phases['lab6'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['lab6'].scale = FULLPROF_SCALE\n", "\n", "experiment.peak.type = 'thompson-cox-hastings'\n", "experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO\n", @@ -288,7 +288,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, @@ -297,7 +297,7 @@ "id": "17", "metadata": {}, "source": [ - "## Fit ed-crysfml to FullProf" + "## Fit edi-crysfml to FullProf" ] }, { @@ -307,7 +307,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['lab6'].scale.free = True\n", + "experiment.linked_structures['lab6'].scale.free = True\n", "experiment.instrument.calib_twotheta_offset.free = True\n", "\n", "experiment.instrument.calib_sample_displacement.free = False\n", @@ -324,7 +324,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-crysfml (refined)',\n", + " candidate_label='edi-crysfml (refined)',\n", ")" ] }, diff --git a/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.py b/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.py index 439ffed0c..8bdbd9e3f 100644 --- a/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.py +++ b/docs/docs/verification/pd-neut-cwl_tch-fcj_lab6.py @@ -2,7 +2,7 @@ # # LaB₆ — neutron powder, constant wavelength, sample absorption # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -21,7 +21,7 @@ structure.space_group.name_h_m = 'P m -3 m' # FullProf Space group symbol structure.cell.length_a = 4.156885 # FullProf a structure.atom_sites.create( - label='La', # FullProf Atom + id='La', # FullProf Atom type_symbol='La', # FullProf Typ fract_x=0.0, # FullProf X fract_y=0.0, # FullProf Y @@ -30,7 +30,7 @@ adp_iso=0.59951, # FullProf Biso ) structure.atom_sites.create( - label='B', # FullProf Atom + id='B', # FullProf Atom type_symbol='11B', # FullProf "B11" fract_x=0.19978, # FullProf X fract_y=0.5, # FullProf Y @@ -82,7 +82,7 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='lab6', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='lab6', scale=FULLPROF_SCALE) experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -104,7 +104,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' @@ -120,14 +120,14 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% -experiment.linked_phases['lab6'].scale.free = True +experiment.linked_structures['lab6'].scale.free = True experiment.instrument.calib_twotheta_offset.free = True experiment.instrument.calib_sample_displacement.free = True experiment.instrument.calib_sample_transparency.free = True @@ -143,16 +143,16 @@ reference=calc_fullprof, candidate=calc_ed_cryspy_refined, reference_label='FullProf', - candidate_label='ed-cryspy (refined)', + candidate_label='edi-cryspy (refined)', ) # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' -experiment.linked_phases['lab6'].scale = FULLPROF_SCALE +experiment.linked_structures['lab6'].scale = FULLPROF_SCALE experiment.peak.type = 'thompson-cox-hastings' experiment.instrument.calib_twotheta_offset = FULLPROF_ZERO @@ -172,14 +172,14 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label='FullProf', - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] -# ## Fit ed-crysfml to FullProf +# ## Fit edi-crysfml to FullProf # %% -experiment.linked_phases['lab6'].scale.free = True +experiment.linked_structures['lab6'].scale.free = True experiment.instrument.calib_twotheta_offset.free = True experiment.instrument.calib_sample_displacement.free = False @@ -196,7 +196,7 @@ reference=calc_fullprof, candidate=calc_ed_crysfml_refined, reference_label='FullProf', - candidate_label='ed-crysfml (refined)', + candidate_label='edi-crysfml (refined)', ) # %% [markdown] diff --git a/docs/docs/verification/pd-neut-tof_j_si.ipynb b/docs/docs/verification/pd-neut-tof_j_si.ipynb index e1a8d46fc..2a03b97a9 100644 --- a/docs/docs/verification/pd-neut-tof_j_si.ipynb +++ b/docs/docs/verification/pd-neut-tof_j_si.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -76,12 +76,12 @@ "structure = StructureFactory.from_scratch(name='si')\n", "\n", "structure.space_group.name_h_m = 'F d -3 m' # FullProf Space group symbol\n", - "structure.space_group.it_coordinate_system_code = '2'\n", + "structure.space_group.coord_system_code = '2'\n", "\n", "structure.cell.length_a = 5.432382 # FullProf a\n", "\n", "structure.atom_sites.create(\n", - " label='Si', # FullProf Atom\n", + " id='Si', # FullProf Atom\n", " type_symbol='Si', # FullProf Typ\n", " fract_x=0.125, # FullProf X\n", " fract_y=0.125, # FullProf Y\n", @@ -158,21 +158,21 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='si', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='si', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_twotheta_bank = FULLPROF_TWOTHETA_BANK\n", "experiment.instrument.calib_d_to_tof_offset = FULLPROF_ZERO\n", "experiment.instrument.calib_d_to_tof_linear = FULLPROF_DTT1\n", - "experiment.instrument.calib_d_to_tof_quad = FULLPROF_DTT2\n", + "experiment.instrument.calib_d_to_tof_quadratic = FULLPROF_DTT2\n", "\n", "experiment.peak.type = 'jorgensen'\n", "experiment.peak.broad_gauss_sigma_0 = FULLPROF_SIGMA_0\n", "experiment.peak.broad_gauss_sigma_1 = FULLPROF_SIGMA_1\n", "experiment.peak.broad_gauss_sigma_2 = FULLPROF_SIGMA_2\n", - "experiment.peak.exp_rise_alpha_0 = FULLPROF_ALPHA_0\n", - "experiment.peak.exp_rise_alpha_1 = FULLPROF_ALPHA_1\n", - "experiment.peak.exp_decay_beta_0 = FULLPROF_BETA_0\n", - "experiment.peak.exp_decay_beta_1 = FULLPROF_BETA_1\n", + "experiment.peak.rise_alpha_0 = FULLPROF_ALPHA_0\n", + "experiment.peak.rise_alpha_1 = FULLPROF_ALPHA_1\n", + "experiment.peak.decay_beta_0 = FULLPROF_BETA_0\n", + "experiment.peak.decay_beta_1 = FULLPROF_BETA_1\n", "\n", "experiment.excluded_regions.create(id='1', start=0, end=5000)\n", "experiment.excluded_regions.create(id='2', start=10000, end=100000)\n", @@ -185,7 +185,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -197,7 +197,7 @@ "source": [ "experiment.calculator.type = 'cryspy'\n", "\n", - "experiment.linked_phases['si'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['si'].scale = FULLPROF_SCALE\n", "\n", "project.analysis.calculate()\n", "calc_ed_cryspy = experiment.data.intensity_calc\n", @@ -207,7 +207,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -216,7 +216,7 @@ "id": "13", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -226,8 +226,8 @@ "metadata": {}, "outputs": [], "source": [ - "# experiment.linked_phases['si'].scale = 15.102255770454704\n", - "experiment.linked_phases['si'].scale.free = True\n", + "# experiment.linked_structures['si'].scale = 15.102255770454704\n", + "experiment.linked_structures['si'].scale.free = True\n", "\n", "project.analysis.fit()\n", "project.display.fit.results()\n", @@ -240,7 +240,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy_refined,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-cryspy (refined)',\n", + " candidate_label='edi-cryspy (refined)',\n", ")" ] }, @@ -251,7 +251,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['si'].scale" + "experiment.linked_structures['si'].scale" ] }, { @@ -259,7 +259,7 @@ "id": "16", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -271,7 +271,7 @@ "source": [ "experiment.calculator.type = 'crysfml'\n", "\n", - "experiment.linked_phases['si'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['si'].scale = FULLPROF_SCALE\n", "\n", "project.analysis.calculate()\n", "calc_ed_crysfml = experiment.data.intensity_calc\n", @@ -281,7 +281,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, @@ -290,7 +290,7 @@ "id": "18", "metadata": {}, "source": [ - "## Fit ed-crysfml to FullProf" + "## Fit edi-crysfml to FullProf" ] }, { @@ -300,8 +300,8 @@ "metadata": {}, "outputs": [], "source": [ - "# experiment.linked_phases['si'].scale = 1275.028259237954\n", - "experiment.linked_phases['si'].scale.free = True\n", + "# experiment.linked_structures['si'].scale = 1275.028259237954\n", + "experiment.linked_structures['si'].scale.free = True\n", "\n", "project.analysis.fit()\n", "project.display.fit.results()\n", @@ -314,7 +314,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml_refined,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-crysfml (refined)',\n", + " candidate_label='edi-crysfml (refined)',\n", ")" ] }, @@ -325,7 +325,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['si'].scale" + "experiment.linked_structures['si'].scale" ] }, { diff --git a/docs/docs/verification/pd-neut-tof_j_si.py b/docs/docs/verification/pd-neut-tof_j_si.py index c5c110b16..1656bd5e9 100644 --- a/docs/docs/verification/pd-neut-tof_j_si.py +++ b/docs/docs/verification/pd-neut-tof_j_si.py @@ -2,7 +2,7 @@ # # Si — neutron powder, time-of-flight, Jorgensen # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -20,12 +20,12 @@ structure = StructureFactory.from_scratch(name='si') structure.space_group.name_h_m = 'F d -3 m' # FullProf Space group symbol -structure.space_group.it_coordinate_system_code = '2' +structure.space_group.coord_system_code = '2' structure.cell.length_a = 5.432382 # FullProf a structure.atom_sites.create( - label='Si', # FullProf Atom + id='Si', # FullProf Atom type_symbol='Si', # FullProf Typ fract_x=0.125, # FullProf X fract_y=0.125, # FullProf Y @@ -78,21 +78,21 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='si', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='si', scale=FULLPROF_SCALE) experiment.instrument.setup_twotheta_bank = FULLPROF_TWOTHETA_BANK experiment.instrument.calib_d_to_tof_offset = FULLPROF_ZERO experiment.instrument.calib_d_to_tof_linear = FULLPROF_DTT1 -experiment.instrument.calib_d_to_tof_quad = FULLPROF_DTT2 +experiment.instrument.calib_d_to_tof_quadratic = FULLPROF_DTT2 experiment.peak.type = 'jorgensen' experiment.peak.broad_gauss_sigma_0 = FULLPROF_SIGMA_0 experiment.peak.broad_gauss_sigma_1 = FULLPROF_SIGMA_1 experiment.peak.broad_gauss_sigma_2 = FULLPROF_SIGMA_2 -experiment.peak.exp_rise_alpha_0 = FULLPROF_ALPHA_0 -experiment.peak.exp_rise_alpha_1 = FULLPROF_ALPHA_1 -experiment.peak.exp_decay_beta_0 = FULLPROF_BETA_0 -experiment.peak.exp_decay_beta_1 = FULLPROF_BETA_1 +experiment.peak.rise_alpha_0 = FULLPROF_ALPHA_0 +experiment.peak.rise_alpha_1 = FULLPROF_ALPHA_1 +experiment.peak.decay_beta_0 = FULLPROF_BETA_0 +experiment.peak.decay_beta_1 = FULLPROF_BETA_1 experiment.excluded_regions.create(id='1', start=0, end=5000) experiment.excluded_regions.create(id='2', start=10000, end=100000) @@ -100,12 +100,12 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' -experiment.linked_phases['si'].scale = FULLPROF_SCALE +experiment.linked_structures['si'].scale = FULLPROF_SCALE project.analysis.calculate() calc_ed_cryspy = experiment.data.intensity_calc @@ -115,15 +115,15 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label=FULLPROF_LABEL, - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% -# experiment.linked_phases['si'].scale = 15.102255770454704 -experiment.linked_phases['si'].scale.free = True +# experiment.linked_structures['si'].scale = 15.102255770454704 +experiment.linked_structures['si'].scale.free = True project.analysis.fit() project.display.fit.results() @@ -136,19 +136,19 @@ reference=calc_fullprof, candidate=calc_ed_cryspy_refined, reference_label=FULLPROF_LABEL, - candidate_label='ed-cryspy (refined)', + candidate_label='edi-cryspy (refined)', ) # %% -experiment.linked_phases['si'].scale +experiment.linked_structures['si'].scale # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' -experiment.linked_phases['si'].scale = FULLPROF_SCALE +experiment.linked_structures['si'].scale = FULLPROF_SCALE project.analysis.calculate() calc_ed_crysfml = experiment.data.intensity_calc @@ -158,15 +158,15 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label=FULLPROF_LABEL, - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] -# ## Fit ed-crysfml to FullProf +# ## Fit edi-crysfml to FullProf # %% -# experiment.linked_phases['si'].scale = 1275.028259237954 -experiment.linked_phases['si'].scale.free = True +# experiment.linked_structures['si'].scale = 1275.028259237954 +experiment.linked_structures['si'].scale.free = True project.analysis.fit() project.display.fit.results() @@ -179,11 +179,11 @@ reference=calc_fullprof, candidate=calc_ed_crysfml_refined, reference_label=FULLPROF_LABEL, - candidate_label='ed-crysfml (refined)', + candidate_label='edi-crysfml (refined)', ) # %% -experiment.linked_phases['si'].scale +experiment.linked_structures['si'].scale # %% [markdown] # ## Agreement check diff --git a/docs/docs/verification/pd-neut-tof_jvd_ncaf.ipynb b/docs/docs/verification/pd-neut-tof_jvd_ncaf.ipynb index 72d127877..b0c171add 100644 --- a/docs/docs/verification/pd-neut-tof_jvd_ncaf.ipynb +++ b/docs/docs/verification/pd-neut-tof_jvd_ncaf.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -77,7 +77,7 @@ "structure.space_group.name_h_m = 'I 21 3' # FullProf Space group symbol\n", "structure.cell.length_a = 10.250256 # FullProf a\n", "structure.atom_sites.create(\n", - " label='Ca', # FullProf Atom\n", + " id='Ca', # FullProf Atom\n", " type_symbol='Ca', # FullProf Typ\n", " fract_x=0.46610, # FullProf X\n", " fract_y=0.0, # FullProf Y\n", @@ -86,7 +86,7 @@ " adp_iso=0.88721, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='Al', # FullProf Atom\n", + " id='Al', # FullProf Atom\n", " type_symbol='Al', # FullProf Typ\n", " fract_x=0.25163, # FullProf X\n", " fract_y=0.25163, # FullProf Y\n", @@ -95,7 +95,7 @@ " adp_iso=0.65230, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='Na', # FullProf Atom\n", + " id='Na', # FullProf Atom\n", " type_symbol='Na', # FullProf Typ\n", " fract_x=0.08472, # FullProf X\n", " fract_y=0.08472, # FullProf Y\n", @@ -104,7 +104,7 @@ " adp_iso=1.89168, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='F1', # FullProf Atom\n", + " id='F1', # FullProf Atom\n", " type_symbol='F', # FullProf Typ\n", " fract_x=0.13748, # FullProf X\n", " fract_y=0.30533, # FullProf Y\n", @@ -113,7 +113,7 @@ " adp_iso=0.89535, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='F2', # FullProf Atom\n", + " id='F2', # FullProf Atom\n", " type_symbol='F', # FullProf Typ\n", " fract_x=0.36263, # FullProf X\n", " fract_y=0.36333, # FullProf Y\n", @@ -122,7 +122,7 @@ " adp_iso=1.27175, # FullProf Biso\n", ")\n", "structure.atom_sites.create(\n", - " label='F3', # FullProf Atom\n", + " id='F3', # FullProf Atom\n", " type_symbol='F', # FullProf Typ\n", " fract_x=0.46120, # FullProf X\n", " fract_y=0.46120, # FullProf Y\n", @@ -202,12 +202,12 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='ncaf', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='ncaf', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_twotheta_bank = FULLPROF_TWOTHETA_BANK\n", "experiment.instrument.calib_d_to_tof_offset = FULLPROF_ZERO\n", "experiment.instrument.calib_d_to_tof_linear = FULLPROF_DTT1\n", - "experiment.instrument.calib_d_to_tof_quad = FULLPROF_DTT2\n", + "experiment.instrument.calib_d_to_tof_quadratic = FULLPROF_DTT2\n", "\n", "experiment.peak.type = 'jorgensen-von-dreele'\n", "experiment.peak.broad_gauss_sigma_0 = FULLPROF_SIGMA_0\n", @@ -216,10 +216,10 @@ "experiment.peak.broad_lorentz_gamma_0 = FULLPROF_GAMMA_0\n", "experiment.peak.broad_lorentz_gamma_1 = FULLPROF_GAMMA_1\n", "experiment.peak.broad_lorentz_gamma_2 = FULLPROF_GAMMA_2\n", - "experiment.peak.exp_rise_alpha_0 = FULLPROF_ALPHA_0\n", - "experiment.peak.exp_rise_alpha_1 = FULLPROF_ALPHA_1\n", - "experiment.peak.exp_decay_beta_0 = FULLPROF_BETA_0\n", - "experiment.peak.exp_decay_beta_1 = FULLPROF_BETA_1\n", + "experiment.peak.rise_alpha_0 = FULLPROF_ALPHA_0\n", + "experiment.peak.rise_alpha_1 = FULLPROF_ALPHA_1\n", + "experiment.peak.decay_beta_0 = FULLPROF_BETA_0\n", + "experiment.peak.decay_beta_1 = FULLPROF_BETA_1\n", "\n", "experiment.excluded_regions.create(id='1', start=0, end=30000)\n", "experiment.excluded_regions.create(id='2', start=50000, end=200000)\n", @@ -232,7 +232,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -244,7 +244,7 @@ "source": [ "experiment.calculator.type = 'cryspy'\n", "\n", - "experiment.linked_phases['ncaf'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['ncaf'].scale = FULLPROF_SCALE\n", "\n", "project.analysis.calculate()\n", "calc_ed_cryspy = experiment.data.intensity_calc\n", @@ -254,7 +254,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -263,7 +263,7 @@ "id": "13", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -273,8 +273,8 @@ "metadata": {}, "outputs": [], "source": [ - "# experiment.linked_phases['ncaf'].scale = 1.0927822317965166\n", - "experiment.linked_phases['ncaf'].scale.free = True\n", + "# experiment.linked_structures['ncaf'].scale = 1.0927822317965166\n", + "experiment.linked_structures['ncaf'].scale.free = True\n", "\n", "project.analysis.fit()\n", "project.display.fit.results()\n", @@ -287,7 +287,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy_refined,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-cryspy (refined)',\n", + " candidate_label='edi-cryspy (refined)',\n", ")" ] }, @@ -298,7 +298,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['ncaf'].scale" + "experiment.linked_structures['ncaf'].scale" ] }, { @@ -306,7 +306,7 @@ "id": "16", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -318,7 +318,7 @@ "source": [ "experiment.calculator.type = 'crysfml'\n", "\n", - "experiment.linked_phases['ncaf'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['ncaf'].scale = FULLPROF_SCALE\n", "\n", "project.analysis.calculate()\n", "calc_ed_crysfml = experiment.data.intensity_calc\n", @@ -328,7 +328,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, @@ -337,7 +337,7 @@ "id": "18", "metadata": {}, "source": [ - "## Fit ed-crysfml to FullProf" + "## Fit edi-crysfml to FullProf" ] }, { @@ -347,8 +347,8 @@ "metadata": {}, "outputs": [], "source": [ - "# experiment.linked_phases['ncaf'].scale = 307.9429\n", - "experiment.linked_phases['ncaf'].scale.free = True\n", + "# experiment.linked_structures['ncaf'].scale = 307.9429\n", + "experiment.linked_structures['ncaf'].scale.free = True\n", "\n", "project.analysis.fit()\n", "project.display.fit.results()\n", @@ -361,7 +361,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml_refined,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-crysfml (refined)',\n", + " candidate_label='edi-crysfml (refined)',\n", ")" ] }, @@ -372,7 +372,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['ncaf'].scale" + "experiment.linked_structures['ncaf'].scale" ] }, { diff --git a/docs/docs/verification/pd-neut-tof_jvd_ncaf.py b/docs/docs/verification/pd-neut-tof_jvd_ncaf.py index 6b2654985..0563ef3ae 100644 --- a/docs/docs/verification/pd-neut-tof_jvd_ncaf.py +++ b/docs/docs/verification/pd-neut-tof_jvd_ncaf.py @@ -2,7 +2,7 @@ # # Na₂Ca₃Al₂F₁₄ — neutron powder, time-of-flight, Jorgensen–Von Dreele # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -21,7 +21,7 @@ structure.space_group.name_h_m = 'I 21 3' # FullProf Space group symbol structure.cell.length_a = 10.250256 # FullProf a structure.atom_sites.create( - label='Ca', # FullProf Atom + id='Ca', # FullProf Atom type_symbol='Ca', # FullProf Typ fract_x=0.46610, # FullProf X fract_y=0.0, # FullProf Y @@ -30,7 +30,7 @@ adp_iso=0.88721, # FullProf Biso ) structure.atom_sites.create( - label='Al', # FullProf Atom + id='Al', # FullProf Atom type_symbol='Al', # FullProf Typ fract_x=0.25163, # FullProf X fract_y=0.25163, # FullProf Y @@ -39,7 +39,7 @@ adp_iso=0.65230, # FullProf Biso ) structure.atom_sites.create( - label='Na', # FullProf Atom + id='Na', # FullProf Atom type_symbol='Na', # FullProf Typ fract_x=0.08472, # FullProf X fract_y=0.08472, # FullProf Y @@ -48,7 +48,7 @@ adp_iso=1.89168, # FullProf Biso ) structure.atom_sites.create( - label='F1', # FullProf Atom + id='F1', # FullProf Atom type_symbol='F', # FullProf Typ fract_x=0.13748, # FullProf X fract_y=0.30533, # FullProf Y @@ -57,7 +57,7 @@ adp_iso=0.89535, # FullProf Biso ) structure.atom_sites.create( - label='F2', # FullProf Atom + id='F2', # FullProf Atom type_symbol='F', # FullProf Typ fract_x=0.36263, # FullProf X fract_y=0.36333, # FullProf Y @@ -66,7 +66,7 @@ adp_iso=1.27175, # FullProf Biso ) structure.atom_sites.create( - label='F3', # FullProf Atom + id='F3', # FullProf Atom type_symbol='F', # FullProf Typ fract_x=0.46120, # FullProf X fract_y=0.46120, # FullProf Y @@ -122,12 +122,12 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='ncaf', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='ncaf', scale=FULLPROF_SCALE) experiment.instrument.setup_twotheta_bank = FULLPROF_TWOTHETA_BANK experiment.instrument.calib_d_to_tof_offset = FULLPROF_ZERO experiment.instrument.calib_d_to_tof_linear = FULLPROF_DTT1 -experiment.instrument.calib_d_to_tof_quad = FULLPROF_DTT2 +experiment.instrument.calib_d_to_tof_quadratic = FULLPROF_DTT2 experiment.peak.type = 'jorgensen-von-dreele' experiment.peak.broad_gauss_sigma_0 = FULLPROF_SIGMA_0 @@ -136,10 +136,10 @@ experiment.peak.broad_lorentz_gamma_0 = FULLPROF_GAMMA_0 experiment.peak.broad_lorentz_gamma_1 = FULLPROF_GAMMA_1 experiment.peak.broad_lorentz_gamma_2 = FULLPROF_GAMMA_2 -experiment.peak.exp_rise_alpha_0 = FULLPROF_ALPHA_0 -experiment.peak.exp_rise_alpha_1 = FULLPROF_ALPHA_1 -experiment.peak.exp_decay_beta_0 = FULLPROF_BETA_0 -experiment.peak.exp_decay_beta_1 = FULLPROF_BETA_1 +experiment.peak.rise_alpha_0 = FULLPROF_ALPHA_0 +experiment.peak.rise_alpha_1 = FULLPROF_ALPHA_1 +experiment.peak.decay_beta_0 = FULLPROF_BETA_0 +experiment.peak.decay_beta_1 = FULLPROF_BETA_1 experiment.excluded_regions.create(id='1', start=0, end=30000) experiment.excluded_regions.create(id='2', start=50000, end=200000) @@ -147,12 +147,12 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' -experiment.linked_phases['ncaf'].scale = FULLPROF_SCALE +experiment.linked_structures['ncaf'].scale = FULLPROF_SCALE project.analysis.calculate() calc_ed_cryspy = experiment.data.intensity_calc @@ -162,15 +162,15 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label=FULLPROF_LABEL, - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% -# experiment.linked_phases['ncaf'].scale = 1.0927822317965166 -experiment.linked_phases['ncaf'].scale.free = True +# experiment.linked_structures['ncaf'].scale = 1.0927822317965166 +experiment.linked_structures['ncaf'].scale.free = True project.analysis.fit() project.display.fit.results() @@ -183,19 +183,19 @@ reference=calc_fullprof, candidate=calc_ed_cryspy_refined, reference_label=FULLPROF_LABEL, - candidate_label='ed-cryspy (refined)', + candidate_label='edi-cryspy (refined)', ) # %% -experiment.linked_phases['ncaf'].scale +experiment.linked_structures['ncaf'].scale # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' -experiment.linked_phases['ncaf'].scale = FULLPROF_SCALE +experiment.linked_structures['ncaf'].scale = FULLPROF_SCALE project.analysis.calculate() calc_ed_crysfml = experiment.data.intensity_calc @@ -205,15 +205,15 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label=FULLPROF_LABEL, - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] -# ## Fit ed-crysfml to FullProf +# ## Fit edi-crysfml to FullProf # %% -# experiment.linked_phases['ncaf'].scale = 307.9429 -experiment.linked_phases['ncaf'].scale.free = True +# experiment.linked_structures['ncaf'].scale = 307.9429 +experiment.linked_structures['ncaf'].scale.free = True project.analysis.fit() project.display.fit.results() @@ -226,11 +226,11 @@ reference=calc_fullprof, candidate=calc_ed_crysfml_refined, reference_label=FULLPROF_LABEL, - candidate_label='ed-crysfml (refined)', + candidate_label='edi-crysfml (refined)', ) # %% -experiment.linked_phases['ncaf'].scale +experiment.linked_structures['ncaf'].scale # %% [markdown] # ## Agreement check diff --git a/docs/docs/verification/pd-neut-tof_jvd_si.ipynb b/docs/docs/verification/pd-neut-tof_jvd_si.ipynb index 0730d6a79..0aad56566 100644 --- a/docs/docs/verification/pd-neut-tof_jvd_si.ipynb +++ b/docs/docs/verification/pd-neut-tof_jvd_si.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -76,12 +76,12 @@ "structure = StructureFactory.from_scratch(name='si')\n", "\n", "structure.space_group.name_h_m = 'F d -3 m' # FullProf Space group symbol\n", - "structure.space_group.it_coordinate_system_code = '2'\n", + "structure.space_group.coord_system_code = '2'\n", "\n", "structure.cell.length_a = 5.431342 # FullProf a\n", "\n", "structure.atom_sites.create(\n", - " label='Si', # FullProf Atom\n", + " id='Si', # FullProf Atom\n", " type_symbol='Si', # FullProf Typ\n", " fract_x=0.125, # FullProf X\n", " fract_y=0.125, # FullProf Y\n", @@ -161,12 +161,12 @@ ")\n", "verify.set_reference_as_measured(experiment, x, calc_fullprof)\n", "\n", - "experiment.linked_phases.create(id='si', scale=FULLPROF_SCALE)\n", + "experiment.linked_structures.create(structure_id='si', scale=FULLPROF_SCALE)\n", "\n", "experiment.instrument.setup_twotheta_bank = FULLPROF_TWOTHETA_BANK\n", "experiment.instrument.calib_d_to_tof_offset = FULLPROF_ZERO\n", "experiment.instrument.calib_d_to_tof_linear = FULLPROF_DTT1\n", - "experiment.instrument.calib_d_to_tof_quad = FULLPROF_DTT2\n", + "experiment.instrument.calib_d_to_tof_quadratic = FULLPROF_DTT2\n", "\n", "experiment.peak.type = 'jorgensen-von-dreele'\n", "experiment.peak.broad_gauss_sigma_0 = FULLPROF_SIGMA_0\n", @@ -175,10 +175,10 @@ "experiment.peak.broad_lorentz_gamma_0 = FULLPROF_GAMMA_0\n", "experiment.peak.broad_lorentz_gamma_1 = FULLPROF_GAMMA_1\n", "experiment.peak.broad_lorentz_gamma_2 = FULLPROF_GAMMA_2\n", - "experiment.peak.exp_rise_alpha_0 = FULLPROF_ALPHA_0\n", - "experiment.peak.exp_rise_alpha_1 = FULLPROF_ALPHA_1\n", - "experiment.peak.exp_decay_beta_0 = FULLPROF_BETA_0\n", - "experiment.peak.exp_decay_beta_1 = FULLPROF_BETA_1\n", + "experiment.peak.rise_alpha_0 = FULLPROF_ALPHA_0\n", + "experiment.peak.rise_alpha_1 = FULLPROF_ALPHA_1\n", + "experiment.peak.decay_beta_0 = FULLPROF_BETA_0\n", + "experiment.peak.decay_beta_1 = FULLPROF_BETA_1\n", "\n", "experiment.excluded_regions.create(id='1', start=0, end=5000)\n", "experiment.excluded_regions.create(id='2', start=10000, end=100000)\n", @@ -191,7 +191,7 @@ "id": "11", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -203,7 +203,7 @@ "source": [ "experiment.calculator.type = 'cryspy'\n", "\n", - "experiment.linked_phases['si'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['si'].scale = FULLPROF_SCALE\n", "\n", "project.analysis.calculate()\n", "calc_ed_cryspy = experiment.data.intensity_calc\n", @@ -213,7 +213,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -222,7 +222,7 @@ "id": "13", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -232,9 +232,9 @@ "metadata": {}, "outputs": [], "source": [ - "# experiment.linked_phases['si'].scale = 16.558439186694915\n", + "# experiment.linked_structures['si'].scale = 16.558439186694915\n", "# experiment.peak.broad_lorentz_gamma_1 = 9.998261092381231\n", - "experiment.linked_phases['si'].scale.free = True\n", + "experiment.linked_structures['si'].scale.free = True\n", "experiment.peak.broad_lorentz_gamma_1.free = True\n", "\n", "project.analysis.fit()\n", @@ -248,7 +248,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_cryspy_refined,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-cryspy (refined)',\n", + " candidate_label='edi-cryspy (refined)',\n", ")" ] }, @@ -259,7 +259,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['si'].scale" + "experiment.linked_structures['si'].scale" ] }, { @@ -277,7 +277,7 @@ "id": "17", "metadata": {}, "source": [ - "## ed-crysfml VS FullProf" + "## edi-crysfml VS FullProf" ] }, { @@ -289,7 +289,7 @@ "source": [ "experiment.calculator.type = 'crysfml'\n", "\n", - "experiment.linked_phases['si'].scale = FULLPROF_SCALE\n", + "experiment.linked_structures['si'].scale = FULLPROF_SCALE\n", "experiment.peak.broad_lorentz_gamma_1 = FULLPROF_GAMMA_1\n", "\n", "project.analysis.calculate()\n", @@ -300,7 +300,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-crysfml',\n", + " candidate_label='edi-crysfml',\n", ")" ] }, @@ -309,7 +309,7 @@ "id": "19", "metadata": {}, "source": [ - "## Fit ed-crysfml to FullProf" + "## Fit edi-crysfml to FullProf" ] }, { @@ -319,8 +319,8 @@ "metadata": {}, "outputs": [], "source": [ - "# experiment.linked_phases['si'].scale = 1275.028259237954\n", - "experiment.linked_phases['si'].scale.free = True\n", + "# experiment.linked_structures['si'].scale = 1275.028259237954\n", + "experiment.linked_structures['si'].scale.free = True\n", "experiment.peak.broad_lorentz_gamma_1.free = False\n", "\n", "project.analysis.fit()\n", @@ -334,7 +334,7 @@ " reference=calc_fullprof,\n", " candidate=calc_ed_crysfml_refined,\n", " reference_label=FULLPROF_LABEL,\n", - " candidate_label='ed-crysfml (refined)',\n", + " candidate_label='edi-crysfml (refined)',\n", ")" ] }, @@ -345,7 +345,7 @@ "metadata": {}, "outputs": [], "source": [ - "experiment.linked_phases['si'].scale" + "experiment.linked_structures['si'].scale" ] }, { diff --git a/docs/docs/verification/pd-neut-tof_jvd_si.py b/docs/docs/verification/pd-neut-tof_jvd_si.py index 9331c24fa..23c8876b0 100644 --- a/docs/docs/verification/pd-neut-tof_jvd_si.py +++ b/docs/docs/verification/pd-neut-tof_jvd_si.py @@ -2,7 +2,7 @@ # # Si — neutron powder, time-of-flight, Jorgensen–Von Dreele # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -20,12 +20,12 @@ structure = StructureFactory.from_scratch(name='si') structure.space_group.name_h_m = 'F d -3 m' # FullProf Space group symbol -structure.space_group.it_coordinate_system_code = '2' +structure.space_group.coord_system_code = '2' structure.cell.length_a = 5.431342 # FullProf a structure.atom_sites.create( - label='Si', # FullProf Atom + id='Si', # FullProf Atom type_symbol='Si', # FullProf Typ fract_x=0.125, # FullProf X fract_y=0.125, # FullProf Y @@ -81,12 +81,12 @@ ) verify.set_reference_as_measured(experiment, x, calc_fullprof) -experiment.linked_phases.create(id='si', scale=FULLPROF_SCALE) +experiment.linked_structures.create(structure_id='si', scale=FULLPROF_SCALE) experiment.instrument.setup_twotheta_bank = FULLPROF_TWOTHETA_BANK experiment.instrument.calib_d_to_tof_offset = FULLPROF_ZERO experiment.instrument.calib_d_to_tof_linear = FULLPROF_DTT1 -experiment.instrument.calib_d_to_tof_quad = FULLPROF_DTT2 +experiment.instrument.calib_d_to_tof_quadratic = FULLPROF_DTT2 experiment.peak.type = 'jorgensen-von-dreele' experiment.peak.broad_gauss_sigma_0 = FULLPROF_SIGMA_0 @@ -95,10 +95,10 @@ experiment.peak.broad_lorentz_gamma_0 = FULLPROF_GAMMA_0 experiment.peak.broad_lorentz_gamma_1 = FULLPROF_GAMMA_1 experiment.peak.broad_lorentz_gamma_2 = FULLPROF_GAMMA_2 -experiment.peak.exp_rise_alpha_0 = FULLPROF_ALPHA_0 -experiment.peak.exp_rise_alpha_1 = FULLPROF_ALPHA_1 -experiment.peak.exp_decay_beta_0 = FULLPROF_BETA_0 -experiment.peak.exp_decay_beta_1 = FULLPROF_BETA_1 +experiment.peak.rise_alpha_0 = FULLPROF_ALPHA_0 +experiment.peak.rise_alpha_1 = FULLPROF_ALPHA_1 +experiment.peak.decay_beta_0 = FULLPROF_BETA_0 +experiment.peak.decay_beta_1 = FULLPROF_BETA_1 experiment.excluded_regions.create(id='1', start=0, end=5000) experiment.excluded_regions.create(id='2', start=10000, end=100000) @@ -106,12 +106,12 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% experiment.calculator.type = 'cryspy' -experiment.linked_phases['si'].scale = FULLPROF_SCALE +experiment.linked_structures['si'].scale = FULLPROF_SCALE project.analysis.calculate() calc_ed_cryspy = experiment.data.intensity_calc @@ -121,16 +121,16 @@ reference=calc_fullprof, candidate=calc_ed_cryspy, reference_label=FULLPROF_LABEL, - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% -# experiment.linked_phases['si'].scale = 16.558439186694915 +# experiment.linked_structures['si'].scale = 16.558439186694915 # experiment.peak.broad_lorentz_gamma_1 = 9.998261092381231 -experiment.linked_phases['si'].scale.free = True +experiment.linked_structures['si'].scale.free = True experiment.peak.broad_lorentz_gamma_1.free = True project.analysis.fit() @@ -144,22 +144,22 @@ reference=calc_fullprof, candidate=calc_ed_cryspy_refined, reference_label=FULLPROF_LABEL, - candidate_label='ed-cryspy (refined)', + candidate_label='edi-cryspy (refined)', ) # %% -experiment.linked_phases['si'].scale +experiment.linked_structures['si'].scale # %% experiment.peak.broad_lorentz_gamma_1 # %% [markdown] -# ## ed-crysfml VS FullProf +# ## edi-crysfml VS FullProf # %% experiment.calculator.type = 'crysfml' -experiment.linked_phases['si'].scale = FULLPROF_SCALE +experiment.linked_structures['si'].scale = FULLPROF_SCALE experiment.peak.broad_lorentz_gamma_1 = FULLPROF_GAMMA_1 project.analysis.calculate() @@ -170,15 +170,15 @@ reference=calc_fullprof, candidate=calc_ed_crysfml, reference_label=FULLPROF_LABEL, - candidate_label='ed-crysfml', + candidate_label='edi-crysfml', ) # %% [markdown] -# ## Fit ed-crysfml to FullProf +# ## Fit edi-crysfml to FullProf # %% -# experiment.linked_phases['si'].scale = 1275.028259237954 -experiment.linked_phases['si'].scale.free = True +# experiment.linked_structures['si'].scale = 1275.028259237954 +experiment.linked_structures['si'].scale.free = True experiment.peak.broad_lorentz_gamma_1.free = False project.analysis.fit() @@ -192,11 +192,11 @@ reference=calc_fullprof, candidate=calc_ed_crysfml_refined, reference_label=FULLPROF_LABEL, - candidate_label='ed-crysfml (refined)', + candidate_label='edi-crysfml (refined)', ) # %% -experiment.linked_phases['si'].scale +experiment.linked_structures['si'].scale # %% experiment.peak.broad_lorentz_gamma_1 diff --git a/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.ipynb b/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.ipynb index 3d486427a..f139ce0fb 100644 --- a/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.ipynb +++ b/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -86,7 +86,7 @@ "# in the site multiplicity; CIF/EasyDiffraction use 1.0 for a fully\n", "# occupied site.\n", "structure.atom_sites.create(\n", - " label='Tb', # FullProf Atom\n", + " id='Tb', # FullProf Atom\n", " type_symbol='Tb', # FullProf Typ\n", " fract_x=0.5, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -98,7 +98,7 @@ "aniso.adp_12 = -0.00047650724 # FullProf beta12\n", "\n", "structure.atom_sites.create(\n", - " label='Ti', # FullProf Atom\n", + " id='Ti', # FullProf Atom\n", " type_symbol='Ti', # FullProf Typ\n", " fract_x=0, # FullProf X\n", " fract_y=0, # FullProf Y\n", @@ -110,7 +110,7 @@ "aniso.adp_12 = -0.00016990340 # FullProf beta12\n", "\n", "structure.atom_sites.create(\n", - " label='O1', # FullProf Atom\n", + " id='O1', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.32804, # FullProf X\n", " fract_y=0.125, # FullProf Y\n", @@ -123,7 +123,7 @@ "aniso.adp_23 = 0.00041246481 # FullProf beta23\n", "\n", "structure.atom_sites.create(\n", - " label='O2', # FullProf Atom\n", + " id='O2', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.375, # FullProf X\n", " fract_y=0.375, # FullProf Y\n", @@ -143,7 +143,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -195,8 +195,8 @@ " radiation_probe='neutron',\n", " scattering_type='bragg',\n", ")\n", - "experiment.linked_crystal.id = 'tbti'\n", - "experiment.linked_crystal.scale = FULLPROF_SCALE\n", + "experiment.linked_structure.structure_id = 'tbti'\n", + "experiment.linked_structure.scale = FULLPROF_SCALE\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "experiment.extinction.type = 'becker-coppens'\n", "experiment.extinction.model = 'gauss'\n", @@ -213,7 +213,7 @@ "id": "12", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -231,7 +231,7 @@ " reference=reference,\n", " candidate=candidate,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -240,7 +240,7 @@ "id": "14", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -252,7 +252,7 @@ "source": [ "experiment.calculator.type = 'cryspy'\n", "\n", - "experiment.linked_crystal.scale.free = True\n", + "experiment.linked_structure.scale.free = True\n", "experiment.extinction.radius.free = True\n", "\n", "project.analysis.fit()\n", @@ -266,7 +266,7 @@ " reference=reference_refined,\n", " candidate=candidate_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy (scale + ext radius)',\n", + " candidate_label='edi-cryspy (scale + ext radius)',\n", ")\n", "\n", "verify.report_refinement_closeness(\n", diff --git a/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.py b/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.py index 68d359b5b..acab703f5 100644 --- a/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.py +++ b/docs/docs/verification/sc-neut-cwl_ext-iso_tbti.py @@ -2,7 +2,7 @@ # # Tb₂Ti₂O₇ — neutron single crystal, constant wavelength, isotropic extinction # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -30,7 +30,7 @@ # in the site multiplicity; CIF/EasyDiffraction use 1.0 for a fully # occupied site. structure.atom_sites.create( - label='Tb', # FullProf Atom + id='Tb', # FullProf Atom type_symbol='Tb', # FullProf Typ fract_x=0.5, # FullProf X fract_y=0.5, # FullProf Y @@ -42,7 +42,7 @@ aniso.adp_12 = -0.00047650724 # FullProf beta12 structure.atom_sites.create( - label='Ti', # FullProf Atom + id='Ti', # FullProf Atom type_symbol='Ti', # FullProf Typ fract_x=0, # FullProf X fract_y=0, # FullProf Y @@ -54,7 +54,7 @@ aniso.adp_12 = -0.00016990340 # FullProf beta12 structure.atom_sites.create( - label='O1', # FullProf Atom + id='O1', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.32804, # FullProf X fract_y=0.125, # FullProf Y @@ -67,7 +67,7 @@ aniso.adp_23 = 0.00041246481 # FullProf beta23 structure.atom_sites.create( - label='O2', # FullProf Atom + id='O2', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.375, # FullProf X fract_y=0.375, # FullProf Y @@ -80,7 +80,7 @@ project.structures.add(structure) # %% -structure.show_as_cif() +structure.show_as_text() # %% [markdown] # ## Load the FullProf reference @@ -108,8 +108,8 @@ radiation_probe='neutron', scattering_type='bragg', ) -experiment.linked_crystal.id = 'tbti' -experiment.linked_crystal.scale = FULLPROF_SCALE +experiment.linked_structure.structure_id = 'tbti' +experiment.linked_structure.scale = FULLPROF_SCALE experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH experiment.extinction.type = 'becker-coppens' experiment.extinction.model = 'gauss' @@ -121,7 +121,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% calc_ed_cryspy = verify.calculate_reflections(project, experiment, 'cryspy') @@ -132,16 +132,16 @@ reference=reference, candidate=candidate, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% experiment.calculator.type = 'cryspy' -experiment.linked_crystal.scale.free = True +experiment.linked_structure.scale.free = True experiment.extinction.radius.free = True project.analysis.fit() @@ -155,7 +155,7 @@ reference=reference_refined, candidate=candidate_refined, reference_label='FullProf', - candidate_label='ed-cryspy (scale + ext radius)', + candidate_label='edi-cryspy (scale + ext radius)', ) verify.report_refinement_closeness( diff --git a/docs/docs/verification/sc-neut-cwl_noext_tbti.ipynb b/docs/docs/verification/sc-neut-cwl_noext_tbti.ipynb index 6dcd17ac6..43269d09c 100644 --- a/docs/docs/verification/sc-neut-cwl_noext_tbti.ipynb +++ b/docs/docs/verification/sc-neut-cwl_noext_tbti.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -86,7 +86,7 @@ "# the site multiplicity over the general multiplicity; CIF/EasyDiffraction\n", "# use 1.0 for a fully occupied site.\n", "structure.atom_sites.create(\n", - " label='Tb', # FullProf Atom\n", + " id='Tb', # FullProf Atom\n", " type_symbol='Tb', # FullProf Typ\n", " fract_x=0.5, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -98,7 +98,7 @@ "aniso.adp_12 = -0.00047650724 # FullProf beta12\n", "\n", "structure.atom_sites.create(\n", - " label='Ti', # FullProf Atom\n", + " id='Ti', # FullProf Atom\n", " type_symbol='Ti', # FullProf Typ\n", " fract_x=0, # FullProf X\n", " fract_y=0, # FullProf Y\n", @@ -110,7 +110,7 @@ "aniso.adp_12 = -0.00016990340 # FullProf beta12\n", "\n", "structure.atom_sites.create(\n", - " label='O1', # FullProf Atom\n", + " id='O1', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.32804, # FullProf X\n", " fract_y=0.125, # FullProf Y\n", @@ -123,7 +123,7 @@ "aniso.adp_23 = 0.00041246481 # FullProf beta23\n", "\n", "structure.atom_sites.create(\n", - " label='O2', # FullProf Atom\n", + " id='O2', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.375, # FullProf X\n", " fract_y=0.375, # FullProf Y\n", @@ -143,7 +143,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -192,8 +192,8 @@ " scattering_type='bragg',\n", ")\n", "\n", - "experiment.linked_crystal.id = 'tbti'\n", - "experiment.linked_crystal.scale = FULLPROF_SCALE\n", + "experiment.linked_structure.structure_id = 'tbti'\n", + "experiment.linked_structure.scale = FULLPROF_SCALE\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "\n", "verify.set_reference_reflections(experiment, f2calc)\n", @@ -206,7 +206,7 @@ "id": "12", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -224,7 +224,7 @@ " reference=reference,\n", " candidate=candidate,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -233,7 +233,7 @@ "id": "14", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -245,7 +245,7 @@ "source": [ "experiment.calculator.type = 'cryspy'\n", "\n", - "experiment.linked_crystal.scale.free = True\n", + "experiment.linked_structure.scale.free = True\n", "\n", "project.analysis.fit()\n", "project.display.fit.results()\n", @@ -258,7 +258,7 @@ " reference=reference_refined,\n", " candidate=candidate_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy (scale only)',\n", + " candidate_label='edi-cryspy (scale only)',\n", ")\n", "\n", "verify.report_refinement_closeness(\n", diff --git a/docs/docs/verification/sc-neut-cwl_noext_tbti.py b/docs/docs/verification/sc-neut-cwl_noext_tbti.py index da2aecd97..6ede69a2d 100644 --- a/docs/docs/verification/sc-neut-cwl_noext_tbti.py +++ b/docs/docs/verification/sc-neut-cwl_noext_tbti.py @@ -2,7 +2,7 @@ # # Tb₂Ti₂O₇ — neutron single crystal, constant wavelength, no extinction # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -30,7 +30,7 @@ # the site multiplicity over the general multiplicity; CIF/EasyDiffraction # use 1.0 for a fully occupied site. structure.atom_sites.create( - label='Tb', # FullProf Atom + id='Tb', # FullProf Atom type_symbol='Tb', # FullProf Typ fract_x=0.5, # FullProf X fract_y=0.5, # FullProf Y @@ -42,7 +42,7 @@ aniso.adp_12 = -0.00047650724 # FullProf beta12 structure.atom_sites.create( - label='Ti', # FullProf Atom + id='Ti', # FullProf Atom type_symbol='Ti', # FullProf Typ fract_x=0, # FullProf X fract_y=0, # FullProf Y @@ -54,7 +54,7 @@ aniso.adp_12 = -0.00016990340 # FullProf beta12 structure.atom_sites.create( - label='O1', # FullProf Atom + id='O1', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.32804, # FullProf X fract_y=0.125, # FullProf Y @@ -67,7 +67,7 @@ aniso.adp_23 = 0.00041246481 # FullProf beta23 structure.atom_sites.create( - label='O2', # FullProf Atom + id='O2', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.375, # FullProf X fract_y=0.375, # FullProf Y @@ -80,7 +80,7 @@ project.structures.add(structure) # %% -structure.show_as_cif() +structure.show_as_text() # %% [markdown] # ## Load the FullProf reference @@ -105,8 +105,8 @@ scattering_type='bragg', ) -experiment.linked_crystal.id = 'tbti' -experiment.linked_crystal.scale = FULLPROF_SCALE +experiment.linked_structure.structure_id = 'tbti' +experiment.linked_structure.scale = FULLPROF_SCALE experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH verify.set_reference_reflections(experiment, f2calc) @@ -114,7 +114,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% calc_ed_cryspy = verify.calculate_reflections(project, experiment, 'cryspy') @@ -125,16 +125,16 @@ reference=reference, candidate=candidate, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% experiment.calculator.type = 'cryspy' -experiment.linked_crystal.scale.free = True +experiment.linked_structure.scale.free = True project.analysis.fit() project.display.fit.results() @@ -147,7 +147,7 @@ reference=reference_refined, candidate=candidate_refined, reference_label='FullProf', - candidate_label='ed-cryspy (scale only)', + candidate_label='edi-cryspy (scale only)', ) verify.report_refinement_closeness( diff --git a/docs/docs/verification/sc-neut-cwl_pr2nio4.ipynb b/docs/docs/verification/sc-neut-cwl_pr2nio4.ipynb index d92c5cd23..dd981cce9 100644 --- a/docs/docs/verification/sc-neut-cwl_pr2nio4.ipynb +++ b/docs/docs/verification/sc-neut-cwl_pr2nio4.ipynb @@ -34,7 +34,7 @@ "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed\n", + "import easydiffraction as edi\n", "from easydiffraction import ExperimentFactory\n", "from easydiffraction import StructureFactory\n", "from easydiffraction.analysis import verification as verify" @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "project = ed.Project()" + "project = edi.Project()" ] }, { @@ -89,7 +89,7 @@ "# occupancy here is the FullProf Occ scaled by the multiplicity (1.0 for\n", "# a full site).\n", "structure.atom_sites.create(\n", - " label='Pr', # FullProf Atom\n", + " id='Pr', # FullProf Atom\n", " type_symbol='Pr', # FullProf Typ\n", " fract_x=0.5, # FullProf X\n", " fract_y=0.5, # FullProf Y\n", @@ -102,7 +102,7 @@ "aniso.adp_33 = 0.00084 # FullProf beta33\n", "\n", "structure.atom_sites.create(\n", - " label='Ni', # FullProf Atom\n", + " id='Ni', # FullProf Atom\n", " type_symbol='Ni', # FullProf Typ\n", " fract_x=0, # FullProf X\n", " fract_y=0, # FullProf Y\n", @@ -115,7 +115,7 @@ "aniso.adp_33 = 0.00151 # FullProf beta33\n", "\n", "structure.atom_sites.create(\n", - " label='O1', # FullProf Atom\n", + " id='O1', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.25, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -129,7 +129,7 @@ "aniso.adp_12 = -0.00140 # FullProf beta12\n", "\n", "structure.atom_sites.create(\n", - " label='O2', # FullProf Atom\n", + " id='O2', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0, # FullProf X\n", " fract_y=0, # FullProf Y\n", @@ -143,7 +143,7 @@ "aniso.adp_33 = 0.00045 # FullProf beta33\n", "\n", "structure.atom_sites.create(\n", - " label='Oi', # FullProf Atom\n", + " id='Oi', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.25, # FullProf X\n", " fract_y=0.25, # FullProf Y\n", @@ -158,7 +158,7 @@ "\n", "# The split interstitial oxygen Od is refined with an isotropic B.\n", "structure.atom_sites.create(\n", - " label='Od', # FullProf Atom\n", + " id='Od', # FullProf Atom\n", " type_symbol='O', # FullProf Typ\n", " fract_x=0.07347, # FullProf X\n", " fract_y=0.07347, # FullProf Y\n", @@ -178,7 +178,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure.show_as_cif()" + "structure.show_as_text()" ] }, { @@ -227,8 +227,8 @@ " scattering_type='bragg',\n", ")\n", "\n", - "experiment.linked_crystal.id = 'pr2nio4'\n", - "experiment.linked_crystal.scale = FULLPROF_SCALE\n", + "experiment.linked_structure.structure_id = 'pr2nio4'\n", + "experiment.linked_structure.scale = FULLPROF_SCALE\n", "experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH\n", "\n", "verify.set_reference_reflections(experiment, f2calc)\n", @@ -241,7 +241,7 @@ "id": "12", "metadata": {}, "source": [ - "## ed-cryspy VS FullProf" + "## edi-cryspy VS FullProf" ] }, { @@ -259,7 +259,7 @@ " reference=reference,\n", " candidate=candidate,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy',\n", + " candidate_label='edi-cryspy',\n", ")" ] }, @@ -268,7 +268,7 @@ "id": "14", "metadata": {}, "source": [ - "## Fit ed-cryspy to FullProf" + "## Fit edi-cryspy to FullProf" ] }, { @@ -280,7 +280,7 @@ "source": [ "experiment.calculator.type = 'cryspy'\n", "\n", - "experiment.linked_crystal.scale.free = True\n", + "experiment.linked_structure.scale.free = True\n", "\n", "project.analysis.fit()\n", "project.display.fit.results()\n", @@ -293,7 +293,7 @@ " reference=reference_refined,\n", " candidate=candidate_refined,\n", " reference_label='FullProf',\n", - " candidate_label='ed-cryspy (scale only)',\n", + " candidate_label='edi-cryspy (scale only)',\n", ")\n", "\n", "verify.report_refinement_closeness(\n", diff --git a/docs/docs/verification/sc-neut-cwl_pr2nio4.py b/docs/docs/verification/sc-neut-cwl_pr2nio4.py index faa916e40..c4ec4ea29 100644 --- a/docs/docs/verification/sc-neut-cwl_pr2nio4.py +++ b/docs/docs/verification/sc-neut-cwl_pr2nio4.py @@ -2,7 +2,7 @@ # # Pr₂NiO₄ — neutron single crystal, constant wavelength # %% -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import StructureFactory from easydiffraction.analysis import verification as verify @@ -11,7 +11,7 @@ # ## Build the project # %% -project = ed.Project() +project = edi.Project() # %% [markdown] # ## Define the structure @@ -33,7 +33,7 @@ # occupancy here is the FullProf Occ scaled by the multiplicity (1.0 for # a full site). structure.atom_sites.create( - label='Pr', # FullProf Atom + id='Pr', # FullProf Atom type_symbol='Pr', # FullProf Typ fract_x=0.5, # FullProf X fract_y=0.5, # FullProf Y @@ -46,7 +46,7 @@ aniso.adp_33 = 0.00084 # FullProf beta33 structure.atom_sites.create( - label='Ni', # FullProf Atom + id='Ni', # FullProf Atom type_symbol='Ni', # FullProf Typ fract_x=0, # FullProf X fract_y=0, # FullProf Y @@ -59,7 +59,7 @@ aniso.adp_33 = 0.00151 # FullProf beta33 structure.atom_sites.create( - label='O1', # FullProf Atom + id='O1', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.25, # FullProf X fract_y=0.25, # FullProf Y @@ -73,7 +73,7 @@ aniso.adp_12 = -0.00140 # FullProf beta12 structure.atom_sites.create( - label='O2', # FullProf Atom + id='O2', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0, # FullProf X fract_y=0, # FullProf Y @@ -87,7 +87,7 @@ aniso.adp_33 = 0.00045 # FullProf beta33 structure.atom_sites.create( - label='Oi', # FullProf Atom + id='Oi', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.25, # FullProf X fract_y=0.25, # FullProf Y @@ -102,7 +102,7 @@ # The split interstitial oxygen Od is refined with an isotropic B. structure.atom_sites.create( - label='Od', # FullProf Atom + id='Od', # FullProf Atom type_symbol='O', # FullProf Typ fract_x=0.07347, # FullProf X fract_y=0.07347, # FullProf Y @@ -115,7 +115,7 @@ project.structures.add(structure) # %% -structure.show_as_cif() +structure.show_as_text() # %% [markdown] # ## Load the FullProf reference @@ -140,8 +140,8 @@ scattering_type='bragg', ) -experiment.linked_crystal.id = 'pr2nio4' -experiment.linked_crystal.scale = FULLPROF_SCALE +experiment.linked_structure.structure_id = 'pr2nio4' +experiment.linked_structure.scale = FULLPROF_SCALE experiment.instrument.setup_wavelength = FULLPROF_WAVELENGTH verify.set_reference_reflections(experiment, f2calc) @@ -149,7 +149,7 @@ project.experiments.add(experiment) # %% [markdown] -# ## ed-cryspy VS FullProf +# ## edi-cryspy VS FullProf # %% calc_ed_cryspy = verify.calculate_reflections(project, experiment, 'cryspy') @@ -160,16 +160,16 @@ reference=reference, candidate=candidate, reference_label='FullProf', - candidate_label='ed-cryspy', + candidate_label='edi-cryspy', ) # %% [markdown] -# ## Fit ed-cryspy to FullProf +# ## Fit edi-cryspy to FullProf # %% experiment.calculator.type = 'cryspy' -experiment.linked_crystal.scale.free = True +experiment.linked_structure.scale.free = True project.analysis.fit() project.display.fit.results() @@ -182,7 +182,7 @@ reference=reference_refined, candidate=candidate_refined, reference_label='FullProf', - candidate_label='ed-cryspy (scale only)', + candidate_label='edi-cryspy (scale only)', ) verify.report_refinement_closeness( diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index f95e4458c..8bfbdeaf4 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -216,43 +216,43 @@ nav: - Tutorials: - Tutorials: tutorials/index.md - Getting Started: - - LBCO quick code: tutorials/ed-2.ipynb - - LBCO basic load: tutorials/ed-1.ipynb - - LBCO complete: tutorials/ed-3.ipynb + - LBCO quick code: tutorials/refine-lbco-hrpt-from-data.ipynb + - LBCO basic load: tutorials/refine-lbco-hrpt-from-cif.ipynb + - LBCO complete: tutorials/refine-lbco-hrpt-report.ipynb - Load Project: - - LBCO Single: tutorials/ed-18.ipynb - - Co2SiO4 Sequential: tutorials/ed-23.ipynb + - LBCO Single: tutorials/load-and-fit-lbco-hrpt.ipynb + - Co2SiO4 Sequential: tutorials/refine-cosio-d20-tscan-resumed.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 + - Co2SiO4 pd-neut-cwl: tutorials/refine-cosio-d20.ipynb + - HS pd-neut-cwl: tutorials/refine-hs-hrpt.ipynb + - Si pd-neut-tof: tutorials/refine-si-sepd.ipynb - Without Measured Data: - - LBCO pd-neut-cwl: tutorials/ed-27.ipynb - - Si pd-neut-tof: tutorials/ed-28.ipynb - - NaCl pd-xray: tutorials/ed-29.ipynb + - LBCO pd-neut-cwl: tutorials/simulate-lbco-cwl.ipynb + - Si pd-neut-tof: tutorials/simulate-si-tof.ipynb + - NaCl pd-xray: tutorials/simulate-nacl-xray.ipynb - Single Crystal Diffraction: - - Tb2TiO7 sc-neut-cwl: tutorials/ed-14.ipynb - - Taurine sc-neut-tof: tutorials/ed-15.ipynb + - Tb2TiO7 sc-neut-cwl: tutorials/refine-tbti-heidi.ipynb + - Taurine sc-neut-tof: tutorials/refine-taurine-senju.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 + - Ni pd-neut-cwl: tutorials/pdf-ni-npd.ipynb + - Si pd-neut-tof: tutorials/pdf-si-nomad.ipynb + - NaCl pd-xray: tutorials/pdf-nacl-xrd.ipynb - Multiple Data Blocks: - - PbSO4 NPD+XRD: tutorials/ed-4.ipynb - - Si Bragg+PDF: tutorials/ed-16.ipynb - - Co2SiO4 T-scan: tutorials/ed-17.ipynb + - PbSO4 NPD+XRD: tutorials/refine-pbso4-joint.ipynb + - NCAF pd-neut-tof: tutorials/refine-ncaf-wish.ipynb + - Si Bragg+PDF: tutorials/joint-si-bragg-pdf.ipynb + - Co2SiO4 T-scan: tutorials/refine-cosio-d20-tscan.ipynb - Simulated Data: - - LBCO+Si McStas: tutorials/ed-9.ipynb - - BEER McStas: tutorials/ed-20.ipynb + - LBCO+Si McStas: tutorials/refine-lbco-si-mcstas.ipynb + - BEER McStas: tutorials/calibrate-beer-ess.ipynb - Bayesian Analysis: - - LBCO pd bumps-dream: tutorials/ed-21.ipynb - - LBCO pd bumps-dream Display: tutorials/ed-24.ipynb - - LBCO pd emcee: tutorials/ed-25.ipynb - - LBCO pd emcee Resume: tutorials/ed-26.ipynb - - Tb2TiO7 sg bumps-dream: tutorials/ed-22.ipynb + - LBCO pd bumps-dream: tutorials/bayesian-dream-lbco-hrpt.ipynb + - LBCO pd bumps-dream Display: tutorials/bayesian-dream-display-lbco-hrpt.ipynb + - LBCO pd emcee: tutorials/bayesian-emcee-lbco-hrpt.ipynb + - LBCO pd emcee Resume: tutorials/bayesian-emcee-resume-lbco-hrpt.ipynb + - Tb2TiO7 sg bumps-dream: tutorials/bayesian-emcee-tbti-heidi.ipynb - Workshops & Schools: - - DMSC Summer School: tutorials/ed-13.ipynb + - DMSC Summer School: tutorials/fitting-exercise-si-lbco.ipynb - Verification: - Verification: verification/index.md - Powder, neutron, constant wavelength: diff --git a/lychee.toml b/lychee.toml index db6de881d..f131cff3c 100644 --- a/lychee.toml +++ b/lychee.toml @@ -2,7 +2,7 @@ # # Fast every-push docs gate: verify local/relative links only. External # URL checking is deferred to avoid flakiness from rate-limited sites -# (see docs/dev/issues/open.md issue 114). +# (see docs/dev/issues/index.md (issue 114)). # Skip online links entirely — deterministic and fast, no network. offline = true diff --git a/pixi.toml b/pixi.toml index 6241ac7b1..18becaef3 100644 --- a/pixi.toml +++ b/pixi.toml @@ -113,9 +113,10 @@ nightly-tests = 'python -m pytest tests/ -m nightly --ignore=tests/benchmarks -- # the nightly CI artifact. Informational (no regression gate yet). benchmarks = 'python -m pytest tests/benchmarks/ --benchmark-only --benchmark-json=benchmark.json --color=yes -v' -# Remove previously saved tutorial output projects (projects/ed_*) so the -# tutorial-output checks cannot pass against a stale artifact from an earlier -# run. Downloaded input projects (projects/ed-NN) are preserved. +# Remove previously saved tutorial output projects (the slug-named +# projects/* directories) so the tutorial-output checks cannot pass against a +# stale artifact from an earlier run. Downloaded project archives +# (projects/proj-*) are preserved so they need not be re-downloaded. clean-tutorial-projects = { cmd = 'python tools/clean_tutorial_projects.py', env = { EASYDIFFRACTION_ARTIFACT_ROOT = 'tmp/tutorials' } } script-tests = { cmd = 'python -m pytest tools/test_scripts.py --color=yes -n auto -v', depends-on = [ 'clean-tutorial-projects', @@ -124,7 +125,7 @@ notebook-tests = { cmd = 'python -m pytest --nbmake docs/docs/tutorials/ docs/do 'clean-tutorial-projects', ], env = { EASYDIFFRACTION_ARTIFACT_ROOT = 'tmp/tutorials' } } -# Parse the analysis.cif of every saved tutorial project and assert its +# Parse the analysis.edi of every saved tutorial project and assert its # refined fit results against tests/tutorials/baseline.json. Run only after # the tutorials have been executed (script-tests or notebook-tests), so the # saved projects exist under tmp/tutorials/projects/. diff --git a/src/easydiffraction/__main__.py b/src/easydiffraction/__main__.py index b7d39c87d..f740e7431 100644 --- a/src/easydiffraction/__main__.py +++ b/src/easydiffraction/__main__.py @@ -13,7 +13,7 @@ import typer -import easydiffraction as ed +import easydiffraction as edi app = typer.Typer(add_completion=False) @@ -46,7 +46,7 @@ def _normalized_cli_args(args: list[str]) -> list[str]: def _load_project(project_dir: str) -> object: """Load one saved project directory.""" - return ed.Project.load(project_dir) + return edi.Project.load(project_dir) def _display_project_patterns(project: object) -> None: @@ -156,7 +156,7 @@ def main( ) -> None: """EasyDiffraction command-line interface.""" if version: - ed.show_version() + edi.show_version() raise typer.Exit(code=0) # If no subcommand and no option provided, show help and exit 0. if ctx.invoked_subcommand is None: @@ -168,18 +168,21 @@ def main( @app.command('list-data') def list_data() -> None: """List available example data and project archives.""" - ed.list_data() + edi.list_data() @app.command('list-tutorials') def list_tutorials() -> None: """List available tutorial notebooks.""" - ed.list_tutorials() + edi.list_tutorials() @app.command('download-data') def download_data( - id: int = typer.Argument(..., help='Data ID to download.'), + name: str = typer.Argument( + ..., + help="Dataset name (e.g. 'meas-lbco-hrpt') or a list-data row number.", + ), destination: str = typer.Option( 'data', '--destination', @@ -193,19 +196,44 @@ def download_data( help='Overwrite an existing file or extracted project if present.', ), ) -> None: - """Download one example data record by ID.""" - ed.download_data(id=id, destination=destination, overwrite=overwrite) + """Download one dataset by its name.""" + edi.download_data(name, destination=destination, overwrite=overwrite) + + +def _selected_tutorial_formats(*, ipynb: bool, py: bool) -> list[str]: + """ + Return the formats to download; default to notebook if none set. + """ + formats = [] + if ipynb: + formats.append('ipynb') + if py: + formats.append('py') + return formats or ['ipynb'] @app.command('download-tutorial') def download_tutorial( - id: int = typer.Argument(..., help='Tutorial ID to download.'), + name: str = typer.Argument( + ..., + help="Tutorial name (e.g. 'refine-lbco-hrpt-from-cif') or a list-tutorials row number.", + ), destination: str = typer.Option( 'tutorials', '--destination', '-d', help='Directory to save the tutorial into.', ), + ipynb: bool = typer.Option( # noqa: FBT001 + False, # noqa: FBT003 + '--ipynb', + help='Download the Jupyter notebook (.ipynb). Default when no format flag is given.', + ), + py: bool = typer.Option( # noqa: FBT001 + False, # noqa: FBT003 + '--py', + help='Download the plain-Python script (.py). Combine with --ipynb to get both.', + ), overwrite: bool = typer.Option( # noqa: FBT001 False, # noqa: FBT003 '--overwrite', @@ -213,8 +241,14 @@ def download_tutorial( help='Overwrite existing file if present.', ), ) -> None: - """Download a specific tutorial notebook by ID.""" - ed.download_tutorial(id=id, destination=destination, overwrite=overwrite) + """Download a tutorial by its name as a notebook and/or script.""" + for file_format in _selected_tutorial_formats(ipynb=ipynb, py=py): + edi.download_tutorial( + name, + destination=destination, + file_format=file_format, + overwrite=overwrite, + ) @app.command('download-all-tutorials') @@ -233,14 +267,14 @@ def download_all_tutorials( ), ) -> None: """Download all available tutorial notebooks.""" - ed.download_all_tutorials(destination=destination, overwrite=overwrite) + edi.download_all_tutorials(destination=destination, overwrite=overwrite) @app.command('display') def display( project_dir: str = typer.Argument( ..., - help='Path to the project directory (must contain project.cif).', + help='Path to the project directory (must contain project.edi).', ), ) -> None: """Display the typical outputs for a saved project state.""" @@ -252,7 +286,7 @@ def display( def fit( project_dir: str = typer.Argument( ..., - help='Path to the project directory (must contain project.cif).', + help='Path to the project directory (must contain project.edi).', ), dry: bool = typer.Option( # noqa: FBT001 False, # noqa: FBT003 @@ -263,7 +297,7 @@ def fit( """Fit a saved project: easydiffraction PROJECT_DIR fit [--dry].""" project = _load_project(project_dir) if dry: - project.info._path = None + project.metadata._path = None project.analysis.fit() _display_fit_outputs(project) @@ -272,7 +306,7 @@ def fit( def undo( project_dir: str = typer.Argument( ..., - help='Path to the project directory (must contain project.cif).', + help='Path to the project directory (must contain project.edi).', ), dry: bool = typer.Option( # noqa: FBT001 False, # noqa: FBT003 diff --git a/src/easydiffraction/_data_index_ref.txt b/src/easydiffraction/_data_index_ref.txt new file mode 100644 index 000000000..8452615e6 --- /dev/null +++ b/src/easydiffraction/_data_index_ref.txt @@ -0,0 +1 @@ +11bb1e4a50b36bc8e710023915244fb0c836ba3b diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 7b3dfc4ff..3d303a5f4 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -37,6 +37,7 @@ from easydiffraction.analysis.enums import FitCorrelationSourceEnum from easydiffraction.analysis.enums import FitModeEnum from easydiffraction.analysis.enums import FitResultKindEnum +from easydiffraction.analysis.enums import SoftwareRoleEnum from easydiffraction.analysis.fit_helpers.bayesian import ESS_BULK_CONVERGENCE_THRESHOLD from easydiffraction.analysis.fit_helpers.bayesian import R_HAT_CONVERGENCE_THRESHOLD from easydiffraction.analysis.fit_helpers.bayesian import BayesianFitResults @@ -56,6 +57,7 @@ from easydiffraction.core.variable import Parameter from easydiffraction.datablocks.experiment.item.base import intensity_category_for from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum +from easydiffraction.display.links import parameter_docs_link from easydiffraction.display.progress import make_display_handle from easydiffraction.display.progress import notebook_fit_stop_control from easydiffraction.display.tables import TableRenderer @@ -72,6 +74,8 @@ from easydiffraction.utils.utils import render_table if TYPE_CHECKING: + from collections.abc import Callable + from easydiffraction.analysis.categories.fit_result import FitResultBase from easydiffraction.analysis.categories.minimizer.base import MinimizerCategoryBase from easydiffraction.core.posterior import PosteriorParameterSummary @@ -81,8 +85,7 @@ # data and derived, read-only tables that would only add noise. The # space_group_Wyckoff table also carries unreadably long coords_xyz. _SUMMARY_HIDDEN_PARAMETER_CATEGORIES = frozenset({ - 'pd_data', - 'total_data', + 'data', 'refln', 'space_group_Wyckoff', }) @@ -347,6 +350,7 @@ def how_to_access_parameters(self) -> None: category_code = param._identity.category_code category_entry_name = param._identity.category_entry_name or '' param_key = param.name + param_label = parameter_docs_link(param) code_variable = ( f"{project_varname}.{datablock_code}['{datablock_entry_name}'].{category_code}" ) @@ -357,7 +361,7 @@ def how_to_access_parameters(self) -> None: datablock_entry_name, category_code, category_entry_name, - param_key, + param_label, code_variable, ]) @@ -368,12 +372,24 @@ def how_to_access_parameters(self) -> None: columns_data=columns_data, ) - def parameter_cif_uids(self) -> None: + def _show_parameter_names( + self, + *, + column_header: str, + paragraph_title: str, + value_fn: Callable[[object], object], + ) -> None: """ - Show CIF unique IDs for all parameters. + Render one name column for every summary parameter. - The output explains which unique identifiers are used when - creating CIF-based constraints. + Parameters + ---------- + column_header : str + Header for the per-parameter name column. + paragraph_title : str + Console paragraph title shown above the table. + value_fn : Callable[[object], object] + Returns the value to show for a parameter. """ all_params = self._summary_parameters_by_datablock() @@ -386,40 +402,53 @@ def parameter_cif_uids(self) -> None: 'category', 'entry', 'parameter', - 'Unique Identifier for CIF Constraints', + column_header, ] - - columns_alignment = [ - 'left', - 'left', - 'left', - 'left', - 'left', + columns_alignment = ['left', 'left', 'left', 'left', 'left'] + + columns_data = [ + [ + param._identity.datablock_entry_name, + param._identity.category_code, + param._identity.category_entry_name or '', + parameter_docs_link(param), + value_fn(param), + ] + for params in all_params.values() + for param in params ] - columns_data = [] - for params in all_params.values(): - for param in params: - datablock_entry_name = param._identity.datablock_entry_name - category_code = param._identity.category_code - category_entry_name = param._identity.category_entry_name or '' - param_key = param.name - cif_uid = param._cif_handler.uid - columns_data.append([ - datablock_entry_name, - category_code, - category_entry_name, - param_key, - cif_uid, - ]) - - console.paragraph('Show parameter CIF unique identifiers') + console.paragraph(paragraph_title) render_table( columns_headers=columns_headers, columns_alignment=columns_alignment, columns_data=columns_data, ) + def parameter_uids(self) -> None: + """Show the constraint unique identifier per parameter.""" + self._show_parameter_names( + column_header='Unique Identifier for Constraints', + paragraph_title='Show parameter unique identifiers for constraints', + value_fn=lambda param: param._tags.uid, + ) + + def parameter_edi_tags(self) -> None: + """Show the Edi persistence tag for every parameter.""" + self._show_parameter_names( + column_header='Edi Tag', + paragraph_title='Show parameter Edi tags', + value_fn=lambda param: param._tags.edi_name, + ) + + def parameter_cif_tags(self) -> None: + """Show the report CIF tag for every parameter.""" + self._show_parameter_names( + column_header='CIF Tag', + paragraph_title='Show parameter CIF tags', + value_fn=lambda param: param._tags.cif_name, + ) + def constraints(self) -> None: """Print a table of all user-defined symbolic constraints.""" self._analysis.constraints.show() @@ -447,8 +476,8 @@ def fit_results(self) -> None: analysis.fitter._process_fit_results(structures, experiments) def as_cif(self) -> None: - """Render the analysis section as CIF in console.""" - self._analysis.show_as_cif() + """Render the analysis section as text in console.""" + self._analysis.show_as_text() class _AnalysisOwnerAccessorsMixin: @@ -663,7 +692,7 @@ def _set_software_role( def _stamp_software_provenance(self) -> None: """Record software identities for the latest successful fit.""" self._set_software_role( - self.software.framework, + self.software[SoftwareRoleEnum.FRAMEWORK.value], ( 'EasyDiffraction', package_version('easydiffraction'), @@ -671,14 +700,14 @@ def _stamp_software_provenance(self) -> None: ), ) self._set_software_role( - self.software.calculator, + self.software[SoftwareRoleEnum.CALCULATOR.value], self._calculator_software_values(), ) self._set_software_role( - self.software.minimizer, + self.software[SoftwareRoleEnum.MINIMIZER.value], self._software_values(self.minimizer), ) - self.software.timestamp = datetime.now(tz=UTC).isoformat(timespec='seconds') + self.project.metadata.timestamp = datetime.now(tz=UTC).isoformat(timespec='seconds') def _swap_minimizer(self, new_type: str) -> None: """Switch the active minimizer category.""" @@ -701,7 +730,7 @@ def _ordered_restored_parameter_names(self) -> list[str]: """ Return persisted parameter names in display and array order. """ - return [row.param_unique_name.value for row in self.fit_parameters] + return [row.parameter_unique_name.value for row in self.fit_parameters] def _restore_live_parameter_bounds_and_anchors( self, @@ -709,26 +738,24 @@ def _restore_live_parameter_bounds_and_anchors( ) -> None: """Restore saved fit controls onto live parameter objects.""" for row in self.fit_parameters: - parameter = param_map.get(row.param_unique_name.value) + parameter = param_map.get(row.parameter_unique_name.value) if parameter is None: log.warning( 'Persisted fit-state references unknown parameter ' - f'{row.param_unique_name.value!r}.' + f'{row.parameter_unique_name.value!r}.' ) continue parameter.fit_min = row.fit_min.value parameter.fit_max = row.fit_max.value - parameter._set_fit_bounds_uncertainty_multiplier( - row.fit_bounds_uncertainty_multiplier.value - ) + parameter._set_bounds_uncertainty_multiplier(row.bounds_uncertainty_multiplier.value) parameter._fit_start_value = row.start_value.value parameter._fit_start_uncertainty = row.start_uncertainty.value def _restore_live_parameter_posterior(self, param_map: dict[str, Parameter]) -> None: """Restore saved posterior summaries onto live parameters.""" for row in self.fit_parameters: - parameter = param_map.get(row.param_unique_name.value) + parameter = param_map.get(row.parameter_unique_name.value) if parameter is None: continue @@ -759,7 +786,7 @@ def _restored_posterior_samples(self) -> PosteriorSamples | None: return None posterior_rows = [row for row in self.fit_parameters if row.has_posterior_summary()] - parameter_names = [row.param_unique_name.value for row in posterior_rows] + parameter_names = [row.parameter_unique_name.value for row in posterior_rows] parameter_sample_array = np.asarray(parameter_samples, dtype=float) if parameter_sample_array.ndim != _POSTERIOR_SAMPLE_NDIM: @@ -787,8 +814,8 @@ def _restored_posterior_summaries(self) -> list[PosteriorParameterSummary]: param_map = self._live_parameter_map() summaries: list[PosteriorParameterSummary] = [] for row in self.fit_parameters: - parameter = param_map.get(row.param_unique_name.value) - display_name = row.param_unique_name.value if parameter is None else parameter.name + parameter = param_map.get(row.parameter_unique_name.value) + display_name = row.parameter_unique_name.value if parameter is None else parameter.name summary = row.posterior_summary(display_name=display_name) if summary is not None: summaries.append(summary) @@ -1204,7 +1231,7 @@ def _serializable_categories(self) -> list: def _has_software_provenance(self) -> bool: """Return True when software provenance has been stamped.""" - return any(parameter.value is not None for parameter in self.software.parameters) + return self.software.has_provenance() # ------------------------------------------------------------------ # Parameter helpers @@ -1236,7 +1263,7 @@ def _get_params_as_dataframe( ('datablock', 'left'): param._identity.datablock_entry_name, ('category', 'left'): param._identity.category_code, ('entry', 'left'): param._identity.category_entry_name or '', - ('parameter', 'left'): param.name, + ('parameter', 'left'): parameter_docs_link(param), ('value', 'right'): '' if param.value is None else param.value, } if isinstance(param, GenericNumericDescriptor): @@ -1355,7 +1382,7 @@ def _is_parameter_at_undo_start( param_map: dict[str, Parameter], ) -> bool: """Return whether one live parameter is already at start.""" - parameter = param_map.get(row.param_unique_name.value) + parameter = param_map.get(row.parameter_unique_name.value) if parameter is None: return True return isclose( @@ -1371,11 +1398,11 @@ def _undo_scalar_rollback(self) -> tuple[str, ...]: param_map = self._live_parameter_map() logged_missing_uncertainty = False for row in self._undo_start_rows(): - parameter = param_map.get(row.param_unique_name.value) + parameter = param_map.get(row.parameter_unique_name.value) if parameter is None: log.warning( 'Persisted fit-state references unknown parameter ' - f'{row.param_unique_name.value!r}.' + f'{row.parameter_unique_name.value!r}.' ) continue @@ -1391,7 +1418,7 @@ def _undo_scalar_rollback(self) -> tuple[str, ...]: else: parameter.uncertainty = row.start_uncertainty.value parameter._set_posterior(None) - restored_names.append(row.param_unique_name.value) + restored_names.append(row.parameter_unique_name.value) return tuple(restored_names) def _undo_clear_per_row_posterior_fields(self) -> None: @@ -1481,7 +1508,7 @@ def _validate_fit_request( if resume and not is_emcee: msg = "Resume is supported only when analysis.minimizer.type = 'emcee'." raise ValueError(msg) - if is_emcee and self.project.info.path is None: + if is_emcee and self.project.metadata.path is None: msg = ( 'emcee requires a saved project; call project.save_as(<path>) ' 'before analysis.fit().' @@ -1515,7 +1542,7 @@ def _resolved_resume_extra_steps(self, extra_steps: int | None) -> int: def _has_resumable_emcee_sidecar(self) -> bool: """Return whether the saved project has a resumable chain.""" - project_path = self.project.info.path + project_path = self.project.metadata.path if project_path is None: return False @@ -1534,7 +1561,7 @@ def _has_resumable_emcee_sidecar(self) -> bool: def _prepare_results_sidecar_for_new_fit(self) -> None: """Remove persisted sidecar arrays before a fresh fit.""" - project_path = self.project.info.path + project_path = self.project.metadata.path if project_path is None: return @@ -1834,10 +1861,10 @@ def _capture_fit_parameter_state(self, parameters: list[Parameter]) -> None: for param in parameters: self.fit_parameters.create( - param_unique_name=param.unique_name, + parameter_unique_name=param.unique_name, fit_min=param.fit_min, fit_max=param.fit_max, - fit_bounds_uncertainty_multiplier=param.fit_bounds_uncertainty_multiplier, + bounds_uncertainty_multiplier=param.bounds_uncertainty_multiplier, start_value=param.value, start_uncertainty=param.uncertainty, ) @@ -1966,7 +1993,7 @@ def _gt_observation_mask( def _is_powder_fit(experiments: list[object]) -> bool: """Return whether any experiment in the fit is powder data.""" return any( - experiment.type.sample_form.value == SampleFormEnum.POWDER.value + experiment.experiment_type.sample_form.value == SampleFormEnum.POWDER.value for experiment in experiments ) @@ -2122,8 +2149,8 @@ def _store_correlation_projection( continue self.fit_parameter_correlations.create( source_kind=source_kind.value, - param_unique_name_i=unique_name_i, - param_unique_name_j=unique_names[column_index], + parameter_unique_name_i=unique_name_i, + parameter_unique_name_j=unique_names[column_index], correlation=float(np.clip(correlation, -1.0, 1.0)), ) @@ -2324,8 +2351,8 @@ def _store_one_posterior_pair_cache_projection( density_array = np.asarray(density_surface[2], dtype=float) contour_levels = self._posterior_pair_contour_levels(density_array) return pair_id, { - 'param_unique_name_x': x_name, - 'param_unique_name_y': y_name, + 'parameter_unique_name_x': x_name, + 'parameter_unique_name_y': y_name, 'x': x_grid_array, 'y': y_grid_array, 'density': density_array, @@ -2406,7 +2433,10 @@ def _store_posterior_predictive_projection( predictive_payload: dict[str, dict[str, object]] = {} for experiment_name in self.project.experiments.names: experiment = self.project.experiments[experiment_name] - x_axis, x_axis_name, _, _, _ = plotter._resolve_x_axis(experiment.type, None) + x_axis, x_axis_name, _, _, _ = plotter._resolve_x_axis( + experiment.experiment_type, + None, + ) summary = plotter._build_posterior_predictive_summary( fit_results=results, experiment=experiment, @@ -2597,7 +2627,7 @@ def _resolve_sequential_data_dir(self) -> Path: if data_dir.is_absolute(): return data_dir - project_path = self.project.info.path + project_path = self.project.metadata.path if project_path is None: msg = ( 'Project must be saved before resolving a relative ' @@ -2657,7 +2687,7 @@ def _run_single( ) self._stamp_software_provenance() - if self.project.info.path is not None: + if self.project.metadata.path is not None: self.project.save() def _run_joint( @@ -2684,7 +2714,7 @@ def _run_joint( ) self._stamp_software_provenance() - if self.project.info.path is not None: + if self.project.metadata.path is not None: self.project.save() def _run_sequential(self) -> None: @@ -2723,7 +2753,7 @@ def _run_sequential(self) -> None: self._stamp_software_provenance() - if self.project.info.path is not None: + if self.project.metadata.path is not None: self.project.save() def _fit_joint( @@ -3015,7 +3045,7 @@ def as_cif(self) -> str: self._update_categories() return analysis_to_cif(self) - def show_as_cif(self) -> None: - """Pretty-print the analysis section as CIF text.""" - console.paragraph('Analysis info as CIF') + def show_as_text(self) -> None: + """Pretty-print the analysis section as text.""" + console.paragraph('Analysis info as text') render_cif(self.as_cif) diff --git a/src/easydiffraction/analysis/calculators/base.py b/src/easydiffraction/analysis/calculators/base.py index e9c2c6bb8..c3bf24a91 100644 --- a/src/easydiffraction/analysis/calculators/base.py +++ b/src/easydiffraction/analysis/calculators/base.py @@ -21,7 +21,7 @@ class PowderReflnRecord: """Calculated powder reflection metadata for one reflection row.""" - phase_id: str + structure_id: str d_spacing: float sin_theta_over_lambda: float index_h: int @@ -88,13 +88,13 @@ def last_powder_refln_records( structure: Structure, experiment: ExperimentBase, *, - phase_id: str, + structure_id: str, ) -> list[PowderReflnRecord] | None: """ - Return the last powder reflection records for one phase. + Return the last powder reflection records for one structure. Backends that do not expose powder reflection metadata return ``None`` so callers can clear stale reflection rows and warn. """ - del self, structure, experiment, phase_id + del self, structure, experiment, structure_id return None diff --git a/src/easydiffraction/analysis/calculators/crysfml.py b/src/easydiffraction/analysis/calculators/crysfml.py index 863616dc8..41aafc52f 100644 --- a/src/easydiffraction/analysis/calculators/crysfml.py +++ b/src/easydiffraction/analysis/calculators/crysfml.py @@ -44,7 +44,7 @@ # corrections are intentionally left unmapped here. ('calib_d_to_tof_offset', '_pd_meas_tof_offset'), ('calib_d_to_tof_linear', '_pd_meas_tof_dtt1'), - ('calib_d_to_tof_quad', '_pd_meas_tof_dtt2'), + ('calib_d_to_tof_quadratic', '_pd_meas_tof_dtt2'), ('setup_twotheta_bank', '_pd_meas_tof_bank_angle'), ) @@ -66,10 +66,10 @@ ('broad_lorentz_gamma_0', '_pd_jorg_vondreele_gamma0'), ('broad_lorentz_gamma_1', '_pd_jorg_vondreele_gamma1'), ('broad_lorentz_gamma_2', '_pd_jorg_vondreele_gamma2'), - ('exp_decay_beta_0', '_pd_jorg_vondreele_beta0'), - ('exp_decay_beta_1', '_pd_jorg_vondreele_beta1'), - ('exp_rise_alpha_0', '_pd_jorg_vondreele_alpha0'), - ('exp_rise_alpha_1', '_pd_jorg_vondreele_alpha1'), + ('decay_beta_0', '_pd_jorg_vondreele_beta0'), + ('decay_beta_1', '_pd_jorg_vondreele_beta1'), + ('rise_alpha_0', '_pd_jorg_vondreele_alpha0'), + ('rise_alpha_1', '_pd_jorg_vondreele_alpha1'), ) @@ -186,13 +186,16 @@ def _calculate_raw_pattern( experiment: ExperimentBase, ) -> list[float] | None: """Calculate a Crysfml pattern without length adjustment.""" - if experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH: + if experiment.experiment_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH: _, y = cfml_py_utilities.cw_powder_pattern_from_dict(crysfml_dict) return y - if experiment.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT: + if experiment.experiment_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT: _, y = cfml_py_utilities.tof_powder_pattern_from_dict(crysfml_dict) return y - log.warning(f'[CrysfmlCalculator] Unsupported beam mode {experiment.type.beam_mode.value}') + log.warning( + f'[CrysfmlCalculator] Unsupported beam mode ' + f'{experiment.experiment_type.beam_mode.value}' + ) return None def _adjust_pattern_length( # noqa: PLR6301 @@ -287,7 +290,7 @@ def _convert_structure_to_dict( # noqa: PLR6301 for atom in structure.atom_sites: atom_site = { - '_label': atom.label.value, + '_label': atom.id.value, '_type_symbol': _element_symbol(atom.type_symbol.value), '_fract_x': atom.fract_x.value, '_fract_y': atom.fract_y.value, @@ -318,7 +321,7 @@ def _convert_experiment_to_dict( A dictionary representation of the experiment. """ experiment_dict = { - '_diffrn_radiation_probe': experiment.type.radiation_probe.value, + '_diffrn_radiation_probe': experiment.experiment_type.radiation_probe.value, } self._update_experiment_dict_from_instrument(experiment_dict, experiment) self._update_experiment_dict_from_peak(experiment_dict, experiment) @@ -342,8 +345,9 @@ def _update_experiment_dict_from_instrument( experiment_dict, _INSTRUMENT_ATTRIBUTE_MAP, ) - # if hasattr(experiment.instrument, 'calib_d_to_tof_recip'): - # ??? = experiment.instrument.calib_d_to_tof_recip.value + # if hasattr(experiment.instrument, + # 'calib_d_to_tof_reciprocal'): + # ??? = experiment.instrument.calib_d_to_tof_reciprocal.value def _update_experiment_dict_from_peak( self, diff --git a/src/easydiffraction/analysis/calculators/cryspy.py b/src/easydiffraction/analysis/calculators/cryspy.py index 9e92de1d9..cc912764d 100644 --- a/src/easydiffraction/analysis/calculators/cryspy.py +++ b/src/easydiffraction/analysis/calculators/cryspy.py @@ -95,7 +95,7 @@ def _invalidate_stale_cache( self._cached_peak_types[combined_name] = current_type # Preferred-orientation row set/identity. Adding or removing a - # row, or changing a row's phase_id or Miller direction, + # row, or changing a row's structure_id or Miller direction, # changes the emitted texture loop's shape and must rebuild the # dict. The refinable r/fraction values are patched in place, so # they are excluded. Constant-wavelength only, matching the @@ -103,12 +103,12 @@ def _invalidate_stale_cache( # tracked here. supports_texture = ( 'preferred_orientation' in type(experiment)._public_attrs() - and experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH + and experiment.experiment_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH ) if supports_texture: current_pref_orient = tuple( ( - item.phase_id.value, + item.structure_id.value, item.index_h.value, item.index_k.value, item.index_l.value, @@ -282,11 +282,14 @@ def calculate_pattern( BeamModeEnum.CONSTANT_WAVELENGTH: 'pd', BeamModeEnum.TIME_OF_FLIGHT: 'tof', } - beam_mode = experiment.type.beam_mode.value + beam_mode = experiment.experiment_type.beam_mode.value if beam_mode in prefixes: cryspy_block_name = f'{prefixes[beam_mode]}_{experiment.name}' else: - log.warning(f'[CryspyCalculator] Unknown beam mode {experiment.type.beam_mode.value}') + log.warning( + f'[CryspyCalculator] Unknown beam mode ' + f'{experiment.experiment_type.beam_mode.value}' + ) return [] try: @@ -308,7 +311,7 @@ def last_powder_refln_records( structure: Structure, experiment: ExperimentBase, *, - phase_id: str, + structure_id: str, ) -> list[PowderReflnRecord] | None: """ Return powder reflection records from the latest pattern run. @@ -321,7 +324,10 @@ def last_powder_refln_records( core_arrays = self._powder_refln_core_arrays(phase_block) if core_arrays is None: return None - x_values = self._powder_refln_x_values(phase_block, experiment.type.beam_mode.value) + x_values = self._powder_refln_x_values( + phase_block, + experiment.experiment_type.beam_mode.value, + ) if x_values is None: return None @@ -332,8 +338,8 @@ def last_powder_refln_records( return [ self._powder_refln_record( - phase_id=phase_id, - beam_mode=experiment.type.beam_mode.value, + structure_id=structure_id, + beam_mode=experiment.experiment_type.beam_mode.value, hkl=(index_h, index_k, index_l), values=(sthovl, d_value, x_value, f_value, f_sq_value), ) @@ -425,7 +431,7 @@ def _powder_refln_x_values( @staticmethod def _powder_refln_record( *, - phase_id: str, + structure_id: str, beam_mode: BeamModeEnum, hkl: tuple[int, int, int], values: tuple[float, float, float, float, float], @@ -443,7 +449,7 @@ def _powder_refln_record( x_kwargs = {'time_of_flight': float(x_value)} return PowderReflnRecord( - phase_id=phase_id, + structure_id=structure_id, d_spacing=float(d_spacing), sin_theta_over_lambda=float(sin_theta_over_lambda), index_h=int(index_h), @@ -638,7 +644,7 @@ class _CellLike: if adp_enum not in {AdpTypeEnum.BANI, AdpTypeEnum.UANI, AdpTypeEnum.BETA}: continue - aniso = structure.atom_site_aniso[atom.label.value] + aniso = structure.atom_site_aniso[atom.id.value] components = [ aniso.adp_11.value, aniso.adp_22.value, @@ -679,8 +685,8 @@ def _update_experiment_in_cryspy_dict( experiment : ExperimentBase The source experiment. """ - if experiment.type.sample_form.value == SampleFormEnum.POWDER: - if experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH: + if experiment.experiment_type.sample_form.value == SampleFormEnum.POWDER: + if experiment.experiment_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH: cryspy_expt_name = f'pd_{experiment.name}' cryspy_expt_dict = cryspy_dict[cryspy_expt_name] @@ -729,14 +735,14 @@ def _update_experiment_in_cryspy_dict( # loop was emitted, so guard. _update_texture_in_cryspy_dict(cryspy_expt_dict, experiment) - elif experiment.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT: + elif experiment.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['dtt2'][0] = experiment.instrument.calib_d_to_tof_quadratic.value cryspy_expt_dict['ttheta_bank'] = np.deg2rad( experiment.instrument.setup_twotheta_bank.value ) @@ -749,12 +755,12 @@ def _update_experiment_in_cryspy_dict( _update_tof_peak_in_cryspy_dict(cryspy_expt_dict, experiment.peak) - if experiment.type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL: + if experiment.experiment_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL: cryspy_expt_name = f'diffrn_{experiment.name}' cryspy_expt_dict = cryspy_dict[cryspy_expt_name] # Instrument - if experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH: + if experiment.experiment_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH: cryspy_expt_dict['wavelength'][0] = experiment.instrument.setup_wavelength.value # Extinction @@ -826,6 +832,34 @@ def _convert_structure_to_cryspy_cif(self, structure: Structure) -> str: finally: self._restore_from_u_notation(structure, saved) + return self._relabel_cif_tags_for_cryspy(cif) + + # Edi persistence renamed several CIF tags away from the legacy + # IUCr spellings that cryspy's CIF parser still requires. The + # displacement values are already converted to U notation by + # ``_temporarily_convert_to_u_notation``; only the tag names need + # mapping back so cryspy recognizes the block as a crystal. + _CRYSPY_TAG_REPLACEMENTS = ( + ('_atom_site_aniso.id', '_atom_site_aniso.label'), + ('_atom_site.id', '_atom_site.label'), + ('_space_group.name_h_m', '_space_group.name_H-M_alt'), + ('_space_group.coord_system_code', '_space_group.IT_coordinate_system_code'), + ('_atom_site.adp_iso', '_atom_site.U_iso_or_equiv'), + ('_atom_site_aniso.adp_11', '_atom_site_aniso.U_11'), + ('_atom_site_aniso.adp_22', '_atom_site_aniso.U_22'), + ('_atom_site_aniso.adp_33', '_atom_site_aniso.U_33'), + ('_atom_site_aniso.adp_12', '_atom_site_aniso.U_12'), + ('_atom_site_aniso.adp_13', '_atom_site_aniso.U_13'), + ('_atom_site_aniso.adp_23', '_atom_site_aniso.U_23'), + ) + + @staticmethod + def _relabel_cif_tags_for_cryspy(cif: str) -> str: + """ + Map Edi CIF tags to cryspy-recognized legacy spellings. + """ + for edi_tag, cryspy_tag in CryspyCalculator._CRYSPY_TAG_REPLACEMENTS: + cif = cif.replace(edi_tag, cryspy_tag) return cif @staticmethod @@ -865,10 +899,10 @@ def _temporarily_convert_to_u_notation( orig_adp_type = atom._adp_type._value orig_iso_val = atom._adp_iso._value - orig_iso_names = list(atom._adp_iso._cif_handler._names) + orig_iso_names = list(atom._adp_iso._tags._edi_names) atom._adp_iso._value = orig_iso_val / factor - atom._adp_iso._cif_handler._names = [ + atom._adp_iso._tags._edi_names = [ '_atom_site.U_iso_or_equiv', '_atom_site.B_iso_or_equiv', ] @@ -878,7 +912,7 @@ def _temporarily_convert_to_u_notation( saved.append((atom, None, None, None, orig_adp_type, orig_iso_names, orig_iso_val)) else: atom._adp_type._value = AdpTypeEnum.UANI.value - lbl = atom.label.value + lbl = atom.id.value if lbl in structure.atom_site_aniso: aniso = structure.atom_site_aniso[lbl] else: @@ -889,9 +923,9 @@ def _temporarily_convert_to_u_notation( for s in suffixes: param = getattr(aniso, f'_adp_{s}') orig_vals.append(param._value) - orig_names.append(list(param._cif_handler._names)) + orig_names.append(list(param._tags._edi_names)) param._value /= factor - param._cif_handler._names = [ + param._tags._edi_names = [ f'_atom_site_aniso.U_{s}', f'_atom_site_aniso.B_{s}', ] @@ -936,12 +970,12 @@ def _restore_from_u_notation( ) in saved: atom._adp_type._value = orig_adp_type atom._adp_iso._value = orig_iso_val - atom._adp_iso._cif_handler._names = orig_iso_names + atom._adp_iso._tags._edi_names = orig_iso_names if aniso is not None and orig_vals is not None: for s, val, names in zip(suffixes, orig_vals, orig_names, strict=False): param = getattr(aniso, f'_adp_{s}') param._value = val - param._cif_handler._names = names + param._tags._edi_names = names @staticmethod def _beta_reciprocal_pairs(structure: Structure) -> tuple[float, ...]: @@ -993,18 +1027,18 @@ def _stash_beta_atom_as_u( orig_adp_type = atom._adp_type._value orig_iso_val = atom._adp_iso._value - orig_iso_names = list(atom._adp_iso._cif_handler._names) + orig_iso_names = list(atom._adp_iso._tags._edi_names) # adp_iso already holds the equivalent U for a beta atom; only # the CIF tag needs relabelling (cryspy zeroes b_iso for aniso # atoms). - atom._adp_iso._cif_handler._names = [ + atom._adp_iso._tags._edi_names = [ '_atom_site.U_iso_or_equiv', '_atom_site.B_iso_or_equiv', ] atom._adp_type._value = AdpTypeEnum.UANI.value - lbl = atom.label.value + lbl = atom.id.value if lbl not in structure.atom_site_aniso: return (atom, None, None, None, orig_adp_type, orig_iso_names, orig_iso_val) aniso = structure.atom_site_aniso[lbl] @@ -1014,9 +1048,9 @@ def _stash_beta_atom_as_u( for s, pair in zip(suffixes, pairs, strict=False): param = getattr(aniso, f'_adp_{s}') orig_vals.append(param._value) - orig_names.append(list(param._cif_handler._names)) + orig_names.append(list(param._tags._edi_names)) param._value /= pair - param._cif_handler._names = [ + param._tags._edi_names = [ f'_atom_site_aniso.U_{s}', f'_atom_site_aniso.B_{s}', ] @@ -1043,7 +1077,7 @@ def _convert_experiment_to_cryspy_cif( # noqa: PLR6301 The Cryspy CIF string representation of the experiment. """ attrs = type(experiment)._public_attrs() - expt_type = experiment.type if 'type' in attrs else None + expt_type = experiment.experiment_type if 'experiment_type' in attrs else None instrument = experiment.instrument if 'instrument' in attrs else None peak = experiment.peak if 'peak' in attrs else None extinction = experiment.extinction if 'extinction' in attrs else None @@ -1112,7 +1146,7 @@ def _cif_instrument_section( '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', + 'calib_d_to_tof_quadratic': '_tof_parameters_dtt2', } elif expt_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL: instrument_mapping = {} # TODO: Check this mapping! @@ -1133,7 +1167,7 @@ def _update_tof_peak_in_cryspy_dict( peak_tag = peak.type_info.tag # TODO: Need to improve this logic to be more robust and extensible # for future profiles - if not hasattr(peak, 'exp_decay_beta_0') and not hasattr(peak, 'dexp_decay_beta_00'): + if not hasattr(peak, 'decay_beta_0') and not hasattr(peak, 'dexp_decay_beta_00'): cryspy_expt_dict['profile_gammas'][0] = peak.broad_lorentz_gamma_0.value cryspy_expt_dict['profile_gammas'][1] = peak.broad_lorentz_gamma_1.value cryspy_expt_dict['profile_gammas'][2] = peak.broad_lorentz_gamma_2.value @@ -1153,11 +1187,11 @@ def _update_tof_peak_in_cryspy_dict( cryspy_expt_dict['profile_gammas'][1] = peak.broad_lorentz_gamma_1.value cryspy_expt_dict['profile_gammas'][2] = peak.broad_lorentz_gamma_2.value else: - cryspy_expt_dict['profile_betas'][0] = peak.exp_decay_beta_0.value - cryspy_expt_dict['profile_betas'][1] = peak.exp_decay_beta_1.value + cryspy_expt_dict['profile_betas'][0] = peak.decay_beta_0.value + cryspy_expt_dict['profile_betas'][1] = peak.decay_beta_1.value - cryspy_expt_dict['profile_alphas'][0] = peak.exp_rise_alpha_0.value - cryspy_expt_dict['profile_alphas'][1] = peak.exp_rise_alpha_1.value + cryspy_expt_dict['profile_alphas'][0] = peak.rise_alpha_0.value + cryspy_expt_dict['profile_alphas'][1] = peak.rise_alpha_1.value if peak_tag == PeakProfileTypeEnum.TOF_JORGENSEN_VON_DREELE: cryspy_expt_dict['profile_gammas'][0] = peak.broad_lorentz_gamma_0.value @@ -1209,12 +1243,12 @@ def _cif_peak_section( 'dexp_switch_r_02': '_tof_profile_r02', 'dexp_switch_r_03': '_tof_profile_r03', }) - elif hasattr(peak, 'exp_decay_beta_0') and hasattr(peak, 'exp_rise_alpha_0'): + elif hasattr(peak, 'decay_beta_0') and hasattr(peak, 'rise_alpha_0'): peak_mapping.update({ - 'exp_decay_beta_0': '_tof_profile_beta0', - 'exp_decay_beta_1': '_tof_profile_beta1', - 'exp_rise_alpha_0': '_tof_profile_alpha0', - 'exp_rise_alpha_1': '_tof_profile_alpha1', + 'decay_beta_0': '_tof_profile_beta0', + 'decay_beta_1': '_tof_profile_beta1', + 'rise_alpha_0': '_tof_profile_alpha0', + 'rise_alpha_1': '_tof_profile_alpha1', }) if peak.type_info.tag == PeakProfileTypeEnum.TOF_JORGENSEN_VON_DREELE: cif_lines.append('_tof_profile_peak_shape pseudo-Voigt') @@ -1344,9 +1378,9 @@ def _cif_pref_orient_section( Append the cryspy texture (March-Dollase) loop for the phase. cryspy keys texture to a phase by ``_texture_label``, so only the - ``pref_orient`` row whose ``phase_id`` matches the phase being - calculated is emitted. A row with ``r = 1`` is a mathematical no-op; - an empty collection (the default) emits nothing. + ``preferred_orientation`` row whose ``structure_id`` matches the + phase being calculated is emitted. A row with ``r = 1`` is a + mathematical no-op; an empty collection (the default) emits nothing. """ # Initial support is constant-wavelength only (ADR Deferred Work); # the TOF pass-through is not wired, so a TOF texture loop would @@ -1363,7 +1397,7 @@ def _cif_pref_orient_section( return phase_label = linked_structure.name row = next( - (item for item in pref_orient if item.phase_id.value == phase_label), + (item for item in pref_orient if item.structure_id.value == phase_label), None, ) if row is None: @@ -1408,10 +1442,10 @@ def _update_texture_in_cryspy_dict( """ Patch cryspy texture g_1/g_2 from preferred-orientation rows. - Matches each emitted texture row to a ``pref_orient`` row by phase - label and writes the refinable coefficient and random fraction in - place. ``index_h``/``index_k``/``index_l`` are fixed descriptors, so - ``texture_axis`` is never touched. No-op when no texture loop was + Matches each emitted texture row to a preferred-orientation row by + phase label and writes the refinable coefficient and random fraction + in place. ``index_h``/``index_k``/``index_l`` are fixed descriptors, + so ``texture_axis`` is never touched. No-op when no texture loop was emitted. """ if 'texture_g1' not in cryspy_expt_dict: @@ -1419,7 +1453,7 @@ def _update_texture_in_cryspy_dict( pref_orient = getattr(experiment, 'preferred_orientation', None) if pref_orient is None: return - rows = {item.phase_id.value: item for item in pref_orient} + rows = {item.structure_id.value: item for item in pref_orient} for index, label in enumerate(cryspy_expt_dict['texture_name']): row = rows.get(str(label)) if row is not None: diff --git a/src/easydiffraction/analysis/calculators/pdffit.py b/src/easydiffraction/analysis/calculators/pdffit.py index 17d7d0f86..8539ea96c 100644 --- a/src/easydiffraction/analysis/calculators/pdffit.py +++ b/src/easydiffraction/analysis/calculators/pdffit.py @@ -32,6 +32,76 @@ def _open_pdffit_devnull() -> object: return os.fdopen(os.dup(tmp_devnull.fileno()), 'w') +_ANISO_SUFFIXES = ('11', '22', '33', '12', '13', '23') +_B_TO_U_FACTOR = 8.0 * np.pi**2 + + +def _normalize_b_family_adp_to_u(structure: Structure) -> list[tuple]: + """ + Temporarily convert B-convention ADP values to U notation. + + diffpy reads a single isotropic/anisotropic ADP column, so a + structure mixing ``Biso``/``Uiso`` (or ``Bani``/``Uani``) atoms must + be normalized to one convention. B-family values are divided by 8π² + so every row can be written under the U tags. Returns saved state + for restoration. ``beta`` atoms keep their stored equivalent values + unchanged. + """ + saved: list[tuple] = [] + for atom in structure.atom_sites: + adp_type = str(atom.adp_type.value).lower() + if adp_type not in {'biso', 'bani'}: + continue + saved.append((atom._adp_iso, atom._adp_iso._value)) + atom._adp_iso._value /= _B_TO_U_FACTOR + if atom.id.value in structure.atom_site_aniso: + aniso = structure.atom_site_aniso[atom.id.value] + for suffix in _ANISO_SUFFIXES: + param = getattr(aniso, f'_adp_{suffix}') + saved.append((param, param._value)) + param._value /= _B_TO_U_FACTOR + return saved + + +def _restore_adp_values(saved: list[tuple]) -> None: + """Restore ADP values saved by ``_normalize_b_family_adp_to_u``.""" + for param, value in saved: + param._value = value + + +def _structure_cif_for_pdffit(structure: Structure) -> str: + """ + Return structure CIF using legacy IUCr tags diffpy recognizes. + + Edi persistence renamed several CIF tags (``_atom_site.id``, + ``_space_group.name_h_m``, type-neutral ``_atom_site.adp_iso``). + diffpy's CIF parser only understands the legacy IUCr spellings, so + map them back. All ADP values are normalized to the U convention + first, so mixed B/U structures are written consistently under the U + tags rather than mislabeling one family. + """ + saved = _normalize_b_family_adp_to_u(structure) + try: + cif = structure.as_cif + finally: + _restore_adp_values(saved) + + replacements = [ + ('_atom_site_aniso.id', '_atom_site_aniso.label'), + ('_atom_site.id', '_atom_site.label'), + ('_space_group.name_h_m', '_space_group.name_H-M_alt'), + ('_space_group.coord_system_code', '_space_group.IT_coordinate_system_code'), + ('_atom_site.adp_iso', '_atom_site.U_iso_or_equiv'), + *( + (f'_atom_site_aniso.adp_{suffix}', f'_atom_site_aniso.U_{suffix}') + for suffix in _ANISO_SUFFIXES + ), + ] + for edi_tag, iucr_tag in replacements: + cif = cif.replace(edi_tag, iucr_tag) + return cif + + try: from diffpy.pdffit2 import PdfFit from diffpy.pdffit2 import redirect_stdout @@ -125,8 +195,9 @@ def calculate_pattern( # noqa: PLR6301 # --------------------------- # TODO: move CIF v2 -> CIF v1 conversion to a separate module - # Convert the structure to CIF supported by PDFfit - cif_string_v2 = structure.as_cif + # Convert the structure to CIF supported by PDFfit, mapping + # Edi tags back to the legacy IUCr spellings diffpy needs. + cif_string_v2 = _structure_cif_for_pdffit(structure) # 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. @@ -145,7 +216,7 @@ def calculate_pattern( # noqa: PLR6301 # ------------------------- # Set some peak-related parameters - calculator.setvar('pscale', experiment.linked_phases[structure.name].scale.value) + calculator.setvar('pscale', experiment.linked_structures[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) @@ -156,7 +227,7 @@ def calculate_pattern( # noqa: PLR6301 # Assign the data to the PDFfit calculator calculator.read_data_lists( - stype=experiment.type.radiation_probe.value[0].upper(), + stype=experiment.experiment_type.radiation_probe.value[0].upper(), qmax=experiment.peak.cutoff_q.value, qdamp=experiment.peak.damp_q.value, r_data=x, diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 1054aacbb..e19e2a797 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -4,7 +4,7 @@ Alias category for mapping friendly names to parameters. Defines a small record type used by analysis configuration to refer to -parameters via readable labels instead of opaque identifiers. At runtime +parameters via readable ids instead of opaque identifiers. At runtime each alias holds a direct object reference to the parameter; for CIF serialization the parameter's ``unique_name`` is stored. """ @@ -18,47 +18,49 @@ 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 +from easydiffraction.io.cif.handler import TagSpec class Alias(CategoryItem): """ Single alias entry. - Maps a human-readable ``label`` to a parameter object. The - ``param_unique_name`` descriptor stores the parameter's + Maps a human-readable ``id`` to a parameter object. The + ``parameter_unique_name`` descriptor stores the parameter's ``unique_name`` for CIF serialization. """ _category_code = 'alias' - _category_entry_name = 'label' + _category_entry_name = 'id' def __init__(self) -> None: """Initialize the alias descriptors and parameter reference.""" super().__init__() - self._label = StringDescriptor( - name='label', - description='Human-readable alias for a parameter.', + self._id = StringDescriptor( + name='id', + description='Human-readable alias id for a parameter.', value_spec=AttributeSpec( default='_', # TODO: Maybe None? validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), - cif_handler=CifHandler( - names=['_alias.label'], - iucr_name='_easydiffraction_alias.label', + tags=TagSpec( + edi_names=['_alias.id'], cif_names=['_easydiffraction_alias.id', '_alias.label'] ), ) - self._param_unique_name = StringDescriptor( - name='param_unique_name', + self._parameter_unique_name = StringDescriptor( + name='parameter_unique_name', description='Unique name of the referenced parameter.', value_spec=AttributeSpec( default='_', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_.]*$'), ), - cif_handler=CifHandler( - names=['_alias.param_unique_name'], - iucr_name='_easydiffraction_alias.param_unique_name', + tags=TagSpec( + edi_names=['_alias.parameter_unique_name'], + cif_names=[ + '_easydiffraction_alias.parameter_unique_name', + '_alias.param_unique_name', + ], ), ) @@ -71,20 +73,20 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def label(self) -> StringDescriptor: + def id(self) -> StringDescriptor: """ - Human-readable alias label (e.g. ``'biso_La'``). + Human-readable alias id (e.g. ``'biso_La'``). Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the parameter value. """ - return self._label + return self._id - @label.setter - def label(self, value: str) -> None: - """Set the alias label value.""" - self._label.value = value + @id.setter + def id(self, value: str) -> None: + """Set the alias id value.""" + self._id.value = value @property def param(self) -> object | None: @@ -94,31 +96,31 @@ def param(self) -> object | None: return self._param_ref @property - def param_unique_name(self) -> StringDescriptor: + def parameter_unique_name(self) -> StringDescriptor: """ Unique name of the referenced parameter (for CIF). Reading this property returns the underlying ``StringDescriptor`` object. """ - return self._param_unique_name + return self._parameter_unique_name def _set_param(self, param: object) -> None: """ Store a direct reference to the parameter. - Also updates ``param_unique_name`` from the parameter's + Also updates ``parameter_unique_name`` from the parameter's ``unique_name`` for CIF round-tripping. """ object.__setattr__(self, '_param_ref', param) # noqa: PLC2801 - self._param_unique_name.value = param.unique_name + self._parameter_unique_name.value = param.unique_name @property def parameters(self) -> list: """ Descriptors owned by this alias (excludes the param reference). """ - return [self._label, self._param_unique_name] + return [self._id, self._parameter_unique_name] @AliasesFactory.register @@ -134,18 +136,18 @@ def __init__(self) -> None: """Create an empty collection of aliases.""" super().__init__(item_type=Alias) - def create(self, *, label: str, param: object) -> None: + def create(self, *, id: str, param: object) -> None: """ - Create a new alias mapping a label to a parameter. + Create a new alias mapping an id to a parameter. Parameters ---------- - label : str + id : str Human-readable alias name (e.g. ``'biso_La'``). param : object The parameter object to reference. """ item = Alias() - item.label = label + item.id = id item._set_param(param) self.add(item) diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index f9625b1ea..c649fa1d5 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -17,7 +17,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.utils.logging import console from easydiffraction.utils.logging import log from easydiffraction.utils.utils import render_table @@ -40,9 +40,8 @@ def __init__(self) -> None: default='_', validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'), ), - cif_handler=CifHandler( - names=['_constraint.id'], - iucr_name='_easydiffraction_constraint.id', + tags=TagSpec( + edi_names=['_constraint.id'], cif_names=['_easydiffraction_constraint.id'] ), ) self._expression = StringDescriptor( @@ -52,9 +51,9 @@ def __init__(self) -> None: default='_', # TODO: Maybe None? validator=RegexValidator(pattern=r'.*'), ), - cif_handler=CifHandler( - names=['_constraint.expression'], - iucr_name='_easydiffraction_constraint.expression', + tags=TagSpec( + edi_names=['_constraint.expression'], + cif_names=['_easydiffraction_constraint.expression'], ), ) diff --git a/src/easydiffraction/analysis/categories/fit_parameter_correlations/default.py b/src/easydiffraction/analysis/categories/fit_parameter_correlations/default.py index eb60a62ca..c913c3020 100644 --- a/src/easydiffraction/analysis/categories/fit_parameter_correlations/default.py +++ b/src/easydiffraction/analysis/categories/fit_parameter_correlations/default.py @@ -17,17 +17,17 @@ from easydiffraction.core.variable import EnumDescriptor from easydiffraction.core.variable import NumericDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec def _normalized_parameter_pair( - param_unique_name_i: str, - param_unique_name_j: str, + parameter_unique_name_i: str, + parameter_unique_name_j: str, ) -> tuple[str, str]: """Return a stable ordering for a parameter pair.""" - if param_unique_name_i <= param_unique_name_j: - return param_unique_name_i, param_unique_name_j - return param_unique_name_j, param_unique_name_i + if parameter_unique_name_i <= parameter_unique_name_j: + return parameter_unique_name_i, parameter_unique_name_j + return parameter_unique_name_j, parameter_unique_name_i class FitParameterCorrelationItem(CategoryItem): @@ -46,31 +46,37 @@ def __init__(self) -> None: default='_', validator=RegexValidator(pattern=r'^[A-Za-z0-9_.:-]+$'), ), - cif_handler=CifHandler(names=['_fit_parameter_correlation.id']), + tags=TagSpec(edi_names=['_fit_parameter_correlation.id']), ) self._source_kind = EnumDescriptor( name='source_kind', enum=FitCorrelationSourceEnum, description='Origin of the persisted correlation summary.', - cif_handler=CifHandler(names=['_fit_parameter_correlation.source_kind']), + tags=TagSpec(edi_names=['_fit_parameter_correlation.source_kind']), ) - self._param_unique_name_i = StringDescriptor( - name='param_unique_name_i', + self._parameter_unique_name_i = StringDescriptor( + name='parameter_unique_name_i', description='First unique parameter name in the persisted pair.', value_spec=AttributeSpec( default='_', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_.]*$'), ), - cif_handler=CifHandler(names=['_fit_parameter_correlation.param_unique_name_i']), + tags=TagSpec( + edi_names=['_fit_parameter_correlation.parameter_unique_name_i'], + cif_names=['_fit_parameter_correlation.param_unique_name_i'], + ), ) - self._param_unique_name_j = StringDescriptor( - name='param_unique_name_j', + self._parameter_unique_name_j = StringDescriptor( + name='parameter_unique_name_j', description='Second unique parameter name in the persisted pair.', value_spec=AttributeSpec( default='_', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_.]*$'), ), - cif_handler=CifHandler(names=['_fit_parameter_correlation.param_unique_name_j']), + tags=TagSpec( + edi_names=['_fit_parameter_correlation.parameter_unique_name_j'], + cif_names=['_fit_parameter_correlation.param_unique_name_j'], + ), ) self._correlation = NumericDescriptor( name='correlation', @@ -79,7 +85,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=-1.0, le=1.0), ), - cif_handler=CifHandler(names=['_fit_parameter_correlation.correlation']), + tags=TagSpec(edi_names=['_fit_parameter_correlation.correlation']), ) @property @@ -101,22 +107,22 @@ def _set_source_kind(self, value: str) -> None: self._source_kind.value = value @property - def param_unique_name_i(self) -> StringDescriptor: + def parameter_unique_name_i(self) -> StringDescriptor: """First unique parameter name in the persisted pair.""" - return self._param_unique_name_i + return self._parameter_unique_name_i - def _set_param_unique_name_i(self, value: str) -> None: + def _set_parameter_unique_name_i(self, value: str) -> None: """Set the first parameter name for internal callers.""" - self._param_unique_name_i.value = value + self._parameter_unique_name_i.value = value @property - def param_unique_name_j(self) -> StringDescriptor: + def parameter_unique_name_j(self) -> StringDescriptor: """Second unique parameter name in the persisted pair.""" - return self._param_unique_name_j + return self._parameter_unique_name_j - def _set_param_unique_name_j(self, value: str) -> None: + def _set_parameter_unique_name_j(self, value: str) -> None: """Set the second parameter name for internal callers.""" - self._param_unique_name_j.value = value + self._parameter_unique_name_j.value = value @property def correlation(self) -> NumericDescriptor: @@ -145,8 +151,8 @@ def create( self, *, source_kind: str, - param_unique_name_i: str, - param_unique_name_j: str, + parameter_unique_name_i: str, + parameter_unique_name_j: str, correlation: float, id: str | None = None, ) -> None: @@ -157,9 +163,9 @@ def create( ---------- source_kind : str Origin of the persisted correlation summary. - param_unique_name_i : str + parameter_unique_name_i : str First unique parameter name in the pair. - param_unique_name_j : str + parameter_unique_name_j : str Second unique parameter name in the pair. correlation : float Correlation coefficient for the parameter pair. @@ -168,13 +174,13 @@ def create( sequential identifier is generated. """ normalized_i, normalized_j = _normalized_parameter_pair( - param_unique_name_i, - param_unique_name_j, + parameter_unique_name_i, + parameter_unique_name_j, ) item = FitParameterCorrelationItem() item._set_source_kind(source_kind) - item._set_param_unique_name_i(normalized_i) - item._set_param_unique_name_j(normalized_j) + item._set_parameter_unique_name_i(normalized_i) + item._set_parameter_unique_name_j(normalized_j) item._set_correlation(correlation) resolved_id = id or str(len(self) + 1) item._set_id(resolved_id) diff --git a/src/easydiffraction/analysis/categories/fit_parameters/default.py b/src/easydiffraction/analysis/categories/fit_parameters/default.py index b95654ac7..467f01700 100644 --- a/src/easydiffraction/analysis/categories/fit_parameters/default.py +++ b/src/easydiffraction/analysis/categories/fit_parameters/default.py @@ -18,23 +18,23 @@ 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 +from easydiffraction.io.cif.handler import TagSpec class FitParameterItem(CategoryItem): """Single persisted fit-parameter control row.""" _category_code = 'fit_parameter' - _category_entry_name = 'param_unique_name' + _category_entry_name = 'parameter_unique_name' _control_descriptor_names: ClassVar[tuple[str, ...]] = ( - 'param_unique_name', + 'parameter_unique_name', 'fit_min', 'fit_max', 'start_value', 'start_uncertainty', ) _optional_control_descriptor_names: ClassVar[tuple[str, ...]] = ( - 'fit_bounds_uncertainty_multiplier', + 'bounds_uncertainty_multiplier', ) _posterior_descriptor_names: ClassVar[tuple[str, ...]] = ( 'posterior_best_sample_value', @@ -51,110 +51,116 @@ class FitParameterItem(CategoryItem): def __init__(self) -> None: """Initialize the persisted fit-parameter descriptors.""" super().__init__() - self._param_unique_name = StringDescriptor( - name='param_unique_name', + self._parameter_unique_name = StringDescriptor( + name='parameter_unique_name', description='Unique name of the referenced live parameter.', value_spec=AttributeSpec( default='_', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_.]*$'), ), - cif_handler=CifHandler(names=['_fit_parameter.param_unique_name']), + tags=TagSpec( + edi_names=['_fit_parameter.parameter_unique_name'], + cif_names=['_fit_parameter.param_unique_name'], + ), ) self._fit_min = NumericDescriptor( name='fit_min', description='Persisted lower fit bound.', value_spec=AttributeSpec(default=-np.inf), - cif_handler=CifHandler(names=['_fit_parameter.fit_min']), + tags=TagSpec(edi_names=['_fit_parameter.fit_min']), ) self._fit_max = NumericDescriptor( name='fit_max', description='Persisted upper fit bound.', value_spec=AttributeSpec(default=np.inf), - cif_handler=CifHandler(names=['_fit_parameter.fit_max']), + tags=TagSpec(edi_names=['_fit_parameter.fit_max']), ) - self._fit_bounds_uncertainty_multiplier = NumericDescriptor( - name='fit_bounds_uncertainty_multiplier', + self._bounds_uncertainty_multiplier = NumericDescriptor( + name='bounds_uncertainty_multiplier', description='Multiplier used to derive fit bounds from uncertainty.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.fit_bounds_uncertainty_multiplier']), + tags=TagSpec( + edi_names=['_fit_parameter.bounds_uncertainty_multiplier'], + cif_names=['_fit_parameter.fit_bounds_uncertainty_multiplier'], + ), ) self._start_value = NumericDescriptor( name='start_value', description='Persisted pre-fit value snapshot.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.start_value']), + tags=TagSpec(edi_names=['_fit_parameter.start_value']), ) self._start_uncertainty = NumericDescriptor( name='start_uncertainty', description='Persisted pre-fit uncertainty snapshot.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.start_uncertainty']), + tags=TagSpec(edi_names=['_fit_parameter.start_uncertainty']), ) self._posterior_best_sample_value = NumericDescriptor( name='posterior_best_sample_value', description='Highest-posterior sampled parameter value.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_best_sample_value']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_best_sample_value']), ) self._posterior_median = NumericDescriptor( name='posterior_median', description='Posterior median value.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_median']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_median']), ) self._posterior_uncertainty = NumericDescriptor( name='posterior_uncertainty', description='Posterior standard deviation.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_uncertainty']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_uncertainty']), ) self._posterior_interval_68_low = NumericDescriptor( name='posterior_interval_68_low', description='Lower bound of the 68% credible interval.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_interval_68_low']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_interval_68_low']), ) self._posterior_interval_68_high = NumericDescriptor( name='posterior_interval_68_high', description='Upper bound of the 68% credible interval.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_interval_68_high']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_interval_68_high']), ) self._posterior_interval_95_low = NumericDescriptor( name='posterior_interval_95_low', description='Lower bound of the 95% credible interval.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_interval_95_low']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_interval_95_low']), ) self._posterior_interval_95_high = NumericDescriptor( name='posterior_interval_95_high', description='Upper bound of the 95% credible interval.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_interval_95_high']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_interval_95_high']), ) self._posterior_gelman_rubin = NumericDescriptor( name='posterior_gelman_rubin', description='Rank-normalized split-R-hat when available.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_gelman_rubin']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_gelman_rubin']), ) self._posterior_effective_sample_size_bulk = NumericDescriptor( name='posterior_effective_sample_size_bulk', description='Bulk effective sample size when available.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_parameter.posterior_effective_sample_size_bulk']), + tags=TagSpec(edi_names=['_fit_parameter.posterior_effective_sample_size_bulk']), ) @property - def param_unique_name(self) -> StringDescriptor: + def parameter_unique_name(self) -> StringDescriptor: """Unique name of the referenced live parameter.""" - return self._param_unique_name + return self._parameter_unique_name - def _set_param_unique_name(self, value: str) -> None: + def _set_parameter_unique_name(self, value: str) -> None: """ Set the referenced parameter unique name for internal callers. """ - self._param_unique_name.value = value + self._parameter_unique_name.value = value @property def fit_min(self) -> NumericDescriptor: @@ -175,18 +181,18 @@ def _set_fit_max(self, value: float) -> None: self._fit_max.value = value @property - def fit_bounds_uncertainty_multiplier(self) -> NumericDescriptor: + def bounds_uncertainty_multiplier(self) -> NumericDescriptor: """Multiplier used to derive fit bounds from uncertainty.""" - return self._fit_bounds_uncertainty_multiplier + return self._bounds_uncertainty_multiplier - def _set_fit_bounds_uncertainty_multiplier( + def _set_bounds_uncertainty_multiplier( self, value: float | None, ) -> None: """ Set the fit-bounds uncertainty multiplier for internal callers. """ - self._fit_bounds_uncertainty_multiplier.value = value + self._bounds_uncertainty_multiplier.value = value @property def start_value(self) -> NumericDescriptor: @@ -327,7 +333,7 @@ def posterior_summary(self, *, display_name: str) -> PosteriorParameterSummary | return None return PosteriorParameterSummary( - unique_name=self.param_unique_name.value, + unique_name=self.parameter_unique_name.value, display_name=display_name, best_sample_value=self._posterior_float(self.posterior_best_sample_value.value), median=self._posterior_float(self.posterior_median.value), @@ -369,7 +375,7 @@ def _include_posterior_cif_descriptors(self) -> bool: def _include_uncertainty_multiplier_cif_descriptor(self) -> bool: """Return whether CIF output includes the bounds multiplier.""" - return any(item.fit_bounds_uncertainty_multiplier.value is not None for item in self) + return any(item.bounds_uncertainty_multiplier.value is not None for item in self) def _cif_loop_parameters(self, item: FitParameterItem) -> list[object]: """Return CIF loop descriptors for the current fit kind.""" @@ -390,10 +396,10 @@ def _cif_loop_parameters(self, item: FitParameterItem) -> list[object]: def create( self, *, - param_unique_name: str, + parameter_unique_name: str, fit_min: float, fit_max: float, - fit_bounds_uncertainty_multiplier: float | None = None, + bounds_uncertainty_multiplier: float | None = None, start_value: float | None = None, start_uncertainty: float | None = None, ) -> None: @@ -402,13 +408,13 @@ def create( Parameters ---------- - param_unique_name : str + parameter_unique_name : str Unique name of the referenced live parameter. fit_min : float Persisted lower fit bound. fit_max : float Persisted upper fit bound. - fit_bounds_uncertainty_multiplier : float | None, default=None + bounds_uncertainty_multiplier : float | None, default=None Multiplier used to derive fit bounds from uncertainty. start_value : float | None, default=None Persisted pre-fit value snapshot. @@ -416,10 +422,10 @@ def create( Persisted pre-fit uncertainty snapshot. """ item = FitParameterItem() - item._set_param_unique_name(param_unique_name) + item._set_parameter_unique_name(parameter_unique_name) item._set_fit_min(fit_min) item._set_fit_max(fit_max) - item._set_fit_bounds_uncertainty_multiplier(fit_bounds_uncertainty_multiplier) + item._set_bounds_uncertainty_multiplier(bounds_uncertainty_multiplier) item._set_start_value(start_value) item._set_start_uncertainty(start_uncertainty) self.add(item) diff --git a/src/easydiffraction/analysis/categories/fit_result/base.py b/src/easydiffraction/analysis/categories/fit_result/base.py index e4765000c..c1282fd65 100644 --- a/src/easydiffraction/analysis/categories/fit_result/base.py +++ b/src/easydiffraction/analysis/categories/fit_result/base.py @@ -18,7 +18,7 @@ from easydiffraction.core.variable import IntegerDescriptor from easydiffraction.core.variable import NumericDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec def _result_display_handler(label: str) -> DisplayHandler: @@ -54,42 +54,42 @@ def __init__(self) -> None: name='result_kind', enum=FitResultKindEnum, description='Kind of the latest persisted fit-result projection.', - cif_handler=CifHandler(names=['_fit_result.result_kind']), + tags=TagSpec(edi_names=['_fit_result.result_kind']), display_handler=_result_display_handler('Result kind'), ) self._success = BoolDescriptor( name='success', description='Whether the latest persisted fit-result projection succeeded.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_result.success']), + tags=TagSpec(edi_names=['_fit_result.success']), display_handler=_result_display_handler('Success'), ) self._message = StringDescriptor( name='message', description='Status message for the latest persisted fit-result projection.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_result.message']), + tags=TagSpec(edi_names=['_fit_result.message']), display_handler=_result_display_handler('Message'), ) self._iterations = IntegerDescriptor( name='iterations', description='Iteration count for the latest persisted fit-result projection.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_result.iterations']), + tags=TagSpec(edi_names=['_fit_result.iterations']), display_handler=_result_display_handler('Iterations'), ) self._fitting_time = NumericDescriptor( name='fitting_time', description='Fitting time in seconds for the latest persisted projection.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_result.fitting_time']), + tags=TagSpec(edi_names=['_fit_result.fitting_time']), display_handler=_result_display_handler('Fitting time (s)'), ) self._reduced_chi_square = NumericDescriptor( name='reduced_chi_square', description='Reduced chi-square for the latest persisted projection.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_fit_result.reduced_chi_square']), + tags=TagSpec(edi_names=['_fit_result.reduced_chi_square']), display_handler=_result_display_handler('Reduced chi-square'), ) diff --git a/src/easydiffraction/analysis/categories/fit_result/bayesian.py b/src/easydiffraction/analysis/categories/fit_result/bayesian.py index cb864cc15..81f8840da 100644 --- a/src/easydiffraction/analysis/categories/fit_result/bayesian.py +++ b/src/easydiffraction/analysis/categories/fit_result/bayesian.py @@ -14,7 +14,7 @@ from easydiffraction.core.variable import IntegerDescriptor from easydiffraction.core.variable import NumericDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec @FitResultFactory.register @@ -64,9 +64,9 @@ def _point_estimate_name_descriptor() -> StringDescriptor: name='point_estimate_name', description='Committed sampled point estimate name.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_fit_result.point_estimate_name'], - iucr_name='_easydiffraction_fit_result.point_estimate_name', + tags=TagSpec( + edi_names=['_fit_result.point_estimate_name'], + cif_names=['_easydiffraction_fit_result.point_estimate_name'], ), ) @@ -77,9 +77,9 @@ def _sampler_completed_descriptor() -> BoolDescriptor: name='sampler_completed', description='Whether the sampler completed and returned posterior data.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_fit_result.sampler_completed'], - iucr_name='_easydiffraction_fit_result.sampler_completed', + tags=TagSpec( + edi_names=['_fit_result.sampler_completed'], + cif_names=['_easydiffraction_fit_result.sampler_completed'], ), ) @@ -90,9 +90,9 @@ def _credible_interval_inner_descriptor() -> NumericDescriptor: name='credible_interval_inner', description='Inner credible-interval level used in summaries.', value_spec=AttributeSpec(default=0.68), - cif_handler=CifHandler( - names=['_fit_result.credible_interval_inner'], - iucr_name='_easydiffraction_fit_result.credible_interval_inner', + tags=TagSpec( + edi_names=['_fit_result.credible_interval_inner'], + cif_names=['_easydiffraction_fit_result.credible_interval_inner'], ), ) @@ -103,9 +103,9 @@ def _credible_interval_outer_descriptor() -> NumericDescriptor: name='credible_interval_outer', description='Outer credible-interval level used in summaries.', value_spec=AttributeSpec(default=0.95), - cif_handler=CifHandler( - names=['_fit_result.credible_interval_outer'], - iucr_name='_easydiffraction_fit_result.credible_interval_outer', + tags=TagSpec( + edi_names=['_fit_result.credible_interval_outer'], + cif_names=['_easydiffraction_fit_result.credible_interval_outer'], ), ) @@ -116,9 +116,9 @@ def _acceptance_rate_mean_descriptor() -> NumericDescriptor: name='acceptance_rate_mean', description='Mean sampler acceptance rate.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_fit_result.acceptance_rate_mean'], - iucr_name='_easydiffraction_fit_result.acceptance_rate_mean', + tags=TagSpec( + edi_names=['_fit_result.acceptance_rate_mean'], + cif_names=['_easydiffraction_fit_result.acceptance_rate_mean'], ), ) @@ -129,9 +129,9 @@ def _resolved_random_seed_descriptor() -> IntegerDescriptor: name='resolved_random_seed', description='Runtime random seed used by the sampler.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_fit_result.resolved_random_seed'], - iucr_name='_easydiffraction_fit_result.resolved_random_seed', + tags=TagSpec( + edi_names=['_fit_result.resolved_random_seed'], + cif_names=['_easydiffraction_fit_result.resolved_random_seed'], ), ) @@ -142,9 +142,9 @@ def _gelman_rubin_max_descriptor() -> NumericDescriptor: name='gelman_rubin_max', description='Maximum rank-normalized split R-hat.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_fit_result.gelman_rubin_max'], - iucr_name='_easydiffraction_fit_result.gelman_rubin_max', + tags=TagSpec( + edi_names=['_fit_result.gelman_rubin_max'], + cif_names=['_easydiffraction_fit_result.gelman_rubin_max'], ), ) @@ -155,9 +155,9 @@ def _effective_sample_size_min_descriptor() -> NumericDescriptor: name='effective_sample_size_min', description='Minimum bulk effective sample size.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_fit_result.effective_sample_size_min'], - iucr_name='_easydiffraction_fit_result.effective_sample_size_min', + tags=TagSpec( + edi_names=['_fit_result.effective_sample_size_min'], + cif_names=['_easydiffraction_fit_result.effective_sample_size_min'], ), ) @@ -168,9 +168,9 @@ def _best_log_posterior_descriptor() -> NumericDescriptor: name='best_log_posterior', description='Best log-posterior value found.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_fit_result.best_log_posterior'], - iucr_name='_easydiffraction_fit_result.best_log_posterior', + tags=TagSpec( + edi_names=['_fit_result.best_log_posterior'], + cif_names=['_easydiffraction_fit_result.best_log_posterior'], ), ) diff --git a/src/easydiffraction/analysis/categories/fit_result/lsq.py b/src/easydiffraction/analysis/categories/fit_result/lsq.py index 5ded1ff01..8a0426d7e 100644 --- a/src/easydiffraction/analysis/categories/fit_result/lsq.py +++ b/src/easydiffraction/analysis/categories/fit_result/lsq.py @@ -14,7 +14,20 @@ from easydiffraction.core.variable import BoolDescriptor from easydiffraction.core.variable import NumericDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec + + +def _fit_result_tags(name: str, cif_name: str | None = None) -> TagSpec: + """ + Return an Edi-first handler for one fit-result descriptor. + """ + names = [f'_fit_result.{name}'] + if cif_name is None: + return TagSpec(edi_names=names) + return TagSpec( + edi_names=names, + cif_names=[f'_fit_result.{cif_name}'], + ) class _LeastSquaresCoreProperties: @@ -489,7 +502,7 @@ def _string_result_descriptor( name=name, description=description, value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=[f'_fit_result.{cif_name or name}']), + tags=_fit_result_tags(name, cif_name), display_handler=_result_display_handler(display_name), ) @@ -508,7 +521,7 @@ def _numeric_result_descriptor( name=name, description=description, value_spec=AttributeSpec(default=default, allow_none=allow_none), - cif_handler=CifHandler(names=[f'_fit_result.{cif_name or name}']), + tags=_fit_result_tags(name, cif_name), display_handler=_result_display_handler(display_name), ) @@ -529,7 +542,7 @@ def _integer_result_descriptor( name=name, description=description, value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=[f'_fit_result.{name}']), + tags=TagSpec(edi_names=[f'_fit_result.{name}']), display_handler=_result_display_handler(display_name), ) @@ -550,7 +563,7 @@ def _bool_result_descriptor( name=name, description=description, value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=[f'_fit_result.{name}']), + tags=TagSpec(edi_names=[f'_fit_result.{name}']), display_handler=_result_display_handler(display_name), ) diff --git a/src/easydiffraction/analysis/categories/fitting_mode/default.py b/src/easydiffraction/analysis/categories/fitting_mode/default.py index 1ec612eea..5fa5bc1c7 100644 --- a/src/easydiffraction/analysis/categories/fitting_mode/default.py +++ b/src/easydiffraction/analysis/categories/fitting_mode/default.py @@ -13,7 +13,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec @FittingModeFactory.register @@ -42,9 +42,8 @@ def __init__(self) -> None: allowed=[mode.value for mode in FitModeEnum], ), ), - cif_handler=CifHandler( - names=['_fitting_mode.type'], - iucr_name='_easydiffraction_fitting_mode.type', + tags=TagSpec( + edi_names=['_fitting_mode.type'], cif_names=['_easydiffraction_fitting_mode.type'] ), display_handler=DisplayHandler(display_name='Type', latex_name='Type'), ) diff --git a/src/easydiffraction/analysis/categories/joint_fit/default.py b/src/easydiffraction/analysis/categories/joint_fit/default.py index eceafa4aa..e310f37f8 100644 --- a/src/easydiffraction/analysis/categories/joint_fit/default.py +++ b/src/easydiffraction/analysis/categories/joint_fit/default.py @@ -18,7 +18,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec class JointFitItem(CategoryItem): @@ -38,9 +38,9 @@ def __init__(self) -> None: default='_', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), - cif_handler=CifHandler( - names=['_joint_fit.experiment_id'], - iucr_name='_easydiffraction_joint_fit.experiment_id', + tags=TagSpec( + edi_names=['_joint_fit.experiment_id'], + cif_names=['_easydiffraction_joint_fit.experiment_id'], ), ) self._weight: NumericDescriptor = NumericDescriptor( @@ -50,9 +50,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_joint_fit.weight'], - iucr_name='_easydiffraction_joint_fit.weight', + tags=TagSpec( + edi_names=['_joint_fit.weight'], cif_names=['_easydiffraction_joint_fit.weight'] ), ) diff --git a/src/easydiffraction/analysis/categories/minimizer/base.py b/src/easydiffraction/analysis/categories/minimizer/base.py index 0fc4175da..7173ca2d4 100644 --- a/src/easydiffraction/analysis/categories/minimizer/base.py +++ b/src/easydiffraction/analysis/categories/minimizer/base.py @@ -15,7 +15,7 @@ from easydiffraction.core.validation import MembershipValidator from easydiffraction.core.variable import GenericDescriptorBase from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class MinimizerCategoryBase(CategoryItem, SwitchableCategoryBase): @@ -42,9 +42,8 @@ def __init__(self) -> None: allowed=[member.value for member in MinimizerTypeEnum], ), ), - cif_handler=CifHandler( - names=['_minimizer.type'], - iucr_name='_easydiffraction_minimizer.type', + tags=TagSpec( + edi_names=['_minimizer.type'], cif_names=['_easydiffraction_minimizer.type'] ), display_handler=DisplayHandler(display_name='Type', latex_name='Type'), ) diff --git a/src/easydiffraction/analysis/categories/minimizer/bayesian_base.py b/src/easydiffraction/analysis/categories/minimizer/bayesian_base.py index 5af55a8d1..cbf128097 100644 --- a/src/easydiffraction/analysis/categories/minimizer/bayesian_base.py +++ b/src/easydiffraction/analysis/categories/minimizer/bayesian_base.py @@ -14,7 +14,7 @@ from easydiffraction.core.validation import RangeValidator from easydiffraction.core.variable import IntegerDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class BayesianMinimizerBase(MinimizerCategoryBase): @@ -72,9 +72,9 @@ def _sampling_steps_descriptor(default: int) -> IntegerDescriptor: name='sampling_steps', description='Total sampler iterations per chain.', value_spec=AttributeSpec(default=default, validator=RangeValidator(ge=1)), - cif_handler=CifHandler( - names=['_minimizer.sampling_steps'], - iucr_name='_easydiffraction_minimizer.sampling_steps', + tags=TagSpec( + edi_names=['_minimizer.sampling_steps'], + cif_names=['_easydiffraction_minimizer.sampling_steps'], ), ) @@ -85,9 +85,9 @@ def _burn_in_steps_descriptor(default: int) -> IntegerDescriptor: name='burn_in_steps', description='Sampler iterations discarded as warm-up.', value_spec=AttributeSpec(default=default, validator=RangeValidator(ge=0)), - cif_handler=CifHandler( - names=['_minimizer.burn_in_steps'], - iucr_name='_easydiffraction_minimizer.burn_in_steps', + tags=TagSpec( + edi_names=['_minimizer.burn_in_steps'], + cif_names=['_easydiffraction_minimizer.burn_in_steps'], ), ) @@ -98,9 +98,9 @@ def _thinning_interval_descriptor(default: int) -> IntegerDescriptor: name='thinning_interval', description='Sampler thinning interval.', value_spec=AttributeSpec(default=default, validator=RangeValidator(ge=1)), - cif_handler=CifHandler( - names=['_minimizer.thinning_interval'], - iucr_name='_easydiffraction_minimizer.thinning_interval', + tags=TagSpec( + edi_names=['_minimizer.thinning_interval'], + cif_names=['_easydiffraction_minimizer.thinning_interval'], ), ) @@ -111,9 +111,9 @@ def _population_size_descriptor(default: int) -> IntegerDescriptor: name='population_size', description='Number of chains or walkers.', value_spec=AttributeSpec(default=default, validator=RangeValidator(ge=1)), - cif_handler=CifHandler( - names=['_minimizer.population_size'], - iucr_name='_easydiffraction_minimizer.population_size', + tags=TagSpec( + edi_names=['_minimizer.population_size'], + cif_names=['_easydiffraction_minimizer.population_size'], ), ) @@ -124,9 +124,9 @@ def _parallel_workers_descriptor(default: int) -> IntegerDescriptor: name='parallel_workers', description='Worker count; 0 uses all available CPUs.', value_spec=AttributeSpec(default=default, validator=RangeValidator(ge=0)), - cif_handler=CifHandler( - names=['_minimizer.parallel_workers'], - iucr_name='_easydiffraction_minimizer.parallel_workers', + tags=TagSpec( + edi_names=['_minimizer.parallel_workers'], + cif_names=['_easydiffraction_minimizer.parallel_workers'], ), ) @@ -141,9 +141,9 @@ def _initialization_method_descriptor(cls) -> StringDescriptor: default=InitializationMethodEnum.LATIN_HYPERCUBE.value, validator=MembershipValidator(allowed=allowed), ), - cif_handler=CifHandler( - names=['_minimizer.initialization_method'], - iucr_name='_easydiffraction_minimizer.initialization_method', + tags=TagSpec( + edi_names=['_minimizer.initialization_method'], + cif_names=['_easydiffraction_minimizer.initialization_method'], ), ) @@ -154,9 +154,9 @@ def _random_seed_descriptor() -> IntegerDescriptor: name='random_seed', description='Random seed; None uses a system-derived seed.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler( - names=['_minimizer.random_seed'], - iucr_name='_easydiffraction_minimizer.random_seed', + tags=TagSpec( + edi_names=['_minimizer.random_seed'], + cif_names=['_easydiffraction_minimizer.random_seed'], ), ) diff --git a/src/easydiffraction/analysis/categories/minimizer/emcee.py b/src/easydiffraction/analysis/categories/minimizer/emcee.py index 54a2fe576..b2f6bc410 100644 --- a/src/easydiffraction/analysis/categories/minimizer/emcee.py +++ b/src/easydiffraction/analysis/categories/minimizer/emcee.py @@ -30,7 +30,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec @MinimizerCategoryFactory.register @@ -97,9 +97,9 @@ def _initialization_method_descriptor(cls) -> StringDescriptor: default=DEFAULT_INITIALIZATION_METHOD.value, validator=MembershipValidator(allowed=allowed), ), - cif_handler=CifHandler( - names=['_minimizer.initialization_method'], - iucr_name='_easydiffraction_minimizer.initialization_method', + tags=TagSpec( + edi_names=['_minimizer.initialization_method'], + cif_names=['_easydiffraction_minimizer.initialization_method'], ), ) @@ -113,9 +113,9 @@ def _proposal_moves_descriptor() -> StringDescriptor: default=DEFAULT_PROPOSAL_MOVES, validator=MembershipValidator(allowed=SUPPORTED_PROPOSAL_MOVES), ), - cif_handler=CifHandler( - names=['_minimizer.proposal_moves'], - iucr_name='_easydiffraction_minimizer.proposal_moves', + tags=TagSpec( + edi_names=['_minimizer.proposal_moves'], + cif_names=['_easydiffraction_minimizer.proposal_moves'], ), ) diff --git a/src/easydiffraction/analysis/categories/minimizer/lsq_base.py b/src/easydiffraction/analysis/categories/minimizer/lsq_base.py index 1eff06e06..4ea81ef97 100644 --- a/src/easydiffraction/analysis/categories/minimizer/lsq_base.py +++ b/src/easydiffraction/analysis/categories/minimizer/lsq_base.py @@ -12,7 +12,7 @@ from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.validation import RangeValidator from easydiffraction.core.variable import IntegerDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class LeastSquaresMinimizerBase(MinimizerCategoryBase): @@ -39,9 +39,9 @@ def _max_iterations_descriptor(default: int) -> IntegerDescriptor: name='max_iterations', description='Maximum solver iterations.', value_spec=AttributeSpec(default=default, validator=RangeValidator(ge=1)), - cif_handler=CifHandler( - names=['_minimizer.max_iterations'], - iucr_name='_easydiffraction_minimizer.max_iterations', + tags=TagSpec( + edi_names=['_minimizer.max_iterations'], + cif_names=['_easydiffraction_minimizer.max_iterations'], ), display_handler=DisplayHandler( display_name='Maximum iterations', diff --git a/src/easydiffraction/analysis/categories/sequential_fit/default.py b/src/easydiffraction/analysis/categories/sequential_fit/default.py index e95db87d7..d74d03ef1 100644 --- a/src/easydiffraction/analysis/categories/sequential_fit/default.py +++ b/src/easydiffraction/analysis/categories/sequential_fit/default.py @@ -15,7 +15,7 @@ from easydiffraction.core.validation import RegexValidator from easydiffraction.core.variable import BoolDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec @SequentialFitFactory.register @@ -37,18 +37,18 @@ def __init__(self) -> None: name='data_dir', description='Directory containing sequential-fit data files.', value_spec=AttributeSpec(default=''), - cif_handler=CifHandler( - names=['_sequential_fit.data_dir'], - iucr_name='_easydiffraction_sequential_fit.data_dir', + tags=TagSpec( + edi_names=['_sequential_fit.data_dir'], + cif_names=['_easydiffraction_sequential_fit.data_dir'], ), ) self._file_pattern = StringDescriptor( name='file_pattern', description='Glob pattern selecting sequential-fit files.', value_spec=AttributeSpec(default='*'), - cif_handler=CifHandler( - names=['_sequential_fit.file_pattern'], - iucr_name='_easydiffraction_sequential_fit.file_pattern', + tags=TagSpec( + edi_names=['_sequential_fit.file_pattern'], + cif_names=['_easydiffraction_sequential_fit.file_pattern'], ), ) self._max_workers = StringDescriptor( @@ -58,9 +58,9 @@ def __init__(self) -> None: default='1', validator=RegexValidator(pattern=r'^(auto|[1-9]\d*)$'), ), - cif_handler=CifHandler( - names=['_sequential_fit.max_workers'], - iucr_name='_easydiffraction_sequential_fit.max_workers', + tags=TagSpec( + edi_names=['_sequential_fit.max_workers'], + cif_names=['_easydiffraction_sequential_fit.max_workers'], ), ) self._chunk_size = StringDescriptor( @@ -70,18 +70,18 @@ def __init__(self) -> None: default='.', validator=RegexValidator(pattern=r'^([1-9]\d*|\.)$'), ), - cif_handler=CifHandler( - names=['_sequential_fit.chunk_size'], - iucr_name='_easydiffraction_sequential_fit.chunk_size', + tags=TagSpec( + edi_names=['_sequential_fit.chunk_size'], + cif_names=['_easydiffraction_sequential_fit.chunk_size'], ), ) self._reverse = BoolDescriptor( name='reverse', description='Whether to process sequential-fit files in reverse.', value_spec=AttributeSpec(default=False), - cif_handler=CifHandler( - names=['_sequential_fit.reverse'], - iucr_name='_easydiffraction_sequential_fit.reverse', + tags=TagSpec( + edi_names=['_sequential_fit.reverse'], + cif_names=['_easydiffraction_sequential_fit.reverse'], ), ) diff --git a/src/easydiffraction/analysis/categories/sequential_fit_extract/default.py b/src/easydiffraction/analysis/categories/sequential_fit_extract/default.py index 6a9c24cc2..7381a0ccd 100644 --- a/src/easydiffraction/analysis/categories/sequential_fit_extract/default.py +++ b/src/easydiffraction/analysis/categories/sequential_fit_extract/default.py @@ -21,7 +21,7 @@ from easydiffraction.core.validation import RegexValidator from easydiffraction.core.variable import BoolDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec _TARGET_SEGMENT_PATTERN = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*$') _EXTRACT_TARGET_SEGMENTS = 2 @@ -72,36 +72,36 @@ def __init__(self) -> None: default='_', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), - cif_handler=CifHandler( - names=['_sequential_fit_extract.id'], - iucr_name='_easydiffraction_sequential_fit_extract.id', + tags=TagSpec( + edi_names=['_sequential_fit_extract.id'], + cif_names=['_easydiffraction_sequential_fit_extract.id'], ), ) self._target = StringDescriptor( name='target', description='diffrn attribute updated by this extract rule.', value_spec=AttributeSpec(default='diffrn._'), - cif_handler=CifHandler( - names=['_sequential_fit_extract.target'], - iucr_name='_easydiffraction_sequential_fit_extract.target', + tags=TagSpec( + edi_names=['_sequential_fit_extract.target'], + cif_names=['_easydiffraction_sequential_fit_extract.target'], ), ) self._pattern = StringDescriptor( name='pattern', description='Regex used to extract one numeric capture group.', value_spec=AttributeSpec(default='(.*)'), - cif_handler=CifHandler( - names=['_sequential_fit_extract.pattern'], - iucr_name='_easydiffraction_sequential_fit_extract.pattern', + tags=TagSpec( + edi_names=['_sequential_fit_extract.pattern'], + cif_names=['_easydiffraction_sequential_fit_extract.pattern'], ), ) self._required = BoolDescriptor( name='required', description='Whether this extract rule must match every file.', value_spec=AttributeSpec(default=False), - cif_handler=CifHandler( - names=['_sequential_fit_extract.required'], - iucr_name='_easydiffraction_sequential_fit_extract.required', + tags=TagSpec( + edi_names=['_sequential_fit_extract.required'], + cif_names=['_easydiffraction_sequential_fit_extract.required'], ), ) diff --git a/src/easydiffraction/analysis/categories/software/__init__.py b/src/easydiffraction/analysis/categories/software/__init__.py index 715288d60..cf359ad7f 100644 --- a/src/easydiffraction/analysis/categories/software/__init__.py +++ b/src/easydiffraction/analysis/categories/software/__init__.py @@ -7,3 +7,4 @@ from easydiffraction.analysis.categories.software.base import SoftwareRole from easydiffraction.analysis.categories.software.default import Software from easydiffraction.analysis.categories.software.factory import SoftwareFactory +from easydiffraction.analysis.enums import SoftwareRoleEnum diff --git a/src/easydiffraction/analysis/categories/software/base.py b/src/easydiffraction/analysis/categories/software/base.py index 8713c64d5..c22d6009c 100644 --- a/src/easydiffraction/analysis/categories/software/base.py +++ b/src/easydiffraction/analysis/categories/software/base.py @@ -1,49 +1,69 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Shared software-provenance role helpers.""" +"""Software-provenance row items.""" from __future__ import annotations -from easydiffraction.core.guard import GuardedBase +from easydiffraction.analysis.enums import SoftwareRoleEnum +from easydiffraction.core.category import CategoryItem from easydiffraction.core.validation import AttributeSpec +from easydiffraction.core.variable import EnumDescriptor from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec -class SoftwareRole(GuardedBase): +class SoftwareRole(CategoryItem): """Name, version, and URL for one software role.""" - def __init__(self, *, role_name: str, description: str) -> None: + _category_code = 'software' + _category_entry_name = 'id' + + def __init__(self, role: SoftwareRoleEnum | str = 'framework') -> None: """ Create descriptors for one software role. Parameters ---------- - role_name : str - Role prefix used in persisted CIF item names. - description : str - Human-readable role description. + role : SoftwareRoleEnum | str, default='framework' + Software role represented by this row. """ super().__init__() + role_value = SoftwareRoleEnum(role).value + self._id = EnumDescriptor( + name='id', + enum=SoftwareRoleEnum, + description='Software role.', + default=role_value, + tags=TagSpec(edi_names=['_software.id']), + ) self._name = StringDescriptor( - name=f'{role_name}_name', - description=f'{description} name.', + name='name', + description='Software package name.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=[f'_software.{role_name}_name']), + tags=TagSpec(edi_names=['_software.name']), ) self._version = StringDescriptor( - name=f'{role_name}_version', - description=f'{description} version.', + name='version', + description='Software package version.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=[f'_software.{role_name}_version']), + tags=TagSpec(edi_names=['_software.version']), ) self._url = StringDescriptor( - name=f'{role_name}_url', - description=f'{description} URL.', + name='url', + description='Software project URL.', value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=[f'_software.{role_name}_url']), + tags=TagSpec(edi_names=['_software.url']), ) + @property + def id(self) -> EnumDescriptor: + """Software role.""" + return self._id + + def _set_id(self, value: str) -> None: + """Set the software role for restore helpers.""" + self._id.value = value + @property def name(self) -> StringDescriptor: """Software name.""" @@ -77,9 +97,4 @@ def url(self, value: str | None) -> None: @property def parameters(self) -> list[StringDescriptor]: """Descriptors owned by this software role.""" - return [self._name, self._version, self._url] - - @property - def as_cif(self) -> str: - """Return CIF representation of this software role.""" - return '\n'.join(param.as_cif for param in self.parameters) + return [self._id, self._name, self._version, self._url] diff --git a/src/easydiffraction/analysis/categories/software/default.py b/src/easydiffraction/analysis/categories/software/default.py index f99059c88..c2b464a60 100644 --- a/src/easydiffraction/analysis/categories/software/default.py +++ b/src/easydiffraction/analysis/categories/software/default.py @@ -6,82 +6,96 @@ from easydiffraction.analysis.categories.software.base import SoftwareRole from easydiffraction.analysis.categories.software.factory import SoftwareFactory -from easydiffraction.core.category import CategoryItem +from easydiffraction.analysis.enums import SoftwareRoleEnum +from easydiffraction.core.category import CategoryCollection from easydiffraction.core.metadata import TypeInfo -from easydiffraction.core.validation import AttributeSpec -from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler @SoftwareFactory.register -class Software(CategoryItem): +class Software(CategoryCollection): """Software-provenance snapshot for the latest successful fit.""" - _category_code = 'software' - type_info = TypeInfo( tag='default', description='Analysis software provenance category', ) def __init__(self) -> None: - """Initialize the software-role and timestamp descriptors.""" - super().__init__() - self._framework = SoftwareRole( - role_name='framework', - description='EasyDiffraction framework', - ) - self._calculator = SoftwareRole( - role_name='calculator', - description='Calculation engine', - ) - self._minimizer = SoftwareRole( - role_name='minimizer', - description='Minimization engine', - ) - self._timestamp = StringDescriptor( - name='timestamp', - description='UTC timestamp of the fit provenance snapshot.', - value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_software.timestamp']), + """Initialize the role-keyed software provenance collection.""" + super().__init__(item_type=SoftwareRole) + self._ensure_role_rows() + + @staticmethod + def _role_order() -> tuple[SoftwareRoleEnum, ...]: + """Return the canonical software role order.""" + return ( + SoftwareRoleEnum.FRAMEWORK, + SoftwareRoleEnum.CALCULATOR, + SoftwareRoleEnum.MINIMIZER, ) - @property - def framework(self) -> SoftwareRole: - """EasyDiffraction framework provenance.""" - return self._framework - - @property - def calculator(self) -> SoftwareRole: - """Calculation-engine provenance.""" - return self._calculator - - @property - def minimizer(self) -> SoftwareRole: - """Minimization-engine provenance.""" - return self._minimizer - - @property - def timestamp(self) -> StringDescriptor: - """UTC timestamp of the fit provenance snapshot.""" - return self._timestamp - - @timestamp.setter - def timestamp(self, value: str | None) -> None: - """Set the UTC timestamp of the provenance snapshot.""" - self._timestamp.value = value - - @property - def parameters(self) -> list[StringDescriptor]: - """Descriptors owned by this software category.""" - return [ - *self._framework.parameters, - *self._calculator.parameters, - *self._minimizer.parameters, - self._timestamp, - ] - - @property - def as_cif(self) -> str: - """Return CIF representation of this software category.""" - return super().as_cif + def _ensure_role_rows(self) -> None: + """Ensure every supported role has exactly one row.""" + rows_by_role: dict[str, SoftwareRole] = {} + for item in self: + role = SoftwareRoleEnum(item.id.value) + rows_by_role[role.value] = item + + ordered_rows: list[SoftwareRole] = [] + for role in self._role_order(): + item = rows_by_role.get(role.value) + if item is None: + item = SoftwareRole(role) + elif item.id.value != role.value: + item._set_id(role.value) + ordered_rows.append(item) + self._adopt_items(ordered_rows) + + @staticmethod + def _legacy_value(block: object, tag: str) -> str | None: + """Return a legacy scalar software value from a CIF block.""" + values = list(block.find_values(tag)) + return values[0] if values else None + + def _restore_legacy_role_fields(self, block: object) -> None: + """Read beta-window wide software fields into role rows.""" + for role in self._role_order(): + item = self[role.value] + for attr_name in ('name', 'version', 'url'): + value = self._legacy_value(block, f'_software.{role.value}_{attr_name}') + descriptor = getattr(item, attr_name) + if value not in {None, '?', '.'} and descriptor.value is None: + setattr(item, attr_name, value) + + def _restore_legacy_timestamp(self, block: object) -> None: + """ + Move a legacy analysis software timestamp to project metadata. + """ + value = self._legacy_value(block, '_software.timestamp') + if value in {None, '?', '.'}: + return + + analysis = getattr(self, '_parent', None) + if analysis is None: + return + analysis.project.metadata.timestamp = value + + def _after_from_cif(self) -> None: + """Normalize restored rows after loop parsing.""" + self._ensure_role_rows() + + def from_cif(self, block: object) -> None: + """Populate software provenance from Edi or legacy CIF.""" + super().from_cif(block) + self._ensure_role_rows() + self._restore_legacy_role_fields(block) + self._restore_legacy_timestamp(block) + + def has_provenance(self) -> bool: + """Return True when any role contains provenance data.""" + return any( + item.name.value is not None + or item.version.value is not None + or item.url.value is not None + for item in self + ) diff --git a/src/easydiffraction/analysis/enums.py b/src/easydiffraction/analysis/enums.py index e52a8f25e..96a9ba1a2 100644 --- a/src/easydiffraction/analysis/enums.py +++ b/src/easydiffraction/analysis/enums.py @@ -72,3 +72,26 @@ def description(self) -> str: if self is FitCorrelationSourceEnum.POSTERIOR: return 'Correlations from posterior samples.' return '' + + +class SoftwareRoleEnum(StrEnum): + """Role of a software package in the latest fit.""" + + FRAMEWORK = 'framework' + CALCULATOR = 'calculator' + MINIMIZER = 'minimizer' + + @classmethod + def default(cls) -> SoftwareRoleEnum: + """Return the default software role.""" + return cls.FRAMEWORK + + def description(self) -> str: + """Return a human-readable description of this role.""" + if self is SoftwareRoleEnum.FRAMEWORK: + return 'EasyDiffraction framework.' + if self is SoftwareRoleEnum.CALCULATOR: + return 'Calculation engine.' + if self is SoftwareRoleEnum.MINIMIZER: + return 'Minimization engine.' + return '' diff --git a/src/easydiffraction/analysis/fit_helpers/reporting.py b/src/easydiffraction/analysis/fit_helpers/reporting.py index b738c31c5..61c2cdd8b 100644 --- a/src/easydiffraction/analysis/fit_helpers/reporting.py +++ b/src/easydiffraction/analysis/fit_helpers/reporting.py @@ -6,6 +6,7 @@ from easydiffraction.analysis.fit_helpers.metrics import calculate_r_factor_squared from easydiffraction.analysis.fit_helpers.metrics import calculate_rb_factor from easydiffraction.analysis.fit_helpers.metrics import calculate_weighted_r_factor +from easydiffraction.display.links import parameter_docs_link from easydiffraction.utils.logging import console from easydiffraction.utils.utils import print_metrics_table from easydiffraction.utils.utils import print_table_footnote @@ -230,7 +231,7 @@ def _is_uncertainty_large(param: object) -> bool: return param.uncertainty > abs_value -def _build_parameter_row(param: object) -> list[str]: +def _build_parameter_row(param: object) -> list[object]: """ Build a single table row for a fitted parameter. @@ -241,10 +242,10 @@ def _build_parameter_row(param: object) -> list[str]: Returns ------- - list[str] + list[object] Column values for the parameter row. """ - name = getattr(param, 'name', 'N/A') + name = parameter_docs_link(param) start = f'{param._fit_start_value:.4f}' if param._fit_start_value is not None else 'N/A' fitted = f'{param.value:.4f}' if param.value is not None else 'N/A' if getattr(param, '_outside_physical_limits', False): diff --git a/src/easydiffraction/analysis/fitting.py b/src/easydiffraction/analysis/fitting.py index c0cbe3a12..9888cf210 100644 --- a/src/easydiffraction/analysis/fitting.py +++ b/src/easydiffraction/analysis/fitting.py @@ -301,8 +301,8 @@ def _set_minimizer_sidecar_path(self, analysis: object) -> None: if analysis is None or not hasattr(self.minimizer, '_sidecar_path'): return - project_info = getattr(getattr(analysis, 'project', None), 'info', None) - project_path = getattr(project_info, 'path', None) + project_metadata = getattr(getattr(analysis, 'project', None), 'metadata', None) + project_path = getattr(project_metadata, 'path', None) sidecar_path = None if project_path is None else project_path / 'analysis' / 'results.h5' self.minimizer._sidecar_path = sidecar_path @@ -323,7 +323,7 @@ def _validate_resume_parameter_set( ) -> None: """Ensure resume uses the same persisted free-parameter set.""" persisted_names = [ - item.param_unique_name.value for item in getattr(analysis, 'fit_parameters', []) + item.parameter_unique_name.value for item in getattr(analysis, 'fit_parameters', []) ] if not persisted_names: return diff --git a/src/easydiffraction/analysis/sequential.py b/src/easydiffraction/analysis/sequential.py index a6b338051..4d07d7ea5 100644 --- a/src/easydiffraction/analysis/sequential.py +++ b/src/easydiffraction/analysis/sequential.py @@ -55,7 +55,7 @@ class SequentialFitTemplate: structure_cif: str experiment_cif: str initial_params: dict[str, float] - free_param_unique_names: list[str] + free_parameter_unique_names: list[str] alias_defs: list[dict[str, str]] constraint_defs: list[str] constraints_enabled: bool @@ -133,7 +133,7 @@ def _fit_worker_success( result.update(_extract_diffrn_values(expt, data_path, template.diffrn_extract_rules)) _apply_param_overrides(project, template.initial_params) - _set_free_params(project, template.free_param_unique_names) + _set_free_params(project, template.free_parameter_unique_names) if template.constraints_enabled and template.alias_defs: _apply_constraints( project, @@ -231,7 +231,7 @@ def _apply_constraints( project : object The worker's project instance. alias_defs : list[dict[str, str]] - Each dict has ``label`` and ``param_unique_name``. + Each dict has ``id`` and ``parameter_unique_name``. constraint_defs : list[str] Constraint expression strings. """ @@ -239,10 +239,10 @@ def _apply_constraints( by_name = {p.unique_name: p for p in all_params if hasattr(p, 'unique_name')} for alias_def in alias_defs: - param = by_name.get(alias_def['param_unique_name']) + param = by_name.get(alias_def['parameter_unique_name']) if param is not None: project.analysis.aliases.create( - label=alias_def['label'], + id=alias_def['id'], param=param, ) @@ -363,7 +363,7 @@ def _collect_results( # Collect all free parameter values and uncertainties all_params = project.structures.parameters + project.experiments.parameters - free_set = set(template.free_param_unique_names) + free_set = set(template.free_parameter_unique_names) result['params'] = {} for p in all_params: if isinstance(p, Parameter) and p.unique_name in free_set: @@ -404,7 +404,7 @@ def _build_csv_header( """ header = list(_META_COLUMNS) header.extend(f'diffrn.{field}' for field in template.diffrn_field_names) - for name in template.free_param_unique_names: + for name in template.free_parameter_unique_names: header.extend((name, f'{name}.uncertainty')) return header @@ -600,8 +600,8 @@ def _build_template(project: object) -> SequentialFitTemplate: # Collect alias definitions alias_defs: list[dict[str, str]] = [ { - 'label': alias.label.value, - 'param_unique_name': alias.param_unique_name.value, + 'id': alias.id.value, + 'parameter_unique_name': alias.parameter_unique_name.value, } for alias in project.analysis.aliases ] @@ -641,7 +641,7 @@ def _build_template(project: object) -> SequentialFitTemplate: structure_cif=structure.as_cif, experiment_cif=experiment.as_cif, initial_params=initial_params, - free_param_unique_names=free_names, + free_parameter_unique_names=free_names, alias_defs=alias_defs, constraint_defs=constraint_defs, constraints_enabled=project.analysis.constraints.enabled, @@ -1053,7 +1053,7 @@ def _check_seq_preconditions(project: object) -> list[str]: ) raise ValueError(msg) - if project.info.path is None: + if project.metadata.path is None: msg = 'Project must be saved before sequential fitting. Call save_as() first.' raise ValueError(msg) @@ -1091,7 +1091,7 @@ def _setup_csv_and_recovery( tuple[Path, list[str], set[str], SequentialFitTemplate] CSV path, header, already-fitted set, and updated template. """ - csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path = project.metadata.path / 'analysis' / 'results.csv' csv_path.parent.mkdir(parents=True, exist_ok=True) header = _build_csv_header(template) diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 9fe0e7687..ed2162b28 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -25,7 +25,7 @@ from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.posterior import PosteriorParameterSummary - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec # ====================================================================== @@ -104,7 +104,7 @@ def __init__( # Assign default directly. # Skip validation — defaults are trusted. # Callable is needed for dynamic defaults like SpaceGroup - # it_coordinate_system_code, and similar cases. + # coord_system_code, and similar cases. self._value = value_spec.default_value() def __str__(self) -> str: @@ -199,6 +199,11 @@ def display_handler(self) -> DisplayHandler | None: """Optional labels and units for display contexts.""" return self._display_handler + @property + def url(self) -> str: + """Online documentation URL for this persisted descriptor.""" + return self._tags.url + def resolve_display_name(self, context: str) -> str: """ Return the display label for the requested context. @@ -381,7 +386,7 @@ def __init__( self._fit_min = self._fit_min_spec.default self._fit_max_spec = AttributeSpec(data_type=DataTypes.NUMERIC, default=np.inf) self._fit_max = self._fit_max_spec.default - self._fit_bounds_uncertainty_multiplier: float | None = None + self._bounds_uncertainty_multiplier: float | None = None self._start_value_spec = AttributeSpec(data_type=DataTypes.NUMERIC, default=0.0) self._start_value = self._start_value_spec.default self._user_constrained_spec = self._BOOL_SPEC_TEMPLATE @@ -527,7 +532,7 @@ def fit_min(self, v: float) -> None: self._fit_min = self._fit_min_spec.validated( v, name=f'{self.unique_name}.fit_min', current=self._fit_min ) - self._fit_bounds_uncertainty_multiplier = None + self._bounds_uncertainty_multiplier = None @property def fit_max(self) -> float: @@ -540,18 +545,18 @@ def fit_max(self, v: float) -> None: self._fit_max = self._fit_max_spec.validated( v, name=f'{self.unique_name}.fit_max', current=self._fit_max ) - self._fit_bounds_uncertainty_multiplier = None + self._bounds_uncertainty_multiplier = None @property - def fit_bounds_uncertainty_multiplier(self) -> float | None: + def bounds_uncertainty_multiplier(self) -> float | None: """ Multiplier used for uncertainty-derived fit bounds, if known. """ - return self._fit_bounds_uncertainty_multiplier + return self._bounds_uncertainty_multiplier - def _set_fit_bounds_uncertainty_multiplier(self, value: float | None) -> None: + def _set_bounds_uncertainty_multiplier(self, value: float | None) -> None: """Set the cached uncertainty-derived fit-bounds multiplier.""" - self._fit_bounds_uncertainty_multiplier = value + self._bounds_uncertainty_multiplier = value def set_fit_bounds_from_uncertainty( self, @@ -617,7 +622,7 @@ def set_fit_bounds_from_uncertainty( self.fit_min = lower self.fit_max = upper - self._fit_bounds_uncertainty_multiplier = resolved_multiplier + self._bounds_uncertainty_multiplier = resolved_multiplier # ====================================================================== @@ -629,7 +634,7 @@ class StringDescriptor(GenericStringDescriptor): def __init__( self, *, - cif_handler: CifHandler, + tags: TagSpec, **kwargs: object, ) -> None: """ @@ -637,14 +642,14 @@ def __init__( Parameters ---------- - cif_handler : CifHandler + tags : TagSpec Object that tracks CIF identifiers. **kwargs : object Forwarded to GenericStringDescriptor. """ super().__init__(**kwargs) - self._cif_handler = cif_handler - self._cif_handler.attach(self) + self._tags = tags + self._tags.attach(self) # ====================================================================== @@ -665,7 +670,7 @@ def __init__( *, name: str, enum: type[StrEnum], - cif_handler: CifHandler, + tags: TagSpec, description: str | None = None, default: str | None = None, display_handler: DisplayHandler | None = None, @@ -680,7 +685,7 @@ def __init__( enum : type[StrEnum] The ``(str, Enum)`` class whose members are the allowed values. - cif_handler : CifHandler + tags : TagSpec Object that tracks CIF identifiers. description : str | None, default=None Optional human-readable description. @@ -700,7 +705,7 @@ def __init__( name=name, description=description, value_spec=value_spec, - cif_handler=cif_handler, + tags=tags, display_handler=display_handler, ) @@ -739,7 +744,7 @@ class BoolDescriptor(GenericBoolDescriptor): def __init__( self, *, - cif_handler: CifHandler, + tags: TagSpec, **kwargs: object, ) -> None: """ @@ -747,14 +752,14 @@ def __init__( Parameters ---------- - cif_handler : CifHandler + tags : TagSpec Object that tracks CIF identifiers. **kwargs : object Forwarded to GenericBoolDescriptor. """ super().__init__(**kwargs) - self._cif_handler = cif_handler - self._cif_handler.attach(self) + self._tags = tags + self._tags.attach(self) # ====================================================================== @@ -766,7 +771,7 @@ class NumericDescriptor(GenericNumericDescriptor): def __init__( self, *, - cif_handler: CifHandler, + tags: TagSpec, **kwargs: object, ) -> None: """ @@ -774,14 +779,14 @@ def __init__( Parameters ---------- - cif_handler : CifHandler + tags : TagSpec Object that tracks CIF identifiers. **kwargs : object Forwarded to GenericNumericDescriptor. """ super().__init__(**kwargs) - self._cif_handler = cif_handler - self._cif_handler.attach(self) + self._tags = tags + self._tags.attach(self) # ====================================================================== @@ -793,7 +798,7 @@ class IntegerDescriptor(GenericIntegerDescriptor): def __init__( self, *, - cif_handler: CifHandler, + tags: TagSpec, **kwargs: object, ) -> None: """ @@ -801,14 +806,14 @@ def __init__( Parameters ---------- - cif_handler : CifHandler + tags : TagSpec Object that tracks CIF identifiers. **kwargs : object Forwarded to GenericIntegerDescriptor. """ super().__init__(**kwargs) - self._cif_handler = cif_handler - self._cif_handler.attach(self) + self._tags = tags + self._tags.attach(self) # ====================================================================== @@ -820,7 +825,7 @@ class Parameter(GenericParameter): def __init__( self, *, - cif_handler: CifHandler, + tags: TagSpec, **kwargs: object, ) -> None: """ @@ -828,11 +833,11 @@ def __init__( Parameters ---------- - cif_handler : CifHandler + tags : TagSpec Object that tracks CIF identifiers. **kwargs : object Forwarded to GenericParameter. """ super().__init__(**kwargs) - self._cif_handler = cif_handler - self._cif_handler.attach(self) + self._tags = tags + self._tags.attach(self) diff --git a/src/easydiffraction/datablocks/experiment/categories/absorption/base.py b/src/easydiffraction/datablocks/experiment/categories/absorption/base.py index 8617bfbac..d3599707f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/absorption/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/absorption/base.py @@ -11,7 +11,7 @@ from easydiffraction.core.variable import StringDescriptor from easydiffraction.datablocks.experiment.categories.absorption.factory import AbsorptionFactory from easydiffraction.datablocks.experiment.item.enums import AbsorptionTypeEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class AbsorptionBase(CategoryItem, SwitchableCategoryBase): @@ -35,9 +35,9 @@ def __init__(self) -> None: allowed=[member.value for member in AbsorptionTypeEnum], ), ), - cif_handler=CifHandler( - names=['_absorption.type'], - iucr_name='_easydiffraction_absorption.type', + tags=TagSpec( + edi_names=['_absorption.type'], + cif_names=['_easydiffraction_absorption.type'], ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/absorption/cylinder_hewat.py b/src/easydiffraction/datablocks/experiment/categories/absorption/cylinder_hewat.py index f65ea342b..7ae249ab1 100644 --- a/src/easydiffraction/datablocks/experiment/categories/absorption/cylinder_hewat.py +++ b/src/easydiffraction/datablocks/experiment/categories/absorption/cylinder_hewat.py @@ -18,7 +18,7 @@ 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.io.cif.handler import TagSpec @AbsorptionFactory.register @@ -64,9 +64,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0.0), ), - cif_handler=CifHandler( - names=['_absorption.mu_r'], - iucr_name='_easydiffraction_absorption.mu_r', + tags=TagSpec( + edi_names=['_absorption.mu_r'], + cif_names=['_easydiffraction_absorption.mu_r'], ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/background/base.py b/src/easydiffraction/datablocks/experiment/categories/background/base.py index 1fade4ced..c61eddeca 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/base.py @@ -14,7 +14,7 @@ from easydiffraction.core.variable import StringDescriptor from easydiffraction.datablocks.experiment.categories.background.enums import BackgroundTypeEnum from easydiffraction.datablocks.experiment.categories.background.factory import BackgroundFactory -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class BackgroundBase(CategoryCollection, SwitchableCategoryBase): @@ -47,9 +47,8 @@ def __init__(self, item_type: type) -> None: allowed=[member.value for member in BackgroundTypeEnum], ), ), - cif_handler=CifHandler( - names=['_background.type'], - iucr_name='_easydiffraction_background.type', + tags=TagSpec( + edi_names=['_background.type'], cif_names=['_easydiffraction_background.type'] ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py index 77e109c8d..0c6316fe5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/chebyshev.py @@ -25,7 +25,7 @@ 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.io.cif.handler import TagSpec from easydiffraction.utils.logging import console from easydiffraction.utils.logging import log from easydiffraction.utils.utils import render_table @@ -57,7 +57,7 @@ def __init__(self) -> None: # Do we need conversion between CIF and internal label? validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'), ), - cif_handler=CifHandler(names=['_pd_background.id']), + tags=TagSpec(edi_names=['_background.id'], cif_names=['_pd_background.id']), ) self._order = NumericDescriptor( name='order', @@ -66,7 +66,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_pd_background.Chebyshev_order']), + tags=TagSpec( + edi_names=['_background.order'], cif_names=['_pd_background.Chebyshev_order'] + ), ) self._coef = Parameter( name='coef', @@ -75,7 +77,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_pd_background.Chebyshev_coef']), + tags=TagSpec( + edi_names=['_background.coef'], cif_names=['_pd_background.Chebyshev_coef'] + ), ) # ------------------------------------------------------------------ diff --git a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py index 79540795d..457fc84bd 100644 --- a/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py +++ b/src/easydiffraction/datablocks/experiment/categories/background/line_segment.py @@ -30,7 +30,7 @@ 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.io.cif.handler import TagSpec from easydiffraction.utils.logging import console from easydiffraction.utils.logging import log from easydiffraction.utils.utils import render_table @@ -57,42 +57,41 @@ def __init__(self) -> None: # Do we need conversion between CIF and internal label? validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'), ), - cif_handler=CifHandler(names=['_pd_background.id']), + tags=TagSpec(edi_names=['_background.id'], cif_names=['_pd_background.id']), display_handler=DisplayHandler( display_name='ID', latex_name='ID', ), ) - self._x = NumericDescriptor( - name='x', - description='X-coordinates used to create many straight-line segments', + self._position = NumericDescriptor( + name='position', + description='Position used to create many straight-line segments', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=[ - '_pd_background.line_segment_X', - '_pd_background_line_segment_X', - ] + tags=TagSpec( + edi_names=['_background.position'], + cif_names=['_pd_background.line_segment_X', '_pd_background_line_segment_X'], ), display_handler=DisplayHandler( - display_name='x', + display_name='Position', latex_name='$x$', ), ) - self._y = Parameter( - name='y', # TODO: rename to intensity + self._intensity = Parameter( + name='intensity', description='Intensity used to create many straight-line segments', value_spec=AttributeSpec( default=0.0, validator=RangeValidator(), - ), # TODO: rename to intensity - cif_handler=CifHandler( - names=[ + ), + tags=TagSpec( + edi_names=['_background.intensity'], + cif_names=[ '_pd_background.line_segment_intensity', '_pd_background_line_segment_intensity', - ] + ], ), display_handler=DisplayHandler( display_name='Intensity', @@ -120,33 +119,33 @@ def id(self, value: str) -> None: self._id.value = value @property - def x(self) -> NumericDescriptor: + def position(self) -> NumericDescriptor: """ - X-coordinates used to create many straight-line segments. + Position 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 + return self._position - @x.setter - def x(self, value: float) -> None: - self._x.value = value + @position.setter + def position(self, value: float) -> None: + self._position.value = value @property - def y(self) -> Parameter: + def intensity(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 + return self._intensity - @y.setter - def y(self, value: float) -> None: - self._y.value = value + @intensity.setter + def intensity(self, value: float) -> None: + self._intensity.value = value def _resolve_method(method: str) -> str: @@ -279,8 +278,8 @@ def _update( data._set_intensity_bkg(np.zeros_like(x)) return - segments_x = np.array([point.x.value for point in self._items]) - segments_y = np.array([point.y.value for point in self._items]) + segments_x = np.array([point.position.value for point in self._items]) + segments_y = np.array([point.intensity.value for point in self._items]) interp_func = interp1d( segments_x, segments_y, @@ -362,9 +361,9 @@ def auto_estimate( log.info('Replacing existing background points with a new estimate.') self.clear() for index, (point_x, height) in enumerate(zip(anchor_x, heights, strict=True), start=1): - self.create(id=str(index), x=float(point_x), y=float(height)) + self.create(id=str(index), position=float(point_x), intensity=float(height)) for point in self._items: - point.y.free = False + point.intensity.free = False count = len(self) width_pts = result.width @@ -375,7 +374,9 @@ def show(self) -> None: """Print a table of control points (x, intensity).""" columns_headers: list[str] = ['X', 'Intensity'] columns_alignment = ['left', 'left'] - columns_data: list[list[float]] = [[p.x.value, p.y.value] for p in self._items] + columns_data: list[list[float]] = [ + [p.position.value, p.intensity.value] for p in self._items + ] console.paragraph('Line-segment background points') render_table( diff --git a/src/easydiffraction/datablocks/experiment/categories/calculator/default.py b/src/easydiffraction/datablocks/experiment/categories/calculator/default.py index 25e430bff..f45c8747f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/calculator/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/calculator/default.py @@ -15,7 +15,7 @@ CalculatorCategoryFactory, ) from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.io.cif.parse import read_cif_str @@ -48,9 +48,8 @@ def __init__( allowed=[member.value for member in CalculatorEnum], ), ), - cif_handler=CifHandler( - names=['_calculator.type'], - iucr_name='_easydiffraction_calculator.type', + tags=TagSpec( + edi_names=['_calculator.type'], cif_names=['_easydiffraction_calculator.type'] ), display_handler=DisplayHandler( display_name='Type', diff --git a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py index 14cbd0ee2..bc2db4949 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/bragg_pd.py @@ -25,7 +25,7 @@ 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.io.cif.handler import TagSpec from easydiffraction.utils.logging import log from easydiffraction.utils.utils import tof_to_d from easydiffraction.utils.utils import twotheta_to_d @@ -47,8 +47,8 @@ class PdDataPointBaseMixin: def __init__(self) -> None: super().__init__() - self._point_id = StringDescriptor( - name='point_id', + self._id = StringDescriptor( + name='id', description='Identifier for this data point in the dataset', display_handler=DisplayHandler( display_name='ID', @@ -61,11 +61,7 @@ def __init__(self) -> None: # 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', - ] - ), + tags=TagSpec(edi_names=['_data.id'], cif_names=['_pd_data.point_id']), ) self._d_spacing = NumericDescriptor( name='d_spacing', @@ -81,7 +77,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_pd_proc.d_spacing']), + tags=TagSpec(edi_names=['_data.d_spacing'], cif_names=['_pd_proc.d_spacing']), ) self._intensity_meas = NumericDescriptor( name='intensity_meas', @@ -94,11 +90,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler( - names=[ - '_pd_meas.intensity_total', - '_pd_proc.intensity_norm', - ] + tags=TagSpec( + edi_names=['_data.intensity_meas'], + cif_names=['_pd_meas.intensity_total', '_pd_proc.intensity_norm'], ), ) self._intensity_meas_su = NumericDescriptor( @@ -112,11 +106,9 @@ def __init__(self) -> None: default=1.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler( - names=[ - '_pd_meas.intensity_total_su', - '_pd_proc.intensity_norm_su', - ] + tags=TagSpec( + edi_names=['_data.intensity_meas_su'], + cif_names=['_pd_meas.intensity_total_su', '_pd_proc.intensity_norm_su'], ), ) self._intensity_calc = NumericDescriptor( @@ -130,7 +122,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_pd_calc.intensity_total']), + tags=TagSpec( + edi_names=['_data.intensity_calc'], cif_names=['_pd_calc.intensity_total'] + ), ) self._intensity_bkg = NumericDescriptor( name='intensity_bkg', @@ -143,7 +137,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_pd_calc.intensity_bkg']), + tags=TagSpec(edi_names=['_data.intensity_bkg'], cif_names=['_pd_calc.intensity_bkg']), ) self._calc_status = StringDescriptor( name='calc_status', @@ -156,10 +150,8 @@ def __init__(self) -> None: default='incl', # TODO: Make Enum validator=MembershipValidator(allowed=['incl', 'excl']), ), - cif_handler=CifHandler( - names=[ - '_pd_data.refinement_status', # TODO: rename to calc_status - ] + tags=TagSpec( + edi_names=['_data.calc_status'], cif_names=['_pd_data.refinement_status'] ), ) @@ -168,14 +160,14 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def point_id(self) -> StringDescriptor: + def id(self) -> StringDescriptor: """ Identifier for this data point in the dataset. Reading this property returns the underlying ``StringDescriptor`` object. """ - return self._point_id + return self._id @property def d_spacing(self) -> NumericDescriptor: @@ -258,11 +250,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler( - names=[ - '_pd_proc.2theta_scan', - '_pd_meas.2theta_scan', - ] + tags=TagSpec( + edi_names=['_data.two_theta'], + cif_names=['_pd_proc.2theta_scan', '_pd_meas.2theta_scan'], ), ) @@ -301,7 +291,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_pd_meas.time_of_flight']), + tags=TagSpec( + edi_names=['_data.time_of_flight'], cif_names=['_pd_meas.time_of_flight'] + ), ) # ------------------------------------------------------------------ @@ -334,8 +326,8 @@ class PdCwlDataPoint( ): """Powder diffraction data point for CWL experiments.""" - _category_code = 'pd_data' - _category_entry_name = 'point_id' + _category_code = 'data' + _category_entry_name = 'id' def __init__(self) -> None: super().__init__() @@ -348,8 +340,8 @@ class PdTofDataPoint( ): """Powder diffraction data point for time-of-flight experiments.""" - _category_code = 'pd_data' - _category_entry_name = 'point_id' + _category_code = 'data' + _category_entry_name = 'id' def __init__(self) -> None: super().__init__() @@ -372,10 +364,10 @@ class PdDataBase(CategoryCollection): # Should be set only once - def _set_point_id(self, values: object) -> None: - """Set point IDs.""" + def _set_id(self, values: object) -> None: + """Set data-point IDs.""" for p, v in zip(self._items, values, strict=True): - p.point_id._value = v + p.id._value = v def _set_intensity_meas(self, values: object) -> None: """Set measured intensity.""" @@ -570,14 +562,14 @@ def _phase_calculation_results( refln_records: list[PowderReflnRecord] = [] missing_refln_records = False - for linked_phase in experiment._get_valid_linked_phases(structures): - structure_id = linked_phase._identity.category_entry_name + for linked_structure in experiment._get_valid_linked_structures(structures): + structure_id = linked_structure._identity.category_entry_name structure = structures[structure_id] structure_scaled_calc, structure_refln_records = self._phase_result( structure=structure, experiment=experiment, calculator=calculator, - linked_phase=linked_phase, + linked_structure=linked_structure, called_by_minimizer=called_by_minimizer, collect_refln_records=collect_refln_records, ) @@ -597,7 +589,7 @@ def _phase_result( structure: object, experiment: object, calculator: object, - linked_phase: object, + linked_structure: object, called_by_minimizer: bool, collect_refln_records: bool, ) -> tuple[np.ndarray, list[PowderReflnRecord] | None]: @@ -606,14 +598,14 @@ def _phase_result( experiment, called_by_minimizer=called_by_minimizer, ) - structure_scaled_calc = linked_phase.scale.value * structure_calc + structure_scaled_calc = linked_structure.scale.value * structure_calc if not collect_refln_records: return structure_scaled_calc, [] structure_refln_records = calculator.last_powder_refln_records( structure, experiment, - phase_id=linked_phase.id.value, + structure_id=linked_structure.structure_id.value, ) return structure_scaled_calc, structure_refln_records @@ -740,7 +732,7 @@ def _create_items_set_xcoord_and_id(self, values: object) -> None: p.two_theta._value = v # Set point IDs - self._set_point_id([str(i + 1) for i in range(values.size)]) + self._set_id([str(i + 1) for i in range(values.size)]) # Misc @@ -826,7 +818,7 @@ def _create_items_set_xcoord_and_id(self, values: object) -> None: p.time_of_flight._value = v # Set point IDs - self._set_point_id([str(i + 1) for i in range(values.size)]) + self._set_id([str(i + 1) for i in range(values.size)]) # Misc @@ -842,7 +834,7 @@ def _update( 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, + experiment.instrument.calib_d_to_tof_quadratic.value, ) self._set_d_spacing(d_spacing) diff --git a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py index 686189818..6655fa886 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/data/total_pd.py @@ -23,7 +23,7 @@ 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.io.cif.handler import TagSpec from easydiffraction.utils.logging import log @@ -35,24 +35,20 @@ class TotalDataPoint(CategoryItem): original measurement was CWL or TOF. """ - _category_code = 'total_data' - _category_entry_name = 'point_id' + _category_code = 'data' + _category_entry_name = 'id' def __init__(self) -> None: super().__init__() - self._point_id = StringDescriptor( - name='point_id', + self._id = StringDescriptor( + name='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 - ] - ), + tags=TagSpec(edi_names=['_data.id'], cif_names=['_pd_data.point_id']), ) self._r = NumericDescriptor( name='r', @@ -66,11 +62,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler( - names=[ - '_pd_proc.r', # TODO: Use PDF-specific CIF names - ] - ), + tags=TagSpec(edi_names=['_data.r'], cif_names=['_pd_proc.r']), ) self._g_r_meas = NumericDescriptor( name='g_r_meas', @@ -78,11 +70,7 @@ def __init__(self) -> None: value_spec=AttributeSpec( default=0.0, ), - cif_handler=CifHandler( - names=[ - '_pd_meas.intensity_total', # TODO: Use PDF-specific CIF names - ] - ), + tags=TagSpec(edi_names=['_data.g_r_meas'], cif_names=['_pd_meas.intensity_total']), ) self._g_r_meas_su = NumericDescriptor( name='g_r_meas_su', @@ -91,10 +79,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler( - names=[ - '_pd_meas.intensity_total_su', # TODO: Use PDF-specific CIF names - ] + tags=TagSpec( + edi_names=['_data.g_r_meas_su'], cif_names=['_pd_meas.intensity_total_su'] ), ) self._g_r_calc = NumericDescriptor( @@ -103,11 +89,7 @@ def __init__(self) -> None: value_spec=AttributeSpec( default=0.0, ), - cif_handler=CifHandler( - names=[ - '_pd_calc.intensity_total', # TODO: Use PDF-specific CIF names - ] - ), + tags=TagSpec(edi_names=['_data.g_r_calc'], cif_names=['_pd_calc.intensity_total']), ) self._calc_status = StringDescriptor( name='calc_status', @@ -116,10 +98,8 @@ def __init__(self) -> None: default='incl', validator=MembershipValidator(allowed=['incl', 'excl']), ), - cif_handler=CifHandler( - names=[ - '_pd_data.refinement_status', # TODO: Use PDF-specific CIF names - ] + tags=TagSpec( + edi_names=['_data.calc_status'], cif_names=['_pd_data.refinement_status'] ), ) @@ -128,14 +108,14 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def point_id(self) -> StringDescriptor: + def id(self) -> StringDescriptor: """ Identifier for this data point in the dataset. Reading this property returns the underlying ``StringDescriptor`` object. """ - return self._point_id + return self._id @property def r(self) -> NumericDescriptor: @@ -199,10 +179,10 @@ class TotalDataBase(CategoryCollection): # Should be set only once - def _set_point_id(self, values: object) -> None: - """Set point IDs.""" + def _set_id(self, values: object) -> None: + """Set data-point IDs.""" for p, v in zip(self._items, values, strict=True): - p.point_id._value = v + p.id._value = v def _set_g_r_meas(self, values: object) -> None: """Set measured G(r).""" @@ -266,13 +246,13 @@ def _update( 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: refactor _get_valid_linked_structures 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 + for linked_structure in experiment._get_valid_linked_structures(structures): + structure_id = linked_structure._identity.category_entry_name + structure_scale = linked_structure.scale.value structure = structures[structure_id] structure_calc = calculator.calculate_pattern( @@ -399,7 +379,7 @@ def _create_items_set_xcoord_and_id(self, values: object) -> None: p.r._value = v # Set point IDs - self._set_point_id([str(i + 1) for i in range(values.size)]) + self._set_id([str(i + 1) for i in range(values.size)]) # ------------------------------------------------------------------ # Public properties diff --git a/src/easydiffraction/datablocks/experiment/categories/data_range/cwl.py b/src/easydiffraction/datablocks/experiment/categories/data_range/cwl.py index 651449aae..917f68d8b 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data_range/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/data_range/cwl.py @@ -18,7 +18,7 @@ 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.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.utils.utils import twotheta_to_d # Bragg geometry caps sin(θ) at just under 1 so the 2θ projection of a @@ -72,7 +72,9 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler(names=['_pd_meas.2theta_range_min']), + tags=TagSpec( + edi_names=['_data_range.two_theta_min'], cif_names=['_pd_meas.2theta_range_min'] + ), ) self._two_theta_max = NumericDescriptor( name='two_theta_max', @@ -88,7 +90,9 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler(names=['_pd_meas.2theta_range_max']), + tags=TagSpec( + edi_names=['_data_range.two_theta_max'], cif_names=['_pd_meas.2theta_range_max'] + ), ) self._two_theta_inc = NumericDescriptor( name='two_theta_inc', @@ -104,7 +108,9 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(gt=0, le=180), ), - cif_handler=CifHandler(names=['_pd_meas.2theta_range_inc']), + tags=TagSpec( + edi_names=['_data_range.two_theta_inc'], cif_names=['_pd_meas.2theta_range_inc'] + ), ) # ------------------------------------------------------------------ diff --git a/src/easydiffraction/datablocks/experiment/categories/data_range/sc.py b/src/easydiffraction/datablocks/experiment/categories/data_range/sc.py index d00b4acb4..1cccd5fb8 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data_range/sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/data_range/sc.py @@ -17,7 +17,7 @@ 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.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec @DataRangeFactory.register @@ -63,7 +63,10 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.sin_theta_over_lambda_range_min']), + tags=TagSpec( + edi_names=['_data_range.sin_theta_over_lambda_min'], + cif_names=['_refln.sin_theta_over_lambda_range_min'], + ), ) self._sin_theta_over_lambda_max = NumericDescriptor( name='sin_theta_over_lambda_max', @@ -79,7 +82,10 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.sin_theta_over_lambda_range_max']), + tags=TagSpec( + edi_names=['_data_range.sin_theta_over_lambda_max'], + cif_names=['_refln.sin_theta_over_lambda_range_max'], + ), ) # ------------------------------------------------------------------ diff --git a/src/easydiffraction/datablocks/experiment/categories/data_range/tof.py b/src/easydiffraction/datablocks/experiment/categories/data_range/tof.py index 954d277a8..3029b628a 100644 --- a/src/easydiffraction/datablocks/experiment/categories/data_range/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/data_range/tof.py @@ -20,7 +20,7 @@ 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.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.utils.utils import tof_to_d @@ -64,7 +64,10 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_pd_meas.time_of_flight_range_min']), + tags=TagSpec( + edi_names=['_data_range.time_of_flight_min'], + cif_names=['_pd_meas.time_of_flight_range_min'], + ), ) self._time_of_flight_max = NumericDescriptor( name='time_of_flight_max', @@ -80,7 +83,10 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_pd_meas.time_of_flight_range_max']), + tags=TagSpec( + edi_names=['_data_range.time_of_flight_max'], + cif_names=['_pd_meas.time_of_flight_range_max'], + ), ) self._time_of_flight_inc = NumericDescriptor( name='time_of_flight_inc', @@ -96,7 +102,10 @@ def __init__(self) -> None: default=np.nan, validator=RangeValidator(gt=0), ), - cif_handler=CifHandler(names=['_pd_meas.time_of_flight_range_inc']), + tags=TagSpec( + edi_names=['_data_range.time_of_flight_inc'], + cif_names=['_pd_meas.time_of_flight_range_inc'], + ), ) # ------------------------------------------------------------------ @@ -104,24 +113,31 @@ def __init__(self) -> None: # ------------------------------------------------------------------ def _tof_calibration(self) -> tuple[float, float, float] | None: - """Return ``(offset, linear, quad)`` calibration, or None.""" + """ + Return ``(offset, linear, quadratic)`` calibration, or None. + """ instrument = self._instrument() if instrument is None: return None return ( instrument.calib_d_to_tof_offset.value, instrument.calib_d_to_tof_linear.value, - instrument.calib_d_to_tof_quad.value, + instrument.calib_d_to_tof_quadratic.value, ) @staticmethod - def _tof_from_d(d_spacing: float, offset: float, linear: float, quad: float) -> float: + def _tof_from_d( + d_spacing: float, + offset: float, + linear: float, + quadratic: float, + ) -> float: """ Return time-of-flight (μs) for a d-spacing. ``TOF = c0+c1·d+c2·d²``. """ - return float(offset + linear * d_spacing + quad * d_spacing**2) + return float(offset + linear * d_spacing + quadratic * d_spacing**2) def _ensure_default_range(self) -> None: """ @@ -130,14 +146,14 @@ def _ensure_default_range(self) -> None: calibration = self._tof_calibration() if calibration is None: return - offset, linear, quad = calibration + offset, linear, quadratic = calibration if np.isnan(self._time_of_flight_min.value): self._time_of_flight_min._value = self._tof_from_d( - DEFAULT_D_SPACING_MIN, offset, linear, quad + DEFAULT_D_SPACING_MIN, offset, linear, quadratic ) if np.isnan(self._time_of_flight_max.value): self._time_of_flight_max._value = self._tof_from_d( - DEFAULT_D_SPACING_MAX, offset, linear, quad + DEFAULT_D_SPACING_MAX, offset, linear, quadratic ) if np.isnan(self._time_of_flight_inc.value): span = self._time_of_flight_max.value - self._time_of_flight_min.value diff --git a/src/easydiffraction/datablocks/experiment/categories/diffrn/default.py b/src/easydiffraction/datablocks/experiment/categories/diffrn/default.py index 13396af9d..0971059d0 100644 --- a/src/easydiffraction/datablocks/experiment/categories/diffrn/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/diffrn/default.py @@ -11,7 +11,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec @DiffrnFactory.register @@ -43,7 +43,7 @@ def __init__(self) -> None: allow_none=True, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_diffrn.ambient_temperature']), + tags=TagSpec(edi_names=['_diffrn.ambient_temperature']), ) self._ambient_pressure = NumericDescriptor( @@ -61,7 +61,7 @@ def __init__(self) -> None: allow_none=True, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_diffrn.ambient_pressure']), + tags=TagSpec(edi_names=['_diffrn.ambient_pressure']), ) self._ambient_magnetic_field = NumericDescriptor( @@ -79,9 +79,9 @@ def __init__(self) -> None: allow_none=True, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_diffrn.ambient_magnetic_field'], - iucr_name='_easydiffraction_diffrn.ambient_magnetic_field', + tags=TagSpec( + edi_names=['_diffrn.ambient_magnetic_field'], + cif_names=['_easydiffraction_diffrn.ambient_magnetic_field'], ), ) @@ -100,9 +100,9 @@ def __init__(self) -> None: allow_none=True, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_diffrn.ambient_electric_field'], - iucr_name='_easydiffraction_diffrn.ambient_electric_field', + tags=TagSpec( + edi_names=['_diffrn.ambient_electric_field'], + cif_names=['_easydiffraction_diffrn.ambient_electric_field'], ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py index c58fb58a0..609afd8aa 100644 --- a/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/excluded_regions/default.py @@ -19,7 +19,7 @@ ExcludedRegionsFactory, ) from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.utils.logging import console from easydiffraction.utils.utils import render_table @@ -33,7 +33,7 @@ class ExcludedRegion(CategoryItem): def __init__(self) -> None: super().__init__() - # TODO: Add point_id as for the background + # TODO: Add id as for the background self._id = StringDescriptor( name='id', description='Identifier for this excluded region', @@ -44,9 +44,9 @@ def __init__(self) -> None: # Do we need conversion between CIF and internal label? validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'), ), - cif_handler=CifHandler( - names=['_excluded_region.id'], - iucr_name='_easydiffraction_excluded_region.id', + tags=TagSpec( + edi_names=['_excluded_region.id'], + cif_names=['_easydiffraction_excluded_region.id'], ), ) self._start = NumericDescriptor( @@ -56,9 +56,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_excluded_region.start'], - iucr_name='_easydiffraction_excluded_region.start', + tags=TagSpec( + edi_names=['_excluded_region.start'], + cif_names=['_easydiffraction_excluded_region.start'], ), ) self._end = NumericDescriptor( @@ -68,9 +68,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_excluded_region.end'], - iucr_name='_easydiffraction_excluded_region.end', + tags=TagSpec( + edi_names=['_excluded_region.end'], + cif_names=['_easydiffraction_excluded_region.end'], ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py index 958fcb8f4..b0a425d04 100644 --- a/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/experiment_type/default.py @@ -4,8 +4,7 @@ Experiment type descriptor (form, beam, probe, scattering). This lightweight container stores the categorical attributes defining an -experiment configuration and handles CIF serialization via -``CifHandler``. +experiment configuration and handles CIF serialization via ``TagSpec``. """ from __future__ import annotations @@ -21,14 +20,14 @@ 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 +from easydiffraction.io.cif.handler import TagSpec @ExperimentTypeFactory.register class ExperimentType(CategoryItem): """Container of attributes defining the experiment type.""" - _category_code = 'expt_type' + _category_code = 'experiment_type' type_info = TypeInfo( tag='default', @@ -42,9 +41,12 @@ def __init__(self) -> None: name='sample_form', enum=SampleFormEnum, description='Powder diffraction or single crystal diffraction', - cif_handler=CifHandler( - names=['_expt_type.sample_form'], - iucr_name='_easydiffraction_experiment_type.sample_form', + tags=TagSpec( + edi_names=['_experiment_type.sample_form'], + cif_names=[ + '_easydiffraction_experiment_type.sample_form', + '_expt_type.sample_form', + ], ), display_handler=DisplayHandler( display_name='Sample form', @@ -56,9 +58,9 @@ def __init__(self) -> None: name='beam_mode', enum=BeamModeEnum, description='Constant wavelength (CW) or time-of-flight (TOF) measurement', - cif_handler=CifHandler( - names=['_expt_type.beam_mode'], - iucr_name='_easydiffraction_experiment_type.beam_mode', + tags=TagSpec( + edi_names=['_experiment_type.beam_mode'], + cif_names=['_easydiffraction_experiment_type.beam_mode', '_expt_type.beam_mode'], ), display_handler=DisplayHandler( display_name='Beam mode', @@ -69,9 +71,12 @@ def __init__(self) -> None: name='radiation_probe', enum=RadiationProbeEnum, description='Neutron or X-ray diffraction measurement', - cif_handler=CifHandler( - names=['_expt_type.radiation_probe'], - iucr_name='_easydiffraction_experiment_type.radiation_probe', + tags=TagSpec( + edi_names=['_experiment_type.radiation_probe'], + cif_names=[ + '_easydiffraction_experiment_type.radiation_probe', + '_expt_type.radiation_probe', + ], ), display_handler=DisplayHandler( display_name='Probe', @@ -82,9 +87,12 @@ def __init__(self) -> None: name='scattering_type', enum=ScatteringTypeEnum, description='Conventional Bragg diffraction or total scattering (PDF)', - cif_handler=CifHandler( - names=['_expt_type.scattering_type'], - iucr_name='_easydiffraction_experiment_type.scattering_type', + tags=TagSpec( + edi_names=['_experiment_type.scattering_type'], + cif_names=[ + '_easydiffraction_experiment_type.scattering_type', + '_expt_type.scattering_type', + ], ), display_handler=DisplayHandler( display_name='Scattering type', diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/base.py b/src/easydiffraction/datablocks/experiment/categories/extinction/base.py index 683e95705..fded298c9 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/base.py @@ -10,7 +10,7 @@ from easydiffraction.core.validation import MembershipValidator from easydiffraction.core.variable import StringDescriptor from easydiffraction.datablocks.experiment.categories.extinction.factory import ExtinctionFactory -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class ExtinctionBase(CategoryItem, SwitchableCategoryBase): @@ -34,9 +34,8 @@ def __init__(self) -> None: allowed=ExtinctionFactory.supported_tags(), ), ), - cif_handler=CifHandler( - names=['_extinction.type'], - iucr_name='_easydiffraction_extinction.type', + tags=TagSpec( + edi_names=['_extinction.type'], cif_names=['_easydiffraction_extinction.type'] ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/extinction/becker_coppens.py b/src/easydiffraction/datablocks/experiment/categories/extinction/becker_coppens.py index f4c2b6acc..fc4998923 100644 --- a/src/easydiffraction/datablocks/experiment/categories/extinction/becker_coppens.py +++ b/src/easydiffraction/datablocks/experiment/categories/extinction/becker_coppens.py @@ -19,7 +19,7 @@ from easydiffraction.datablocks.experiment.item.enums import CalculatorEnum from easydiffraction.datablocks.experiment.item.enums import ExtinctionModelEnum from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec @ExtinctionFactory.register @@ -54,9 +54,8 @@ def __init__(self) -> None: name='model', enum=ExtinctionModelEnum, description='Mosaicity distribution model (gauss or lorentz)', - cif_handler=CifHandler( - names=['_extinction.model'], - iucr_name='_easydiffraction_extinction.model', + tags=TagSpec( + edi_names=['_extinction.model'], cif_names=['_easydiffraction_extinction.model'] ), ) @@ -72,9 +71,9 @@ def __init__(self) -> None: default=1.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_extinction.mosaicity'], - iucr_name='_easydiffraction_extinction.mosaicity', + tags=TagSpec( + edi_names=['_extinction.mosaicity'], + cif_names=['_easydiffraction_extinction.mosaicity'], ), ) self._radius = Parameter( @@ -89,9 +88,8 @@ def __init__(self) -> None: default=1.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_extinction.radius'], - iucr_name='_easydiffraction_extinction.radius', + tags=TagSpec( + edi_names=['_extinction.radius'], cif_names=['_easydiffraction_extinction.radius'] ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py index 452e10477..bd84e6a0d 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/cwl.py @@ -15,7 +15,7 @@ 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.io.cif.handler import TagSpec class CwlInstrumentBase(InstrumentBase): @@ -39,10 +39,9 @@ def __init__(self) -> None: default=1.5406, validator=RangeValidator(ge=0.0), ), - cif_handler=CifHandler( - names=[ - '_instr.wavelength', - ] + tags=TagSpec( + edi_names=['_instrument.setup_wavelength'], + cif_names=['_diffrn_radiation_wavelength.value', '_instr.wavelength'], ), ) @@ -123,10 +122,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=[ - '_instr.2theta_offset', - ] + tags=TagSpec( + edi_names=['_instrument.calib_twotheta_offset'], + cif_names=['_pd_calib.2theta_offset', '_instr.2theta_offset'], ), ) @@ -144,10 +142,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=[ - '_instr.sample_displacement', - ] + tags=TagSpec( + edi_names=['_instrument.calib_sample_displacement'], + cif_names=['_instr.sample_displacement'], ), ) @@ -165,10 +162,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=[ - '_instr.sample_transparency', - ] + tags=TagSpec( + edi_names=['_instrument.calib_sample_transparency'], + cif_names=['_instr.sample_transparency'], ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py index d78f758e6..0b8230860 100644 --- a/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py +++ b/src/easydiffraction/datablocks/experiment/categories/instrument/tof.py @@ -15,7 +15,7 @@ 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.io.cif.handler import TagSpec @InstrumentFactory.register @@ -75,7 +75,9 @@ def __init__(self) -> None: default=150.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_instr.2theta_bank']), + tags=TagSpec( + edi_names=['_instrument.setup_twotheta_bank'], cif_names=['_instr.2theta_bank'] + ), ) self._calib_d_to_tof_offset: Parameter = Parameter( name='d_to_tof_offset', @@ -91,7 +93,10 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_instr.d_to_tof_offset']), + tags=TagSpec( + edi_names=['_instrument.calib_d_to_tof_offset'], + cif_names=['_instr.d_to_tof_offset'], + ), ) self._calib_d_to_tof_linear: Parameter = Parameter( name='d_to_tof_linear', @@ -107,10 +112,13 @@ def __init__(self) -> None: default=10000.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_instr.d_to_tof_linear']), + tags=TagSpec( + edi_names=['_instrument.calib_d_to_tof_linear'], + cif_names=['_instr.d_to_tof_linear'], + ), ) - self._calib_d_to_tof_quad: Parameter = Parameter( - name='d_to_tof_quad', + self._calib_d_to_tof_quadratic: Parameter = Parameter( + name='d_to_tof_quadratic', description='TOF quadratic correction', units='microseconds_per_angstrom_squared', display_handler=DisplayHandler( @@ -123,10 +131,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_instr.d_to_tof_quad']), + tags=TagSpec( + edi_names=['_instrument.calib_d_to_tof_quadratic'], + cif_names=['_instr.d_to_tof_quad'], + ), ) - self._calib_d_to_tof_recip: Parameter = Parameter( - name='d_to_tof_recip', + self._calib_d_to_tof_reciprocal: Parameter = Parameter( + name='d_to_tof_reciprocal', description='TOF reciprocal velocity correction', units='microsecond_angstroms', display_handler=DisplayHandler( @@ -139,7 +150,10 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_instr.d_to_tof_recip']), + tags=TagSpec( + edi_names=['_instrument.calib_d_to_tof_reciprocal'], + cif_names=['_instr.d_to_tof_recip'], + ), ) @property @@ -188,31 +202,31 @@ 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: + def calib_d_to_tof_quadratic(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 + return self._calib_d_to_tof_quadratic - @calib_d_to_tof_quad.setter - def calib_d_to_tof_quad(self, value: float) -> None: + @calib_d_to_tof_quadratic.setter + def calib_d_to_tof_quadratic(self, value: float) -> None: """Set the TOF quadratic correction (μs/Ų).""" - self._calib_d_to_tof_quad.value = value + self._calib_d_to_tof_quadratic.value = value @property - def calib_d_to_tof_recip(self) -> Parameter: + def calib_d_to_tof_reciprocal(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 + return self._calib_d_to_tof_reciprocal - @calib_d_to_tof_recip.setter - def calib_d_to_tof_recip(self, value: float) -> None: + @calib_d_to_tof_reciprocal.setter + def calib_d_to_tof_reciprocal(self, value: float) -> None: """Set the TOF reciprocal velocity correction (μs·Å).""" - self._calib_d_to_tof_recip.value = value + self._calib_d_to_tof_reciprocal.value = value diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py deleted file mode 100644 index 9de40a4f0..000000000 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> -# SPDX-License-Identifier: BSD-3-Clause -"""Linked crystal category referencing a sample model by ID.""" - -from easydiffraction.datablocks.experiment.categories.linked_crystal.default import LinkedCrystal diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py deleted file mode 100644 index d092f39ad..000000000 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> -# SPDX-License-Identifier: BSD-3-Clause -"""Linked phases category weighting sample models in a pattern.""" - -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_structure/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_structure/__init__.py new file mode 100644 index 000000000..09b6cfa6f --- /dev/null +++ b/src/easydiffraction/datablocks/experiment/categories/linked_structure/__init__.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Linked structure category referencing a sample model by ID.""" + +from easydiffraction.datablocks.experiment.categories.linked_structure.default import ( + LinkedStructure, +) diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_structure/default.py similarity index 61% rename from src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py rename to src/easydiffraction/datablocks/experiment/categories/linked_structure/default.py index 88ab4e887..9c6ca52a7 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_structure/default.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Default linked-crystal reference (id + scale).""" +"""Default linked-structure reference for single-crystal experiments.""" from __future__ import annotations @@ -13,22 +13,22 @@ 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.categories.linked_structure.factory import ( + LinkedStructureFactory, ) from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec -@LinkedCrystalFactory.register -class LinkedCrystal(CategoryItem): - """Linked crystal reference for single-crystal diffraction.""" +@LinkedStructureFactory.register +class LinkedStructure(CategoryItem): + """Linked structure reference for single-crystal diffraction.""" - _category_code = 'linked_crystal' + _category_code = 'linked_structure' type_info = TypeInfo( tag='default', - description='Crystal reference with id and scale factor', + description='Structure reference with id and scale factor', ) compatibility = Compatibility( sample_form=frozenset({SampleFormEnum.SINGLE_CRYSTAL}), @@ -37,32 +37,32 @@ class LinkedCrystal(CategoryItem): def __init__(self) -> None: super().__init__() - self._id = StringDescriptor( - name='id', - description='Identifier of the linked crystal', + self._structure_id = StringDescriptor( + name='structure_id', + description='Identifier of the linked structure', value_spec=AttributeSpec( default='Si', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), - cif_handler=CifHandler( - names=['_sc_crystal_block.id'], - iucr_name='_easydiffraction_sc_crystal_block.id', + tags=TagSpec( + edi_names=['_linked_structure.structure_id'], + cif_names=['_easydiffraction_sc_crystal_block.id', '_sc_crystal_block.id'], ), display_handler=DisplayHandler( - display_name='Crystal', - latex_name='Crystal', + display_name='Structure', + latex_name='Structure', ), ) self._scale = Parameter( name='scale', - description='Scale factor of the linked crystal', + description='Scale factor of the linked structure', value_spec=AttributeSpec( default=1.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_sc_crystal_block.scale'], - iucr_name='_easydiffraction_sc_crystal_block.scale', + tags=TagSpec( + edi_names=['_linked_structure.scale'], + cif_names=['_easydiffraction_sc_crystal_block.scale', '_sc_crystal_block.scale'], ), display_handler=DisplayHandler( display_name='Scale', @@ -75,24 +75,24 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def id(self) -> StringDescriptor: + def structure_id(self) -> StringDescriptor: """ - Identifier of the linked crystal. + Identifier of the linked structure. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the parameter value. """ - return self._id + return self._structure_id - @id.setter - def id(self, value: str) -> None: - self._id.value = value + @structure_id.setter + def structure_id(self, value: str) -> None: + self._structure_id.value = value @property def scale(self) -> Parameter: """ - Scale factor of the linked crystal. + Scale factor of the linked structure. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py b/src/easydiffraction/datablocks/experiment/categories/linked_structure/factory.py similarity index 66% rename from src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py rename to src/easydiffraction/datablocks/experiment/categories/linked_structure/factory.py index 65095dc63..9dc65433c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_structure/factory.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Linked-phases factory — delegates entirely to ``FactoryBase``.""" +"""Linked-structure factory delegates to ``FactoryBase``.""" from __future__ import annotations @@ -9,8 +9,8 @@ from easydiffraction.core.factory import FactoryBase -class LinkedPhasesFactory(FactoryBase): - """Create linked-phases collections by tag.""" +class LinkedStructureFactory(FactoryBase): + """Create linked-structure references by tag.""" _default_rules: ClassVar[dict] = { frozenset(): 'default', diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_structures/__init__.py b/src/easydiffraction/datablocks/experiment/categories/linked_structures/__init__.py new file mode 100644 index 000000000..fcb5b5893 --- /dev/null +++ b/src/easydiffraction/datablocks/experiment/categories/linked_structures/__init__.py @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Linked structures category weighting sample models in a pattern.""" + +from easydiffraction.datablocks.experiment.categories.linked_structures.default import ( + LinkedStructure, +) +from easydiffraction.datablocks.experiment.categories.linked_structures.default import ( + LinkedStructures, +) diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py b/src/easydiffraction/datablocks/experiment/categories/linked_structures/default.py similarity index 59% rename from src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py rename to src/easydiffraction/datablocks/experiment/categories/linked_structures/default.py index c1106f45a..bd0c5f5b6 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_phases/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_structures/default.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Linked phases allow combining phases with scale factors.""" +"""Linked structures allow combining models with scale factors.""" from __future__ import annotations @@ -14,43 +14,47 @@ 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.categories.linked_structures.factory import ( + LinkedStructuresFactory, ) from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec -class LinkedPhase(CategoryItem): - """Link to a phase by id with a scale factor.""" +class LinkedStructure(CategoryItem): + """Link to a structure by id with a scale factor.""" - _category_code = 'linked_phases' - _category_entry_name = 'id' + _category_code = 'linked_structure' + _category_entry_name = 'structure_id' def __init__(self) -> None: super().__init__() - self._id = StringDescriptor( - name='id', - description='Identifier of the linked phase', + self._structure_id = StringDescriptor( + name='structure_id', + description='Identifier of the linked structure', value_spec=AttributeSpec( default='Si', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), - cif_handler=CifHandler(names=['_pd_phase_block.id']), + tags=TagSpec( + edi_names=['_linked_structure.structure_id'], cif_names=['_pd_phase_block.id'] + ), display_handler=DisplayHandler( - display_name='Phase', - latex_name='Phase', + display_name='Structure', + latex_name='Structure', ), ) self._scale = Parameter( name='scale', - description='Scale factor of the linked phase.', + description='Scale factor of the linked structure.', value_spec=AttributeSpec( default=1.0, validator=RangeValidator(ge=0.0), ), - cif_handler=CifHandler(names=['_pd_phase_block.scale']), + tags=TagSpec( + edi_names=['_linked_structure.scale'], cif_names=['_pd_phase_block.scale'] + ), display_handler=DisplayHandler( display_name='Scale', latex_name='Scale', @@ -62,24 +66,24 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def id(self) -> StringDescriptor: + def structure_id(self) -> StringDescriptor: """ - Identifier of the linked phase. + Identifier of the linked structure. Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the parameter value. """ - return self._id + return self._structure_id - @id.setter - def id(self, value: str) -> None: - self._id.value = value + @structure_id.setter + def structure_id(self, value: str) -> None: + self._structure_id.value = value @property def scale(self) -> Parameter: """ - Scale factor of the linked phase. + Scale factor of the linked structure. Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. @@ -91,18 +95,18 @@ def scale(self, value: float) -> None: self._scale.value = value -@LinkedPhasesFactory.register -class LinkedPhases(CategoryCollection): - """Collection of LinkedPhase instances.""" +@LinkedStructuresFactory.register +class LinkedStructures(CategoryCollection): + """Collection of LinkedStructure instances.""" type_info = TypeInfo( tag='default', - description='Phase references with scale factors', + description='Structure 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) + """Create an empty collection of linked structures.""" + super().__init__(item_type=LinkedStructure) diff --git a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py b/src/easydiffraction/datablocks/experiment/categories/linked_structures/factory.py similarity index 66% rename from src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py rename to src/easydiffraction/datablocks/experiment/categories/linked_structures/factory.py index 9715c3c3a..6ef75f5e0 100644 --- a/src/easydiffraction/datablocks/experiment/categories/linked_crystal/factory.py +++ b/src/easydiffraction/datablocks/experiment/categories/linked_structures/factory.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Linked-crystal factory — delegates entirely to ``FactoryBase``.""" +"""Linked-structures factory delegates to ``FactoryBase``.""" from __future__ import annotations @@ -9,8 +9,8 @@ from easydiffraction.core.factory import FactoryBase -class LinkedCrystalFactory(FactoryBase): - """Create linked-crystal references by tag.""" +class LinkedStructuresFactory(FactoryBase): + """Create linked-structures collections by tag.""" _default_rules: ClassVar[dict] = { frozenset(): 'default', diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/base.py b/src/easydiffraction/datablocks/experiment/categories/peak/base.py index abbfab723..707bb219c 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/base.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/base.py @@ -12,7 +12,7 @@ from easydiffraction.core.variable import StringDescriptor from easydiffraction.datablocks.experiment.categories.peak.factory import PeakFactory from easydiffraction.datablocks.experiment.item.enums import PeakProfileTypeEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.utils.logging import console from easydiffraction.utils.utils import render_table @@ -43,10 +43,7 @@ def __init__(self) -> None: allowed=[member.value for member in PeakProfileTypeEnum], ), ), - cif_handler=CifHandler( - names=['_peak.type'], - iucr_name='_easydiffraction_peak.type', - ), + tags=TagSpec(edi_names=['_peak.type'], cif_names=['_easydiffraction_peak.type']), ) def _canonicalize(self, value: str) -> str: diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py index ca6fc37da..95f110b5d 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/cwl_mixins.py @@ -12,7 +12,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec class CwlBroadeningMixin: @@ -36,9 +36,9 @@ def __init__(self) -> None: default=0.01, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.broad_gauss_u'], - iucr_name='_easydiffraction_peak.broad_gauss_u', + tags=TagSpec( + edi_names=['_peak.broad_gauss_u'], + cif_names=['_easydiffraction_peak.broad_gauss_u'], ), ) self._broad_gauss_v: Parameter = Parameter( @@ -55,9 +55,9 @@ def __init__(self) -> None: default=-0.01, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.broad_gauss_v'], - iucr_name='_easydiffraction_peak.broad_gauss_v', + tags=TagSpec( + edi_names=['_peak.broad_gauss_v'], + cif_names=['_easydiffraction_peak.broad_gauss_v'], ), ) self._broad_gauss_w: Parameter = Parameter( @@ -74,9 +74,9 @@ def __init__(self) -> None: default=0.02, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.broad_gauss_w'], - iucr_name='_easydiffraction_peak.broad_gauss_w', + tags=TagSpec( + edi_names=['_peak.broad_gauss_w'], + cif_names=['_easydiffraction_peak.broad_gauss_w'], ), ) self._broad_lorentz_x: Parameter = Parameter( @@ -93,9 +93,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.broad_lorentz_x'], - iucr_name='_easydiffraction_peak.broad_lorentz_x', + tags=TagSpec( + edi_names=['_peak.broad_lorentz_x'], + cif_names=['_easydiffraction_peak.broad_lorentz_x'], ), ) self._broad_lorentz_y: Parameter = Parameter( @@ -112,9 +112,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.broad_lorentz_y'], - iucr_name='_easydiffraction_peak.broad_lorentz_y', + tags=TagSpec( + edi_names=['_peak.broad_lorentz_y'], + cif_names=['_easydiffraction_peak.broad_lorentz_y'], ), ) @@ -213,9 +213,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.asym_empir_1'], - iucr_name='_easydiffraction_peak.asym_empir_1', + tags=TagSpec( + edi_names=['_peak.asym_empir_1'], cif_names=['_easydiffraction_peak.asym_empir_1'] ), ) self._asym_empir_2: Parameter = Parameter( @@ -226,9 +225,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.asym_empir_2'], - iucr_name='_easydiffraction_peak.asym_empir_2', + tags=TagSpec( + edi_names=['_peak.asym_empir_2'], cif_names=['_easydiffraction_peak.asym_empir_2'] ), ) self._asym_empir_3: Parameter = Parameter( @@ -239,9 +237,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.asym_empir_3'], - iucr_name='_easydiffraction_peak.asym_empir_3', + tags=TagSpec( + edi_names=['_peak.asym_empir_3'], cif_names=['_easydiffraction_peak.asym_empir_3'] ), ) self._asym_empir_4: Parameter = Parameter( @@ -252,9 +249,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.asym_empir_4'], - iucr_name='_easydiffraction_peak.asym_empir_4', + tags=TagSpec( + edi_names=['_peak.asym_empir_4'], cif_names=['_easydiffraction_peak.asym_empir_4'] ), ) @@ -338,9 +334,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.asym_fcj_1'], - iucr_name='_easydiffraction_peak.asym_fcj_1', + tags=TagSpec( + edi_names=['_peak.asym_fcj_1'], cif_names=['_easydiffraction_peak.asym_fcj_1'] ), ) self._asym_fcj_2: Parameter = Parameter( @@ -351,9 +346,8 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.asym_fcj_2'], - iucr_name='_easydiffraction_peak.asym_fcj_2', + tags=TagSpec( + edi_names=['_peak.asym_fcj_2'], cif_names=['_easydiffraction_peak.asym_fcj_2'] ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py index 893618c67..2fb211cf5 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/tof_mixins.py @@ -19,7 +19,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec class TofGaussianBroadeningMixin: @@ -35,7 +35,7 @@ def __init__(self) -> None: super().__init__() self._broad_gauss_sigma_0 = Parameter( - name='gauss_sigma_0', + name='broad_gauss_sigma_0', description='Gaussian broadening (instrumental resolution)', units='microseconds_squared', display_handler=DisplayHandler( @@ -46,13 +46,13 @@ def __init__(self) -> None: default=7.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.gauss_sigma_0'], - iucr_name='_easydiffraction_peak.gauss_sigma_0', + tags=TagSpec( + edi_names=['_peak.broad_gauss_sigma_0'], + cif_names=['_easydiffraction_peak.broad_gauss_sigma_0'], ), ) self._broad_gauss_sigma_1 = Parameter( - name='gauss_sigma_1', + name='broad_gauss_sigma_1', description='Gaussian broadening (dependent on d-spacing)', units='microseconds_per_angstrom', display_handler=DisplayHandler( @@ -63,13 +63,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.gauss_sigma_1'], - iucr_name='_easydiffraction_peak.gauss_sigma_1', + tags=TagSpec( + edi_names=['_peak.broad_gauss_sigma_1'], + cif_names=['_easydiffraction_peak.broad_gauss_sigma_1'], ), ) self._broad_gauss_sigma_2 = Parameter( - name='gauss_sigma_2', + name='broad_gauss_sigma_2', description='Gaussian broadening (instrument-dependent term)', units='microseconds_squared_per_angstrom_squared', display_handler=DisplayHandler( @@ -80,9 +80,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.gauss_sigma_2'], - iucr_name='_easydiffraction_peak.gauss_sigma_2', + tags=TagSpec( + edi_names=['_peak.broad_gauss_sigma_2'], + cif_names=['_easydiffraction_peak.broad_gauss_sigma_2'], ), ) @@ -140,7 +140,7 @@ def __init__(self) -> None: super().__init__() self._broad_lorentz_gamma_0 = Parameter( - name='lorentz_gamma_0', + name='broad_lorentz_gamma_0', description='Lorentzian broadening (microstrain effects)', units='microseconds', display_handler=DisplayHandler( @@ -151,13 +151,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.lorentz_gamma_0'], - iucr_name='_easydiffraction_peak.lorentz_gamma_0', + tags=TagSpec( + edi_names=['_peak.broad_lorentz_gamma_0'], + cif_names=['_easydiffraction_peak.broad_lorentz_gamma_0'], ), ) self._broad_lorentz_gamma_1 = Parameter( - name='lorentz_gamma_1', + name='broad_lorentz_gamma_1', description='Lorentzian broadening (dependent on d-spacing)', units='microseconds_per_angstrom', display_handler=DisplayHandler( @@ -168,13 +168,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.lorentz_gamma_1'], - iucr_name='_easydiffraction_peak.lorentz_gamma_1', + tags=TagSpec( + edi_names=['_peak.broad_lorentz_gamma_1'], + cif_names=['_easydiffraction_peak.broad_lorentz_gamma_1'], ), ) self._broad_lorentz_gamma_2 = Parameter( - name='lorentz_gamma_2', + name='broad_lorentz_gamma_2', description='Lorentzian broadening (instrument-dependent term)', units='microseconds_squared_per_angstrom_squared', display_handler=DisplayHandler( @@ -185,9 +185,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.lorentz_gamma_2'], - iucr_name='_easydiffraction_peak.lorentz_gamma_2', + tags=TagSpec( + edi_names=['_peak.broad_lorentz_gamma_2'], + cif_names=['_easydiffraction_peak.broad_lorentz_gamma_2'], ), ) @@ -256,7 +256,7 @@ def __init__(self) -> None: """Initialize the back-to-back exponential parameters.""" super().__init__() - self._exp_rise_alpha_0 = Parameter( + self._rise_alpha_0 = Parameter( name='rise_alpha_0', description='Back-to-back exponential rise α₀', units='microseconds', @@ -268,12 +268,12 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.rise_alpha_0'], - iucr_name='_easydiffraction_peak.rise_alpha_0', + tags=TagSpec( + edi_names=['_peak.rise_alpha_0'], + cif_names=['_easydiffraction_peak.rise_alpha_0'], ), ) - self._exp_rise_alpha_1 = Parameter( + self._rise_alpha_1 = Parameter( name='rise_alpha_1', description='Back-to-back exponential rise α₁', units='microseconds_per_angstrom', @@ -285,12 +285,12 @@ def __init__(self) -> None: default=0.2, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.rise_alpha_1'], - iucr_name='_easydiffraction_peak.rise_alpha_1', + tags=TagSpec( + edi_names=['_peak.rise_alpha_1'], + cif_names=['_easydiffraction_peak.rise_alpha_1'], ), ) - self._exp_decay_beta_0 = Parameter( + self._decay_beta_0 = Parameter( name='decay_beta_0', description='Back-to-back exponential decay β₀', units='microseconds', @@ -302,12 +302,12 @@ def __init__(self) -> None: default=0.04, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.decay_beta_0'], - iucr_name='_easydiffraction_peak.decay_beta_0', + tags=TagSpec( + edi_names=['_peak.decay_beta_0'], + cif_names=['_easydiffraction_peak.decay_beta_0'], ), ) - self._exp_decay_beta_1 = Parameter( + self._decay_beta_1 = Parameter( name='decay_beta_1', description='Back-to-back exponential decay β₁', units='microseconds_per_angstrom', @@ -319,71 +319,71 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.decay_beta_1'], - iucr_name='_easydiffraction_peak.decay_beta_1', + tags=TagSpec( + edi_names=['_peak.decay_beta_1'], + cif_names=['_easydiffraction_peak.decay_beta_1'], ), ) @property - def exp_rise_alpha_0(self) -> Parameter: + def rise_alpha_0(self) -> Parameter: """ Back-to-back exponential rise α₀ (μs). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. """ - return self._exp_rise_alpha_0 + return self._rise_alpha_0 - @exp_rise_alpha_0.setter - def exp_rise_alpha_0(self, value: float) -> None: + @rise_alpha_0.setter + def rise_alpha_0(self, value: float) -> None: """Set the back-to-back exponential rise α₀ (μs).""" - self._exp_rise_alpha_0.value = value + self._rise_alpha_0.value = value @property - def exp_rise_alpha_1(self) -> Parameter: + def rise_alpha_1(self) -> Parameter: """ Back-to-back exponential rise α₁ (μs/Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. """ - return self._exp_rise_alpha_1 + return self._rise_alpha_1 - @exp_rise_alpha_1.setter - def exp_rise_alpha_1(self, value: float) -> None: + @rise_alpha_1.setter + def rise_alpha_1(self, value: float) -> None: """Set the back-to-back exponential rise α₁ (μs/Å).""" - self._exp_rise_alpha_1.value = value + self._rise_alpha_1.value = value @property - def exp_decay_beta_0(self) -> Parameter: + def decay_beta_0(self) -> Parameter: """ Back-to-back exponential decay β₀ (μs). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. """ - return self._exp_decay_beta_0 + return self._decay_beta_0 - @exp_decay_beta_0.setter - def exp_decay_beta_0(self, value: float) -> None: + @decay_beta_0.setter + def decay_beta_0(self, value: float) -> None: """Set the back-to-back exponential decay β₀ (μs).""" - self._exp_decay_beta_0.value = value + self._decay_beta_0.value = value @property - def exp_decay_beta_1(self) -> Parameter: + def decay_beta_1(self) -> Parameter: """ Back-to-back exponential decay β₁ (μs/Å). Reading this property returns the underlying ``Parameter`` object. Assigning to it updates the parameter value. """ - return self._exp_decay_beta_1 + return self._decay_beta_1 - @exp_decay_beta_1.setter - def exp_decay_beta_1(self, value: float) -> None: + @decay_beta_1.setter + def decay_beta_1(self, value: float) -> None: """Set the back-to-back exponential decay β₁ (μs/Å).""" - self._exp_decay_beta_1.value = value + self._decay_beta_1.value = value class TofDoubleExponentialMixin: @@ -414,9 +414,9 @@ def __init__(self) -> None: default=0.25, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_rise_alpha_1'], - iucr_name='_easydiffraction_peak.dexp_rise_alpha_1', + tags=TagSpec( + edi_names=['_peak.dexp_rise_alpha_1'], + cif_names=['_easydiffraction_peak.dexp_rise_alpha_1'], ), ) self._dexp_rise_alpha_2 = Parameter( @@ -431,9 +431,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_rise_alpha_2'], - iucr_name='_easydiffraction_peak.dexp_rise_alpha_2', + tags=TagSpec( + edi_names=['_peak.dexp_rise_alpha_2'], + cif_names=['_easydiffraction_peak.dexp_rise_alpha_2'], ), ) self._dexp_decay_beta_00 = Parameter( @@ -448,9 +448,9 @@ def __init__(self) -> None: default=4.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_decay_beta_00'], - iucr_name='_easydiffraction_peak.dexp_decay_beta_00', + tags=TagSpec( + edi_names=['_peak.dexp_decay_beta_00'], + cif_names=['_easydiffraction_peak.dexp_decay_beta_00'], ), ) self._dexp_decay_beta_01 = Parameter( @@ -465,9 +465,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_decay_beta_01'], - iucr_name='_easydiffraction_peak.dexp_decay_beta_01', + tags=TagSpec( + edi_names=['_peak.dexp_decay_beta_01'], + cif_names=['_easydiffraction_peak.dexp_decay_beta_01'], ), ) self._dexp_decay_beta_10 = Parameter( @@ -482,9 +482,9 @@ def __init__(self) -> None: default=2.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_decay_beta_10'], - iucr_name='_easydiffraction_peak.dexp_decay_beta_10', + tags=TagSpec( + edi_names=['_peak.dexp_decay_beta_10'], + cif_names=['_easydiffraction_peak.dexp_decay_beta_10'], ), ) self._dexp_switch_r_01 = Parameter( @@ -495,9 +495,9 @@ def __init__(self) -> None: default=0.5, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_switch_r_01'], - iucr_name='_easydiffraction_peak.dexp_switch_r_01', + tags=TagSpec( + edi_names=['_peak.dexp_switch_r_01'], + cif_names=['_easydiffraction_peak.dexp_switch_r_01'], ), ) self._dexp_switch_r_02 = Parameter( @@ -508,9 +508,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_switch_r_02'], - iucr_name='_easydiffraction_peak.dexp_switch_r_02', + tags=TagSpec( + edi_names=['_peak.dexp_switch_r_02'], + cif_names=['_easydiffraction_peak.dexp_switch_r_02'], ), ) self._dexp_switch_r_03 = Parameter( @@ -521,9 +521,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.dexp_switch_r_03'], - iucr_name='_easydiffraction_peak.dexp_switch_r_03', + tags=TagSpec( + edi_names=['_peak.dexp_switch_r_03'], + cif_names=['_easydiffraction_peak.dexp_switch_r_03'], ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py index 15b2c539a..d82880e5f 100644 --- a/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py +++ b/src/easydiffraction/datablocks/experiment/categories/peak/total_mixins.py @@ -12,7 +12,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec class TotalBroadeningMixin: @@ -34,10 +34,7 @@ def __init__(self) -> None: default=0.05, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.damp_q'], - iucr_name='_easydiffraction_peak.damp_q', - ), + tags=TagSpec(edi_names=['_peak.damp_q'], cif_names=['_easydiffraction_peak.damp_q']), ) self._broad_q = Parameter( name='broad_q', @@ -51,10 +48,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.broad_q'], - iucr_name='_easydiffraction_peak.broad_q', - ), + tags=TagSpec(edi_names=['_peak.broad_q'], cif_names=['_easydiffraction_peak.broad_q']), ) self._cutoff_q = Parameter( name='cutoff_q', @@ -68,9 +62,8 @@ def __init__(self) -> None: default=25.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.cutoff_q'], - iucr_name='_easydiffraction_peak.cutoff_q', + tags=TagSpec( + edi_names=['_peak.cutoff_q'], cif_names=['_easydiffraction_peak.cutoff_q'] ), ) self._sharp_delta_1 = Parameter( @@ -85,9 +78,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.sharp_delta_1'], - iucr_name='_easydiffraction_peak.sharp_delta_1', + tags=TagSpec( + edi_names=['_peak.sharp_delta_1'], + cif_names=['_easydiffraction_peak.sharp_delta_1'], ), ) self._sharp_delta_2 = Parameter( @@ -102,9 +95,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.sharp_delta_2'], - iucr_name='_easydiffraction_peak.sharp_delta_2', + tags=TagSpec( + edi_names=['_peak.sharp_delta_2'], + cif_names=['_easydiffraction_peak.sharp_delta_2'], ), ) self._damp_particle_diameter = Parameter( @@ -119,9 +112,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=['_peak.damp_particle_diameter'], - iucr_name='_easydiffraction_peak.damp_particle_diameter', + tags=TagSpec( + edi_names=['_peak.damp_particle_diameter'], + cif_names=['_easydiffraction_peak.damp_particle_diameter'], ), ) diff --git a/src/easydiffraction/datablocks/experiment/categories/pref_orient/default.py b/src/easydiffraction/datablocks/experiment/categories/pref_orient/default.py index 9c1d74c50..976ad97e0 100644 --- a/src/easydiffraction/datablocks/experiment/categories/pref_orient/default.py +++ b/src/easydiffraction/datablocks/experiment/categories/pref_orient/default.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Per-phase March-Dollase preferred-orientation corrections.""" +"""Per-structure March-Dollase preferred-orientation corrections.""" from __future__ import annotations @@ -20,32 +20,34 @@ 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.io.cif.handler import TagSpec class PrefOrient(CategoryItem): - """March-Dollase preferred-orientation correction for one phase.""" + """ + March-Dollase preferred-orientation correction for one structure. + """ - _category_code = 'pref_orient' - _category_entry_name = 'phase_id' + _category_code = 'preferred_orientation' + _category_entry_name = 'structure_id' def __init__(self) -> None: super().__init__() - self._phase_id = StringDescriptor( - name='phase_id', - description='Identifier of the corrected phase', + self._structure_id = StringDescriptor( + name='structure_id', + description='Identifier of the corrected structure', value_spec=AttributeSpec( default='Si', validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), - cif_handler=CifHandler( - names=['_pref_orient.phase_id'], - iucr_name='_pd_pref_orient_March_Dollase.phase_id', + tags=TagSpec( + edi_names=['_preferred_orientation.structure_id'], + cif_names=['_pd_pref_orient_March_Dollase.phase_id', '_pref_orient.phase_id'], ), display_handler=DisplayHandler( - display_name='Phase', - latex_name='Phase', + display_name='Structure', + latex_name='Structure', ), ) self._march_r = Parameter( @@ -55,9 +57,9 @@ def __init__(self) -> None: default=1.0, validator=RangeValidator(gt=0.0), ), - cif_handler=CifHandler( - names=['_pref_orient.march_r'], - iucr_name='_pd_pref_orient_March_Dollase.r', + tags=TagSpec( + edi_names=['_preferred_orientation.march_r'], + cif_names=['_pd_pref_orient_March_Dollase.r', '_pref_orient.march_r'], ), display_handler=DisplayHandler( display_name='March coefficient', @@ -68,9 +70,9 @@ def __init__(self) -> None: name='index_h', description='Texture-axis Miller index h', value_spec=AttributeSpec(default=0), - cif_handler=CifHandler( - names=['_pref_orient.index_h'], - iucr_name='_pd_pref_orient_March_Dollase.index_h', + tags=TagSpec( + edi_names=['_preferred_orientation.index_h'], + cif_names=['_pd_pref_orient_March_Dollase.index_h', '_pref_orient.index_h'], ), display_handler=DisplayHandler( display_name='h', @@ -81,9 +83,9 @@ def __init__(self) -> None: name='index_k', description='Texture-axis Miller index k', value_spec=AttributeSpec(default=0), - cif_handler=CifHandler( - names=['_pref_orient.index_k'], - iucr_name='_pd_pref_orient_March_Dollase.index_k', + tags=TagSpec( + edi_names=['_preferred_orientation.index_k'], + cif_names=['_pd_pref_orient_March_Dollase.index_k', '_pref_orient.index_k'], ), display_handler=DisplayHandler( display_name='k', @@ -94,9 +96,9 @@ def __init__(self) -> None: name='index_l', description='Texture-axis Miller index l', value_spec=AttributeSpec(default=1), - cif_handler=CifHandler( - names=['_pref_orient.index_l'], - iucr_name='_pd_pref_orient_March_Dollase.index_l', + tags=TagSpec( + edi_names=['_preferred_orientation.index_l'], + cif_names=['_pd_pref_orient_March_Dollase.index_l', '_pref_orient.index_l'], ), display_handler=DisplayHandler( display_name='l', @@ -110,9 +112,12 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0.0, le=1.0), ), - cif_handler=CifHandler( - names=['_pref_orient.march_random_fract'], - iucr_name='_easydiffraction_pref_orient.march_random_fract', + tags=TagSpec( + edi_names=['_preferred_orientation.march_random_fract'], + cif_names=[ + '_easydiffraction_pref_orient.march_random_fract', + '_pref_orient.march_random_fract', + ], ), display_handler=DisplayHandler( display_name='Random fraction', @@ -125,13 +130,13 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def phase_id(self) -> StringDescriptor: - """Identifier of the corrected phase.""" - return self._phase_id + def structure_id(self) -> StringDescriptor: + """Identifier of the corrected structure.""" + return self._structure_id - @phase_id.setter - def phase_id(self, value: str) -> None: - self._phase_id.value = value + @structure_id.setter + def structure_id(self, value: str) -> None: + self._structure_id.value = value @property def march_r(self) -> Parameter: @@ -185,11 +190,11 @@ def march_random_fract(self, value: float) -> None: @PrefOrientFactory.register class PrefOrients(CategoryCollection): - """Collection of per-phase preferred-orientation corrections.""" + """Collection of per-structure preferred-orientation corrections.""" type_info = TypeInfo( tag='default', - description='Per-phase March-Dollase preferred orientation', + description='Per-structure March-Dollase preferred orientation', ) compatibility = Compatibility( sample_form=frozenset({SampleFormEnum.POWDER}), diff --git a/src/easydiffraction/datablocks/experiment/categories/refln/bragg_pd.py b/src/easydiffraction/datablocks/experiment/categories/refln/bragg_pd.py index a59796545..cd8280812 100644 --- a/src/easydiffraction/datablocks/experiment/categories/refln/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/categories/refln/bragg_pd.py @@ -25,7 +25,7 @@ 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.io.cif.handler import TagSpec if TYPE_CHECKING: from collections.abc import Sequence @@ -39,15 +39,18 @@ class PowderReflnBase(SingleCrystalRefln): def __init__(self) -> None: super().__init__() - self._phase_id = StringDescriptor( - name='phase_id', - description='Identifier of the linked phase for this reflection', + self._structure_id = StringDescriptor( + name='structure_id', + description='Identifier of the linked structure for this reflection', display_handler=DisplayHandler( - display_name='Phase', - latex_name='Phase', + display_name='Structure', + latex_name='Structure', ), value_spec=AttributeSpec(default=''), - cif_handler=CifHandler(names=['_refln.phase_id']), + tags=TagSpec( + edi_names=['_refln.structure_id'], + cif_names=['_pd_refln.phase_id', '_refln.phase_id'], + ), ) self._f_calc = NumericDescriptor( name='f_calc', @@ -60,7 +63,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.f_calc']), + tags=TagSpec(edi_names=['_refln.f_calc']), ) self._f_squared_calc = NumericDescriptor( name='f_squared_calc', @@ -73,13 +76,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.f_squared_calc']), + tags=TagSpec(edi_names=['_refln.f_squared_calc']), ) @property - def phase_id(self) -> StringDescriptor: - """Linked-phase identifier for this reflection.""" - return self._phase_id + def structure_id(self) -> StringDescriptor: + """Linked-structure identifier for this reflection.""" + return self._structure_id @property def f_calc(self) -> NumericDescriptor: @@ -96,7 +99,7 @@ def parameters(self) -> list: """Powder reflection descriptors serialized in CIF loops.""" return [ self._id, - self._phase_id, + self._structure_id, self._d_spacing, self._sin_theta_over_lambda, self._index_h, @@ -127,7 +130,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler(names=['_refln.two_theta']), + tags=TagSpec(edi_names=['_refln.two_theta']), ) @property @@ -161,7 +164,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.time_of_flight']), + tags=TagSpec(edi_names=['_refln.time_of_flight']), ) @property @@ -190,7 +193,7 @@ def _replace_from_records(self, records: Sequence[PowderReflnRecord]) -> None: item = self._item_type() item._parent = self item.id._value = str(index) - item.phase_id._value = str(record.phase_id) + item.structure_id._value = str(record.structure_id) item.d_spacing._value = float(record.d_spacing) item.sin_theta_over_lambda._value = float(record.sin_theta_over_lambda) item.index_h._value = record.index_h @@ -219,9 +222,9 @@ def id(self) -> np.ndarray: return np.fromiter((item.id.value for item in self._items), dtype=object) @property - def phase_id(self) -> np.ndarray: - """Linked-phase identifiers for all rows.""" - return np.fromiter((item.phase_id.value for item in self._items), dtype=object) + def structure_id(self) -> np.ndarray: + """Linked-structure identifiers for all rows.""" + return np.fromiter((item.structure_id.value for item in self._items), dtype=object) @property def d_spacing(self) -> np.ndarray: diff --git a/src/easydiffraction/datablocks/experiment/categories/refln/bragg_sc.py b/src/easydiffraction/datablocks/experiment/categories/refln/bragg_sc.py index 4c896937e..e461d6460 100644 --- a/src/easydiffraction/datablocks/experiment/categories/refln/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/categories/refln/bragg_sc.py @@ -22,7 +22,7 @@ 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.io.cif.handler import TagSpec from easydiffraction.utils.logging import log from easydiffraction.utils.utils import sin_theta_over_lambda_to_d_spacing @@ -50,7 +50,7 @@ def __init__(self) -> None: # Do we need conversion between CIF and internal label? validator=RegexValidator(pattern=r'^[A-Za-z0-9_]*$'), ), - cif_handler=CifHandler(names=['_refln.id']), + tags=TagSpec(edi_names=['_refln.id']), ) self._d_spacing = NumericDescriptor( name='d_spacing', @@ -66,7 +66,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.d_spacing']), + tags=TagSpec(edi_names=['_refln.d_spacing']), ) self._sin_theta_over_lambda = NumericDescriptor( name='sin_theta_over_lambda', @@ -82,7 +82,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.sin_theta_over_lambda']), + tags=TagSpec(edi_names=['_refln.sin_theta_over_lambda']), ) self._index_h = NumericDescriptor( name='index_h', @@ -95,7 +95,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_refln.index_h']), + tags=TagSpec(edi_names=['_refln.index_h']), ) self._index_k = NumericDescriptor( name='index_k', @@ -108,7 +108,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_refln.index_k']), + tags=TagSpec(edi_names=['_refln.index_k']), ) self._index_l = NumericDescriptor( name='index_l', @@ -121,7 +121,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_refln.index_l']), + tags=TagSpec(edi_names=['_refln.index_l']), ) self._intensity_meas = NumericDescriptor( name='intensity_meas', @@ -134,7 +134,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.intensity_meas']), + tags=TagSpec(edi_names=['_refln.intensity_meas']), ) self._intensity_meas_su = NumericDescriptor( name='intensity_meas_su', @@ -147,7 +147,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.intensity_meas_su']), + tags=TagSpec(edi_names=['_refln.intensity_meas_su']), ) self._intensity_calc = NumericDescriptor( name='intensity_calc', @@ -160,7 +160,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.intensity_calc']), + tags=TagSpec(edi_names=['_refln.intensity_calc']), ) # ------------------------------------------------------------------ @@ -280,7 +280,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0), ), - cif_handler=CifHandler(names=['_refln.wavelength']), + tags=TagSpec(edi_names=['_refln.wavelength']), ) @property @@ -383,18 +383,18 @@ def _update( structures = project.structures calculator = experiment.calculator.calculator - linked_crystal = experiment.linked_crystal - linked_crystal_id = experiment.linked_crystal.id.value + linked_structure = experiment.linked_structure + linked_structure_id = experiment.linked_structure.structure_id.value - if linked_crystal_id not in structures.names: + if linked_structure_id not in structures.names: log.error( - f"Linked crystal ID '{linked_crystal_id}' not found in " + f"Linked structure ID '{linked_structure_id}' not found in " f'structure IDs {structures.names}.' ) return - structure_id = linked_crystal_id - structure_scale = linked_crystal.scale.value + structure_id = linked_structure_id + structure_scale = linked_structure.scale.value structure = structures[structure_id] stol, raw_calc = calculator.calculate_structure_factors( diff --git a/src/easydiffraction/datablocks/experiment/collection.py b/src/easydiffraction/datablocks/experiment/collection.py index 65646d506..4dec04c44 100644 --- a/src/easydiffraction/datablocks/experiment/collection.py +++ b/src/easydiffraction/datablocks/experiment/collection.py @@ -2,11 +2,14 @@ # SPDX-License-Identifier: BSD-3-Clause """Collection of experiment data blocks.""" +import pathlib + 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.io.edi import edi_body_from_text from easydiffraction.utils.enums import VerbosityEnum from easydiffraction.utils.logging import console @@ -96,6 +99,23 @@ def add_from_cif_path( experiment = ExperimentFactory.from_cif_path(cif_path) self.add(experiment) + @typechecked + def add_from_edi_path( + self, + edi_path: str, + ) -> None: + """ + Add an experiment from an Edi file. + + Parameters + ---------- + edi_path : str + Path to an Edi experiment file. + """ + body = edi_body_from_text(pathlib.Path(edi_path).read_text(encoding='utf-8')) + experiment = ExperimentFactory.from_cif_str(body) + self.add(experiment) + @typechecked def add_from_data_path( self, diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 6f7d47a5d..289e97663 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -22,11 +22,11 @@ ) 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_structure.factory import ( + LinkedStructureFactory, ) -from easydiffraction.datablocks.experiment.categories.linked_phases.factory import ( - LinkedPhasesFactory, +from easydiffraction.datablocks.experiment.categories.linked_structures.factory import ( + LinkedStructuresFactory, ) from easydiffraction.datablocks.experiment.categories.peak.factory import PeakFactory from easydiffraction.datablocks.experiment.categories.refln.factory import ReflnFactory @@ -71,11 +71,11 @@ def __init__( self, *, name: str, - type: ExperimentType, + experiment_type: ExperimentType, ) -> None: super().__init__() self._name = name - self._type = type + self._experiment_type = experiment_type self._calculator = None self._identity.datablock_entry_name = lambda: self.name @@ -90,15 +90,15 @@ def __init__( def _attach_category_parents(self) -> None: """Link owned categories back to this experiment object.""" for category in [ - self._type, + self._experiment_type, getattr(self, '_diffrn', None), getattr(self, '_calculator_category', None), getattr(self, '_extinction', None), getattr(self, '_absorption', None), - getattr(self, '_linked_crystal', None), + getattr(self, '_linked_structure', None), getattr(self, '_instrument', None), getattr(self, '_refln', None), - getattr(self, '_linked_phases', None), + getattr(self, '_linked_structures', None), getattr(self, '_pref_orient', None), getattr(self, '_excluded_regions', None), getattr(self, '_data', None), @@ -119,16 +119,16 @@ def _supported_filters_for(self, category: object) -> dict[str, object]: if category is getattr(self, '_absorption', None): return { 'calculator': calculator, - 'sample_form': self.type.sample_form.value, - 'scattering_type': self.type.scattering_type.value, - 'beam_mode': self.type.beam_mode.value, + 'sample_form': self.experiment_type.sample_form.value, + 'scattering_type': self.experiment_type.scattering_type.value, + 'beam_mode': self.experiment_type.beam_mode.value, } if category is getattr(self, '_peak', None): return { 'calculator': calculator, - 'sample_form': self.type.sample_form.value, - 'scattering_type': self.type.scattering_type.value, - 'beam_mode': self.type.beam_mode.value, + 'sample_form': self.experiment_type.sample_form.value, + 'scattering_type': self.experiment_type.scattering_type.value, + 'beam_mode': self.experiment_type.beam_mode.value, } return {} @@ -306,9 +306,9 @@ def name(self, new: str) -> None: self._name = new @property - def type(self) -> object: # TODO: Consider another name + def experiment_type(self) -> object: """Experiment type: sample form, probe, beam mode.""" - return self._type + return self._experiment_type @property def measured_range(self) -> MeasuredRange | None: @@ -401,9 +401,9 @@ 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.""" - paragraph_title: str = f"Experiment 🔬 '{self.name}' as cif" + def show_as_text(self) -> None: + """Pretty-print the experiment as text.""" + paragraph_title: str = f"Experiment 🔬 '{self.name}' as text" console.paragraph(paragraph_title) render_cif(self._cif_for_display()) @@ -445,7 +445,7 @@ def _default_calculator_tag(self) -> str: from easydiffraction.analysis.calculators.factory import CalculatorFactory # noqa: PLC0415 return CalculatorFactory.default_tag( - scattering_type=self.type.scattering_type.value, + scattering_type=self.experiment_type.scattering_type.value, ) def _resolve_calculator(self) -> None: @@ -495,28 +495,28 @@ def __init__( self, *, name: str, - type: ExperimentType, + experiment_type: ExperimentType, ) -> None: - super().__init__(name=name, type=type) + super().__init__(name=name, experiment_type=experiment_type) self._extinction = ExtinctionFactory.create(ExtinctionFactory.default_tag()) - self._linked_crystal_type: str = LinkedCrystalFactory.default_tag() - self._linked_crystal = LinkedCrystalFactory.create(self._linked_crystal_type) + self._linked_structure_type: str = LinkedStructureFactory.default_tag() + self._linked_structure = LinkedStructureFactory.create(self._linked_structure_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, + scattering_type=self.experiment_type.scattering_type.value, + beam_mode=self.experiment_type.beam_mode.value, + sample_form=self.experiment_type.sample_form.value, ) self._instrument = InstrumentFactory.create(self._instrument_type) self._refln_type: str = ReflnFactory.default_tag( - sample_form=self.type.sample_form.value, - beam_mode=self.type.beam_mode.value, - scattering_type=self.type.scattering_type.value, + sample_form=self.experiment_type.sample_form.value, + beam_mode=self.experiment_type.beam_mode.value, + scattering_type=self.experiment_type.scattering_type.value, ) self._refln = ReflnFactory.create(self._refln_type) self._data_range_type: str = DataRangeFactory.default_tag( - beam_mode=self.type.beam_mode.value, - sample_form=self.type.sample_form.value, + beam_mode=self.experiment_type.beam_mode.value, + sample_form=self.experiment_type.sample_form.value, ) self._data_range = DataRangeFactory.create(self._data_range_type) self._resolve_calculator() @@ -553,13 +553,13 @@ def _restore_switchable_types(self, block: object) -> None: self._replace_extinction(extinction_tag, announce=False, strict=False) # ------------------------------------------------------------------ - # Linked crystal (read-only, single type) + # Linked structure (read-only, single type) # ------------------------------------------------------------------ @property - def linked_crystal(self) -> object: - """Linked crystal model for this experiment.""" - return self._linked_crystal + def linked_structure(self) -> object: + """Linked structure model for this experiment.""" + return self._linked_structure # ------------------------------------------------------------------ # Instrument (fixed at creation) @@ -603,40 +603,40 @@ def __init__( self, *, name: str, - type: ExperimentType, + experiment_type: ExperimentType, ) -> None: - super().__init__(name=name, type=type) + super().__init__(name=name, experiment_type=experiment_type) - self._linked_phases_type: str = LinkedPhasesFactory.default_tag() - self._linked_phases = LinkedPhasesFactory.create(self._linked_phases_type) + self._linked_structures_type: str = LinkedStructuresFactory.default_tag() + self._linked_structures = LinkedStructuresFactory.create(self._linked_structures_type) self._excluded_regions_type: str = ExcludedRegionsFactory.default_tag() self._excluded_regions = ExcludedRegionsFactory.create(self._excluded_regions_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, + sample_form=self.experiment_type.sample_form.value, + beam_mode=self.experiment_type.beam_mode.value, + scattering_type=self.experiment_type.scattering_type.value, ) self._data = DataFactory.create(self._data_type) self._data_range_type: str = DataRangeFactory.default_tag( - beam_mode=self.type.beam_mode.value, - sample_form=self.type.sample_form.value, + beam_mode=self.experiment_type.beam_mode.value, + sample_form=self.experiment_type.sample_form.value, ) self._data_range = DataRangeFactory.create(self._data_range_type) self._peak = PeakFactory.create( PeakFactory.default_tag( - scattering_type=self.type.scattering_type.value, - beam_mode=self.type.beam_mode.value, + scattering_type=self.experiment_type.scattering_type.value, + beam_mode=self.experiment_type.beam_mode.value, ) ) self._resolve_calculator() self._attach_category_parents() - def _get_valid_linked_phases( + def _get_valid_linked_structures( self, structures: Structures, ) -> list[Any]: """ - Get valid linked phases for this experiment. + Get valid linked structures for this experiment. Parameters ---------- @@ -646,26 +646,28 @@ def _get_valid_linked_phases( Returns ------- list[Any] - A list of valid linked phases. + A list of valid linked structures. """ - if not self.linked_phases: - log.warning('No linked phases defined. Returning empty pattern.') + if not self.linked_structures: + log.warning('No linked structures 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: + valid_linked_structures = [] + for linked_structure in self.linked_structures: + if linked_structure._identity.category_entry_name not in structures.names: log.warning( - f"Linked phase '{linked_phase.id.value}' not " + f"Linked structure '{linked_structure.structure_id.value}' not " f'found in Structures {structures.names}. Skipping it.' ) continue - valid_linked_phases.append(linked_phase) + valid_linked_structures.append(linked_structure) - if not valid_linked_phases: - log.warning('None of the linked phases found in Structures. Returning empty pattern.') + if not valid_linked_structures: + log.warning( + 'None of the linked structures found in Structures. Returning empty pattern.' + ) - return valid_linked_phases + return valid_linked_structures @abstractmethod def _load_ascii_data_to_experiment(self, data_path: str) -> int: @@ -685,9 +687,9 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: """ @property - def linked_phases(self) -> object: - """Collection of phases linked to this experiment.""" - return self._linked_phases + def linked_structures(self) -> object: + """Collection of structures linked to this experiment.""" + return self._linked_structures @property def excluded_regions(self) -> object: @@ -792,8 +794,8 @@ def _peak_profile_context(self) -> dict[str, object]: Return the context that resolves local peak profile aliases. """ return { - 'scattering_type': self.type.scattering_type.value, - 'beam_mode': self.type.beam_mode.value, + 'scattering_type': self.experiment_type.scattering_type.value, + 'beam_mode': self.experiment_type.beam_mode.value, } def _restore_switchable_types(self, block: object) -> None: diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py index dbee4ff31..2948f63ea 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_pd.py @@ -54,14 +54,14 @@ def __init__( self, *, name: str, - type: ExperimentType, + experiment_type: ExperimentType, ) -> None: - super().__init__(name=name, type=type) + super().__init__(name=name, experiment_type=experiment_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, + scattering_type=self.experiment_type.scattering_type.value, + beam_mode=self.experiment_type.beam_mode.value, + sample_form=self.experiment_type.sample_form.value, ) self._instrument = InstrumentFactory.create(self._instrument_type) self._background = BackgroundFactory.create(BackgroundFactory.default_tag()) @@ -76,9 +76,9 @@ def _refln_collection_tag(self) -> str: Return the reflection-collection tag for this beam mode. """ return ReflnFactory.default_tag( - sample_form=self.type.sample_form.value, - beam_mode=self.type.beam_mode.value, - scattering_type=self.type.scattering_type.value, + sample_form=self.experiment_type.sample_form.value, + beam_mode=self.experiment_type.beam_mode.value, + scattering_type=self.experiment_type.scattering_type.value, ) def _refln_collection_type(self) -> type[object]: diff --git a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py index f831e630b..d22b1a6a2 100644 --- a/src/easydiffraction/datablocks/experiment/item/bragg_sc.py +++ b/src/easydiffraction/datablocks/experiment/item/bragg_sc.py @@ -42,9 +42,9 @@ def __init__( self, *, name: str, - type: ExperimentType, + experiment_type: ExperimentType, ) -> None: - super().__init__(name=name, type=type) + super().__init__(name=name, experiment_type=experiment_type) def _load_ascii_data_to_experiment(self, data_path: str) -> int: """ @@ -107,9 +107,9 @@ def __init__( self, *, name: str, - type: ExperimentType, + experiment_type: ExperimentType, ) -> None: - super().__init__(name=name, type=type) + super().__init__(name=name, experiment_type=experiment_type) def _load_ascii_data_to_experiment(self, data_path: str) -> int: """ diff --git a/src/easydiffraction/datablocks/experiment/item/factory.py b/src/easydiffraction/datablocks/experiment/item/factory.py index d722d5729..59116753c 100644 --- a/src/easydiffraction/datablocks/experiment/item/factory.py +++ b/src/easydiffraction/datablocks/experiment/item/factory.py @@ -118,7 +118,7 @@ def _from_gemmi_block( param.from_cif(block) expt_class = cls._resolve_class(expt_type) - expt_obj = expt_class(name=name, type=expt_type) + expt_obj = expt_class(name=name, experiment_type=expt_type) # Restore switchable category types before loading parameters # so implementation-specific descriptors exist for from_cif. @@ -172,7 +172,7 @@ def from_scratch( scattering_type=scattering_type, ) expt_class = cls._resolve_class(expt_type) - return expt_class(name=name, type=expt_type) + return expt_class(name=name, experiment_type=expt_type) # TODO: add minimal default configuration for missing parameters @classmethod diff --git a/src/easydiffraction/datablocks/experiment/item/total_pd.py b/src/easydiffraction/datablocks/experiment/item/total_pd.py index 23004adf4..f00dfd6ba 100644 --- a/src/easydiffraction/datablocks/experiment/item/total_pd.py +++ b/src/easydiffraction/datablocks/experiment/item/total_pd.py @@ -42,9 +42,9 @@ class TotalPdExperiment(PdExperimentBase): def __init__( self, name: str, - type: ExperimentType, + experiment_type: ExperimentType, ) -> None: - super().__init__(name=name, type=type) + super().__init__(name=name, experiment_type=experiment_type) def _load_ascii_data_to_experiment(self, data_path: str) -> int: """ diff --git a/src/easydiffraction/datablocks/structure/categories/atom_site_aniso/default.py b/src/easydiffraction/datablocks/structure/categories/atom_site_aniso/default.py index 74151b645..bcb4102e1 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_site_aniso/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_site_aniso/default.py @@ -21,7 +21,7 @@ from easydiffraction.datablocks.structure.categories.atom_site_aniso.factory import ( AtomSiteAnisoFactory, ) -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class _AnisoAdpParameter(Parameter): @@ -67,14 +67,14 @@ def _owning_adp_type(self) -> str | None: # parameter). Any broken link falls back to the declared unit # rather than raising in a display path. aniso_item = getattr(self, '_parent', None) - label = getattr(getattr(aniso_item, '_label', None), 'value', None) + atom_id = getattr(getattr(aniso_item, '_id', None), 'value', None) collection = getattr(aniso_item, '_parent', None) structure = getattr(collection, '_parent', None) atom_sites = getattr(structure, 'atom_sites', None) - if atom_sites is None or label is None: + if atom_sites is None or atom_id is None: return None try: - atom = atom_sites[label] + atom = atom_sites[atom_id] except (KeyError, TypeError): return None return getattr(getattr(atom, 'adp_type', None), 'value', None) @@ -84,23 +84,23 @@ class AtomSiteAniso(CategoryItem): """ Single atom site anisotropic ADP entry. - Each entry mirrors an :class:`AtomSite` by label and holds six - tensor components whose physical meaning (B or U) is determined by + Each entry mirrors an :class:`AtomSite` by id and holds six tensor + components whose physical meaning (B or U) is determined by ``atom_site.adp_type``. """ _category_code = 'atom_site_aniso' - _category_entry_name = 'label' + _category_entry_name = 'id' def __init__(self) -> None: """Initialise with default zero-valued tensor components.""" super().__init__() - self._label = StringDescriptor( - name='label', - description='Atom-site label matching the parent atom_site entry.', + self._id = StringDescriptor( + name='id', + description='Atom-site id matching the parent atom_site entry.', value_spec=AttributeSpec(default=''), - cif_handler=CifHandler(names=['_atom_site_aniso.label']), + tags=TagSpec(edi_names=['_atom_site_aniso.id'], cif_names=['_atom_site_aniso.label']), ) self._adp_11 = _AnisoAdpParameter( @@ -117,12 +117,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0.0, le=10.0), ), - cif_handler=CifHandler( - names=[ + tags=TagSpec( + edi_names=['_atom_site_aniso.adp_11'], + cif_names=[ '_atom_site_aniso.B_11', '_atom_site_aniso.U_11', '_atom_site_aniso.beta_11', - ] + ], ), ) self._adp_22 = _AnisoAdpParameter( @@ -139,12 +140,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0.0, le=10.0), ), - cif_handler=CifHandler( - names=[ + tags=TagSpec( + edi_names=['_atom_site_aniso.adp_22'], + cif_names=[ '_atom_site_aniso.B_22', '_atom_site_aniso.U_22', '_atom_site_aniso.beta_22', - ] + ], ), ) self._adp_33 = _AnisoAdpParameter( @@ -161,12 +163,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0.0, le=10.0), ), - cif_handler=CifHandler( - names=[ + tags=TagSpec( + edi_names=['_atom_site_aniso.adp_33'], + cif_names=[ '_atom_site_aniso.B_33', '_atom_site_aniso.U_33', '_atom_site_aniso.beta_33', - ] + ], ), ) self._adp_12 = _AnisoAdpParameter( @@ -183,12 +186,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=[ + tags=TagSpec( + edi_names=['_atom_site_aniso.adp_12'], + cif_names=[ '_atom_site_aniso.B_12', '_atom_site_aniso.U_12', '_atom_site_aniso.beta_12', - ] + ], ), ) self._adp_13 = _AnisoAdpParameter( @@ -205,12 +209,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=[ + tags=TagSpec( + edi_names=['_atom_site_aniso.adp_13'], + cif_names=[ '_atom_site_aniso.B_13', '_atom_site_aniso.U_13', '_atom_site_aniso.beta_13', - ] + ], ), ) self._adp_23 = _AnisoAdpParameter( @@ -227,12 +232,13 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler( - names=[ + tags=TagSpec( + edi_names=['_atom_site_aniso.adp_23'], + cif_names=[ '_atom_site_aniso.B_23', '_atom_site_aniso.U_23', '_atom_site_aniso.beta_23', - ] + ], ), ) @@ -241,13 +247,13 @@ def __init__(self) -> None: # ------------------------------------------------------------------ @property - def label(self) -> StringDescriptor: - """Label matching the parent atom_site entry.""" - return self._label + def id(self) -> StringDescriptor: + """ID matching the parent atom_site entry.""" + return self._id - @label.setter - def label(self, value: str) -> None: - self._label.value = value + @id.setter + def id(self, value: str) -> None: + self._id.value = value @property def adp_11(self) -> Parameter: diff --git a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py index 1486f93b0..a8b56991b 100644 --- a/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py +++ b/src/easydiffraction/datablocks/structure/categories/atom_sites/default.py @@ -29,7 +29,7 @@ from easydiffraction.crystallography import crystallography as ecr from easydiffraction.datablocks.structure.categories.atom_sites.enums import AdpTypeEnum from easydiffraction.datablocks.structure.categories.atom_sites.factory import AtomSitesFactory -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.utils.logging import log @@ -42,7 +42,7 @@ class AtomSite(CategoryItem): """ _category_code = 'atom_site' - _category_entry_name = 'label' + _category_entry_name = 'id' def __init__(self) -> None: """Initialise the atom site with default descriptor values.""" @@ -56,21 +56,21 @@ def __init__(self) -> None: self._wyckoff_coord_baseline: tuple[float, float, float] | None = None self._wyckoff_key_baseline: tuple[str, str | None] | None = None - self._label = StringDescriptor( - name='label', + self._id = StringDescriptor( + name='id', description='Unique identifier for the atom site.', display_handler=DisplayHandler( - display_name='Label', - latex_name='Label', + display_name='ID', + latex_name='ID', ), 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? + # (keywords are not checked). CIF id is less strict. + # Do we need conversion between CIF and internal id? validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), - cif_handler=CifHandler(names=['_atom_site.label']), + tags=TagSpec(edi_names=['_atom_site.id'], cif_names=['_atom_site.label']), ) self._type_symbol = StringDescriptor( name='type_symbol', @@ -83,7 +83,7 @@ def __init__(self) -> None: default='Tb', validator=MembershipValidator(allowed=self._type_symbol_allowed_values), ), - cif_handler=CifHandler(names=['_atom_site.type_symbol']), + tags=TagSpec(edi_names=['_atom_site.type_symbol']), ) self._fract_x = Parameter( name='fract_x', @@ -96,7 +96,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_atom_site.fract_x']), + tags=TagSpec(edi_names=['_atom_site.fract_x']), ) self._fract_y = Parameter( name='fract_y', @@ -109,7 +109,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_atom_site.fract_y']), + tags=TagSpec(edi_names=['_atom_site.fract_y']), ) self._fract_z = Parameter( name='fract_z', @@ -122,7 +122,7 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(), ), - cif_handler=CifHandler(names=['_atom_site.fract_z']), + tags=TagSpec(edi_names=['_atom_site.fract_z']), ) self._wyckoff_letter = StringDescriptor( name='wyckoff_letter', @@ -138,12 +138,9 @@ def __init__(self) -> None: allowed=lambda: self._wyckoff_letter_allowed_values, ), ), - cif_handler=CifHandler( - names=[ - '_atom_site.Wyckoff_symbol', - '_atom_site.Wyckoff_letter', - '_atom_site.wyckoff_letter', - ] + tags=TagSpec( + edi_names=['_atom_site.wyckoff_letter'], + cif_names=['_atom_site.Wyckoff_symbol', '_atom_site.Wyckoff_letter'], ), ) self._multiplicity = IntegerDescriptor( @@ -155,7 +152,10 @@ def __init__(self) -> None: latex_name='Mult.', ), value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=['_atom_site.site_symmetry_multiplicity']), + tags=TagSpec( + edi_names=['_atom_site.multiplicity'], + cif_names=['_atom_site.site_symmetry_multiplicity'], + ), ) self._occupancy = Parameter( name='occupancy', @@ -169,7 +169,7 @@ def __init__(self) -> None: default=1.0, validator=RangeValidator(ge=0.0, le=1.0), ), - cif_handler=CifHandler(names=['_atom_site.occupancy']), + tags=TagSpec(edi_names=['_atom_site.occupancy']), ) self._adp_iso = Parameter( name='adp_iso', @@ -185,11 +185,9 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0.0, le=10.0), ), - cif_handler=CifHandler( - names=[ - '_atom_site.B_iso_or_equiv', - '_atom_site.U_iso_or_equiv', - ] + tags=TagSpec( + edi_names=['_atom_site.adp_iso'], + cif_names=['_atom_site.B_iso_or_equiv', '_atom_site.U_iso_or_equiv'], ), ) self._adp_type = EnumDescriptor( @@ -201,7 +199,7 @@ def __init__(self) -> None: display_name='ADP type', latex_name='ADP type', ), - cif_handler=CifHandler(names=['_atom_site.ADP_type', '_atom_site.adp_type']), + tags=TagSpec(edi_names=['_atom_site.adp_type'], cif_names=['_atom_site.ADP_type']), ) # ------------------------------------------------------------------ @@ -248,7 +246,7 @@ def _wyckoff_letter_allowed_values(self) -> list[str]: return [] positions = ecr.space_group_wyckoff_table( space_group.name_h_m.value, - space_group.it_coordinate_system_code.value, + space_group.coord_system_code.value, ) if positions is None: return [] @@ -398,7 +396,7 @@ def _get_aniso_entry(self) -> object | None: aniso_coll = getattr(structure, '_atom_site_aniso', None) if aniso_coll is None: return None - lbl = self._label.value + lbl = self._id.value if lbl in aniso_coll: return aniso_coll[lbl] return None @@ -537,7 +535,7 @@ def _reciprocal_lengths_for_conversion(self) -> tuple[float, float, float]: if cell is None: msg = ( f"Cannot convert the ADP type to or from 'beta' for atom " - f"'{self._label.value}': no unit cell is reachable. Add the atom " + f"'{self._id.value}': no unit cell is reachable. Add the atom " f'to a structure with a defined cell before switching to or from ' f'the beta tensor.' ) @@ -565,12 +563,12 @@ def _reorder_adp_cif_names(self, new_type: str) -> None: return is_u = AdpTypeEnum(new_type) in {AdpTypeEnum.UISO, AdpTypeEnum.UANI} if is_u: - self._adp_iso._cif_handler._names = [ + self._adp_iso._tags._cif_names = [ '_atom_site.U_iso_or_equiv', '_atom_site.B_iso_or_equiv', ] else: - self._adp_iso._cif_handler._names = [ + self._adp_iso._tags._cif_names = [ '_atom_site.B_iso_or_equiv', '_atom_site.U_iso_or_equiv', ] @@ -582,12 +580,12 @@ def _reorder_adp_cif_names(self, new_type: str) -> None: for suffix in ('11', '22', '33', '12', '13', '23'): param = getattr(aniso, f'_adp_{suffix}') if is_u: - param._cif_handler._names = [ + param._tags._cif_names = [ f'_atom_site_aniso.U_{suffix}', f'_atom_site_aniso.B_{suffix}', ] else: - param._cif_handler._names = [ + param._tags._cif_names = [ f'_atom_site_aniso.B_{suffix}', f'_atom_site_aniso.U_{suffix}', ] @@ -597,7 +595,7 @@ def _reorder_adp_cif_names_beta(self) -> None: Put the beta-family CIF names first for a beta-tensor atom. """ # adp_iso has no beta form; keep its B/U-equivalent ordering. - self._adp_iso._cif_handler._names = [ + self._adp_iso._tags._cif_names = [ '_atom_site.B_iso_or_equiv', '_atom_site.U_iso_or_equiv', ] @@ -606,7 +604,7 @@ def _reorder_adp_cif_names_beta(self) -> None: return for suffix in ('11', '22', '33', '12', '13', '23'): param = getattr(aniso, f'_adp_{suffix}') - param._cif_handler._names = [ + param._tags._cif_names = [ f'_atom_site_aniso.beta_{suffix}', f'_atom_site_aniso.B_{suffix}', f'_atom_site_aniso.U_{suffix}', @@ -617,7 +615,7 @@ def _reorder_adp_cif_names_beta(self) -> None: # ------------------------------------------------------------------ @property - def label(self) -> StringDescriptor: + def id(self) -> StringDescriptor: """ Unique identifier for the atom site. @@ -625,11 +623,11 @@ def label(self) -> StringDescriptor: ``StringDescriptor`` object. Assigning to it updates the parameter value. """ - return self._label + return self._id - @label.setter - def label(self, value: str) -> None: - self._label.value = value + @id.setter + def id(self, value: str) -> None: + self._id.value = value @property def type_symbol(self) -> StringDescriptor: @@ -883,7 +881,7 @@ def _apply_atomic_coordinates_symmetry_constraints( """ structure = self._parent name_hm = structure.space_group.name_h_m.value - coord_code = structure.space_group.it_coordinate_system_code.value + coord_code = structure.space_group.coord_system_code.value supported = ecr.space_group_wyckoff_table(name_hm, coord_code) is not None for atom in self._items: if atom._wyckoff_letter_needs_validation: @@ -924,7 +922,7 @@ def _mark_atom_untabulated( self._clear_fract_symmetry_constrained(atom) if atom.wyckoff_letter.value and not called_by_minimizer: log.warning( - f'Wyckoff letter of {atom.label.value} is stored but not ' + f'Wyckoff letter of {atom.id.value} is stored but not ' f'validated because the space group is untabulated' ) atom._wyckoff_coord_baseline = (atom.fract_x.value, atom.fract_y.value, atom.fract_z.value) @@ -962,7 +960,7 @@ def _detect_and_snap_atom( position = ecr.detect_wyckoff_position(name_hm, coord_code, coords) if position is not None and letter_before and position.letter != letter_before: log.warning( - f'change moved the Wyckoff letter of {atom.label.value} ' + f'change moved the Wyckoff letter of {atom.id.value} ' f'from {letter_before} to {position.letter}' ) if position is not None: @@ -995,12 +993,12 @@ def _detect_and_snap_atom( if moved and not called_by_minimizer: if not detect: log.warning( - f'coordinates of {atom.label.value} did not fit letter ' + f'coordinates of {atom.id.value} did not fit letter ' f'{position.letter} and were adjusted' ) elif letter_before and position.letter == letter_before: log.warning( - f'coordinates of {atom.label.value} were adjusted to satisfy ' + f'coordinates of {atom.id.value} were adjusted to satisfy ' f'Wyckoff letter {position.letter}' ) atom._wyckoff_coord_baseline = snapped @@ -1031,7 +1029,7 @@ def _apply_adp_symmetry_constraints( structure = self._parent aniso_types = {AdpTypeEnum.BANI.value, AdpTypeEnum.UANI.value, AdpTypeEnum.BETA.value} space_group_name = structure.space_group.name_h_m.value - space_group_coord_code = structure.space_group.it_coordinate_system_code.value + space_group_coord_code = structure.space_group.coord_system_code.value aniso_collection = structure.atom_site_aniso for atom in self._items: @@ -1043,7 +1041,7 @@ def _apply_adp_symmetry_constraints( wl = atom.wyckoff_letter.value if not wl: continue - lbl = atom.label.value + lbl = atom.id.value if lbl not in aniso_collection: continue aniso_entry = aniso_collection[lbl] diff --git a/src/easydiffraction/datablocks/structure/categories/cell/default.py b/src/easydiffraction/datablocks/structure/categories/cell/default.py index 7a735df00..5d64bc229 100644 --- a/src/easydiffraction/datablocks/structure/categories/cell/default.py +++ b/src/easydiffraction/datablocks/structure/categories/cell/default.py @@ -12,7 +12,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec @CellFactory.register @@ -49,7 +49,7 @@ def __init__(self) -> None: default=10.0, validator=RangeValidator(ge=0, le=30), ), - cif_handler=CifHandler(names=['_cell.length_a']), + tags=TagSpec(edi_names=['_cell.length_a']), ) self._length_b = Parameter( name='length_b', @@ -65,7 +65,7 @@ def __init__(self) -> None: default=10.0, validator=RangeValidator(ge=0, le=30), ), - cif_handler=CifHandler(names=['_cell.length_b']), + tags=TagSpec(edi_names=['_cell.length_b']), ) self._length_c = Parameter( name='length_c', @@ -81,7 +81,7 @@ def __init__(self) -> None: default=10.0, validator=RangeValidator(ge=0, le=30), ), - cif_handler=CifHandler(names=['_cell.length_c']), + tags=TagSpec(edi_names=['_cell.length_c']), ) self._angle_alpha = Parameter( name='angle_alpha', @@ -97,7 +97,7 @@ def __init__(self) -> None: default=90.0, validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler(names=['_cell.angle_alpha']), + tags=TagSpec(edi_names=['_cell.angle_alpha']), ) self._angle_beta = Parameter( name='angle_beta', @@ -113,7 +113,7 @@ def __init__(self) -> None: default=90.0, validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler(names=['_cell.angle_beta']), + tags=TagSpec(edi_names=['_cell.angle_beta']), ) self._angle_gamma = Parameter( name='angle_gamma', @@ -129,7 +129,7 @@ def __init__(self) -> None: default=90.0, validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler(names=['_cell.angle_gamma']), + tags=TagSpec(edi_names=['_cell.angle_gamma']), ) # ------------------------------------------------------------------ diff --git a/src/easydiffraction/datablocks/structure/categories/geom/default.py b/src/easydiffraction/datablocks/structure/categories/geom/default.py index 64882d71e..a11b06030 100644 --- a/src/easydiffraction/datablocks/structure/categories/geom/default.py +++ b/src/easydiffraction/datablocks/structure/categories/geom/default.py @@ -12,7 +12,7 @@ from easydiffraction.core.validation import RangeValidator from easydiffraction.core.variable import NumericDescriptor from easydiffraction.datablocks.structure.categories.geom.factory import GeomFactory -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec @GeomFactory.register @@ -36,16 +36,18 @@ def __init__(self) -> None: default=0.0, validator=RangeValidator(ge=0.0), ), - cif_handler=CifHandler(names=['_geom.min_bond_distance_cutoff']), + tags=TagSpec(edi_names=['_geom.min_bond_distance_cutoff']), ) - self._bond_distance_incr = NumericDescriptor( - name='bond_distance_incr', + self._bond_distance_inc = NumericDescriptor( + name='bond_distance_inc', description='Increment added to the summed bonding radii (angstrom).', value_spec=AttributeSpec( default=0.25, validator=RangeValidator(ge=0.0), ), - cif_handler=CifHandler(names=['_geom.bond_distance_incr']), + tags=TagSpec( + edi_names=['_geom.bond_distance_inc'], cif_names=['_geom.bond_distance_incr'] + ), ) @property @@ -58,13 +60,13 @@ def min_bond_distance_cutoff(self, value: float) -> None: self._min_bond_distance_cutoff.value = value @property - def bond_distance_incr(self) -> NumericDescriptor: + def bond_distance_inc(self) -> NumericDescriptor: """Increment added to the summed bonding radii (angstrom).""" - return self._bond_distance_incr + return self._bond_distance_inc - @bond_distance_incr.setter - def bond_distance_incr(self, value: float) -> None: - self._bond_distance_incr.value = value + @bond_distance_inc.setter + def bond_distance_inc(self, value: float) -> None: + self._bond_distance_inc.value = value @property def as_cif(self) -> str: diff --git a/src/easydiffraction/datablocks/structure/categories/space_group/default.py b/src/easydiffraction/datablocks/structure/categories/space_group/default.py index a8c34b548..49a45e1e0 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group/default.py @@ -17,7 +17,7 @@ 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 +from easydiffraction.io.cif.handler import TagSpec _CRYSTAL_SYSTEM_RANGES = ( (1, 2, 'triclinic'), @@ -33,12 +33,12 @@ @SpaceGroupFactory.register class SpaceGroup(CategoryItem): """ - Space group with H-M symbol and IT coordinate system code. + Space group with H-M symbol and 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. + Tables coordinate-system qualifier (``coord_system_code``). Changing + the symbol automatically resets the coordinate-system code to the + first allowed value for the new group. """ _category_code = 'space_group' @@ -65,38 +65,37 @@ def __init__(self) -> None: allowed=lambda: self._name_h_m_allowed_values, ), ), - cif_handler=CifHandler( - # TODO: Keep only version with "." and automate ... - names=[ + tags=TagSpec( + edi_names=['_space_group.name_h_m'], + cif_names=[ '_space_group.name_H-M_alt', - '_space_group.name_h_m', '_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', + self._coord_system_code = StringDescriptor( + name='coord_system_code', description='A qualifier identifying which setting in IT is used.', display_handler=DisplayHandler( display_name='IT code', latex_name='IT code', ), value_spec=AttributeSpec( - default=lambda: self._it_coordinate_system_code_default_value, + default=lambda: self._coord_system_code_default_value, validator=MembershipValidator( - allowed=lambda: self._it_coordinate_system_code_allowed_values + allowed=lambda: self._coord_system_code_allowed_values ), ), - cif_handler=CifHandler( - names=[ + tags=TagSpec( + edi_names=['_space_group.coord_system_code'], + cif_names=[ '_space_group.IT_coordinate_system_code', - '_space_group.it_coordinate_system_code', '_space_group_IT_coordinate_system_code', '_symmetry.IT_coordinate_system_code', '_symmetry_IT_coordinate_system_code', - ] + ], ), ) @@ -104,9 +103,9 @@ def __init__(self) -> None: # Private helper methods # ------------------------------------------------------------------ - def _reset_it_coordinate_system_code(self) -> None: + def _reset_coord_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 + self._coord_system_code.value = self._coord_system_code_default_value @property def _name_h_m_allowed_values(self) -> list[str]: @@ -121,7 +120,7 @@ def _name_h_m_allowed_values(self) -> list[str]: return ACCESIBLE_NAME_HM_SHORT @property - def _it_coordinate_system_code_allowed_values(self) -> list[str]: + def _coord_system_code_allowed_values(self) -> list[str]: """ Return allowed IT coordinate system codes for the current group. @@ -137,7 +136,7 @@ def _it_coordinate_system_code_allowed_values(self) -> list[str]: return codes or [''] @property - def _it_coordinate_system_code_default_value(self) -> str: + def _coord_system_code_default_value(self) -> str: """ Return the default IT coordinate system code. @@ -146,7 +145,7 @@ def _it_coordinate_system_code_default_value(self) -> str: str First element of the allowed codes list. """ - return self._it_coordinate_system_code_allowed_values[0] + return self._coord_system_code_allowed_values[0] # ------------------------------------------------------------------ # Public properties @@ -166,10 +165,10 @@ def name_h_m(self) -> StringDescriptor: @name_h_m.setter def name_h_m(self, value: str) -> None: self._name_h_m.value = value - self._reset_it_coordinate_system_code() + self._reset_coord_system_code() @property - def it_coordinate_system_code(self) -> StringDescriptor: + def coord_system_code(self) -> StringDescriptor: """ A qualifier identifying which setting in IT is used. @@ -177,11 +176,11 @@ def it_coordinate_system_code(self) -> StringDescriptor: ``StringDescriptor`` object. Assigning to it updates the parameter value. """ - return self._it_coordinate_system_code + return self._coord_system_code - @it_coordinate_system_code.setter - def it_coordinate_system_code(self, value: str) -> None: - self._it_coordinate_system_code.value = value + @coord_system_code.setter + def coord_system_code(self, value: str) -> None: + self._coord_system_code.value = value @property def crystal_system(self) -> str: diff --git a/src/easydiffraction/datablocks/structure/categories/space_group_wyckoff/default.py b/src/easydiffraction/datablocks/structure/categories/space_group_wyckoff/default.py index 0478cfe02..c077b75f9 100644 --- a/src/easydiffraction/datablocks/structure/categories/space_group_wyckoff/default.py +++ b/src/easydiffraction/datablocks/structure/categories/space_group_wyckoff/default.py @@ -26,7 +26,7 @@ from easydiffraction.datablocks.structure.categories.space_group_wyckoff.factory import ( SpaceGroupWyckoffFactory, ) -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec _READ_ONLY_MESSAGE = ( 'space_group_wyckoff is derived from the space group and is read-only; ' @@ -51,21 +51,21 @@ def __init__(self) -> None: description='Identifier of the Wyckoff position.', display_handler=DisplayHandler(display_name='ID', latex_name='ID'), value_spec=AttributeSpec(default=''), - cif_handler=CifHandler(names=['_space_group_Wyckoff.id']), + tags=TagSpec(edi_names=['_space_group_Wyckoff.id']), ) self._letter = StringDescriptor( name='letter', description='Wyckoff letter of the position.', display_handler=DisplayHandler(display_name='Letter', latex_name='Letter'), value_spec=AttributeSpec(default=''), - cif_handler=CifHandler(names=['_space_group_Wyckoff.letter']), + tags=TagSpec(edi_names=['_space_group_Wyckoff.letter']), ) self._multiplicity = IntegerDescriptor( name='multiplicity', description='Multiplicity of the Wyckoff position.', display_handler=DisplayHandler(display_name='Multiplicity', latex_name='Multiplicity'), value_spec=AttributeSpec(default=0), - cif_handler=CifHandler(names=['_space_group_Wyckoff.multiplicity']), + tags=TagSpec(edi_names=['_space_group_Wyckoff.multiplicity']), ) self._site_symmetry = StringDescriptor( name='site_symmetry', @@ -74,14 +74,14 @@ def __init__(self) -> None: display_name='Site symmetry', latex_name='Site symmetry' ), value_spec=AttributeSpec(default=''), - cif_handler=CifHandler(names=['_space_group_Wyckoff.site_symmetry']), + tags=TagSpec(edi_names=['_space_group_Wyckoff.site_symmetry']), ) self._coords_xyz = StringDescriptor( name='coords_xyz', description='Coordinates of the Wyckoff orbit.', display_handler=DisplayHandler(display_name='Coordinates', latex_name='Coordinates'), value_spec=AttributeSpec(default=''), - cif_handler=CifHandler(names=['_space_group_Wyckoff.coords_xyz']), + tags=TagSpec(edi_names=['_space_group_Wyckoff.coords_xyz']), ) @property @@ -208,7 +208,7 @@ def _replace_from_space_group(self) -> None: self._adopt_items([]) return name_hm = structure.space_group.name_h_m.value - coord_code = structure.space_group.it_coordinate_system_code.value + coord_code = structure.space_group.coord_system_code.value positions = ecr.space_group_wyckoff_table(name_hm, coord_code) if not positions: self._adopt_items([]) diff --git a/src/easydiffraction/datablocks/structure/collection.py b/src/easydiffraction/datablocks/structure/collection.py index 801b416aa..f3cc769ed 100644 --- a/src/easydiffraction/datablocks/structure/collection.py +++ b/src/easydiffraction/datablocks/structure/collection.py @@ -2,11 +2,14 @@ # SPDX-License-Identifier: BSD-3-Clause """Collection of structure data blocks.""" +import pathlib + 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.io.edi import edi_body_from_text from easydiffraction.utils.logging import console @@ -79,6 +82,23 @@ def add_from_cif_path( structure = StructureFactory.from_cif_path(cif_path) self.add(structure) + @typechecked + def add_from_edi_path( + self, + edi_path: str, + ) -> None: + """ + Create a structure from an Edi file and add it. + + Parameters + ---------- + edi_path : str + Filesystem path to an Edi structure file. + """ + body = edi_body_from_text(pathlib.Path(edi_path).read_text(encoding='utf-8')) + structure = StructureFactory.from_cif_str(body) + self.add(structure) + # TODO: Move to DatablockCollection? def show_names(self) -> None: """List all structure names in the collection.""" diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index 8e3df2854..9e817520f 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -204,33 +204,33 @@ def _sync_atom_site_aniso(self) -> None: Reconcile ``atom_site_aniso`` with anisotropic atoms only. Adds an entry for every atom whose ``adp_type`` is ``Bani`` or - ``Uani``, removes entries whose label is stale or whose atom has + ``Uani``, removes entries whose id is stale or whose atom has switched to an isotropic type, and reorders CIF names on all atom-site parameters to match each atom's ``adp_type``. """ aniso_types = {AdpTypeEnum.BANI, AdpTypeEnum.UANI, AdpTypeEnum.BETA} - existing_labels = {a.label.value for a in self._atom_sites} - aniso_labels_needed = { - a.label.value for a in self._atom_sites if a.adp_type.value in aniso_types + existing_ids = {a.id.value for a in self._atom_sites} + aniso_ids_needed = { + a.id.value for a in self._atom_sites if a.adp_type.value in aniso_types } - current_aniso_labels = {a.label.value for a in self._atom_site_aniso} + current_aniso_ids = {a.id.value for a in self._atom_site_aniso} # Add missing entries for anisotropic atoms for atom in self._atom_sites: - lbl = atom.label.value - if lbl not in current_aniso_labels and atom.adp_type.value in aniso_types: + atom_id = atom.id.value + if atom_id not in current_aniso_ids and atom.adp_type.value in aniso_types: entry = AtomSiteAniso() - entry.label = lbl + entry.id = atom_id self._atom_site_aniso.add(entry) - # Remove entries for isotropic atoms and stale labels + # Remove entries for isotropic atoms and stale ids stale = [ - a.label.value + a.id.value for a in self._atom_site_aniso - if a.label.value not in existing_labels or a.label.value not in aniso_labels_needed + if a.id.value not in existing_ids or a.id.value not in aniso_ids_needed ] - for lbl in stale: - self._atom_site_aniso.remove(lbl) + for atom_id in stale: + self._atom_site_aniso.remove(atom_id) # Reorder CIF names to match each atom's adp_type for atom in self._atom_sites: @@ -258,7 +258,7 @@ def _update_categories( # Public methods # ------------------------------------------------------------------ - def show_as_cif(self) -> None: - """Render the CIF text for this structure in the terminal.""" - console.paragraph(f"Structure 🧩 '{self.name}' as cif") + def show_as_text(self) -> None: + """Render the text for this structure in the terminal.""" + console.paragraph(f"Structure 🧩 '{self.name}' as text") render_cif(self._cif_for_display()) diff --git a/src/easydiffraction/display/links.py b/src/easydiffraction/display/links.py new file mode 100644 index 000000000..cf6911116 --- /dev/null +++ b/src/easydiffraction/display/links.py @@ -0,0 +1,64 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Small link-aware table cell helpers.""" + +from __future__ import annotations + +from collections import UserString + + +class TableLink(UserString): + """Clickable table cell text with a target URL.""" + + url: str + title: str | None + + def __init__( + self, + text: str, + url: str, + title: str | None = None, + ) -> None: + """ + Initialize a string-like link cell. + + Parameters + ---------- + text : str + Visible cell text. + url : str + Link target. + title : str | None, default=None + Optional tooltip text. + """ + super().__init__(text) + self.url = url + self.title = title + + @property + def text(self) -> str: + """Visible cell text.""" + return self.data + + +def parameter_docs_link(parameter: object) -> TableLink | str: + """ + Return a documentation link cell for a parameter-like object. + + Parameters + ---------- + parameter : object + Descriptor or parameter object with ``name`` and optional + ``url`` attributes. + + Returns + ------- + TableLink | str + Link-aware cell when a documentation URL is available, otherwise + the plain parameter name. + """ + name = str(getattr(parameter, 'name', 'N/A')) + url = getattr(parameter, 'url', None) + if isinstance(url, str) and url: + return TableLink(text=name, url=url, title=f'Documentation for {name}') + return name diff --git a/src/easydiffraction/display/plotters/base.py b/src/easydiffraction/display/plotters/base.py index ee837e7e8..f6132791f 100644 --- a/src/easydiffraction/display/plotters/base.py +++ b/src/easydiffraction/display/plotters/base.py @@ -26,14 +26,14 @@ @dataclass(frozen=True) class BraggTickSet: """ - Bragg tick data for one linked phase row. + Bragg tick data for one linked structure row. The plotting facade converts experiment reflection-category data into this display-specific container so plotting backends stay decoupled from experiment datablock internals. """ - phase_id: str + structure_id: str x: np.ndarray h: np.ndarray k: np.ndarray diff --git a/src/easydiffraction/display/plotters/plotly.py b/src/easydiffraction/display/plotters/plotly.py index 90c41e53e..b3fb4124b 100644 --- a/src/easydiffraction/display/plotters/plotly.py +++ b/src/easydiffraction/display/plotters/plotly.py @@ -1729,7 +1729,7 @@ def _get_bragg_tick_trace( color: str, ) -> object: """ - Create a hover-capable Bragg tick trace for one linked phase. + Create a Bragg tick hover trace for one linked structure. Only the Miller-index line is colored to match the phase tick marker; the phase name and x line use the default tooltip text @@ -1743,7 +1743,7 @@ def _get_bragg_tick_trace( index_k = int(tick_set.k[idx]) index_l = int(tick_set.ell[idx]) lines = [ - tick_set.phase_id, + tick_set.structure_id, f'x: {float(x_value):,.2f}', cls._hover_color_span( f'Miller indices: ({index_h} {index_k} {index_l})', @@ -1764,7 +1764,7 @@ def _get_bragg_tick_trace( 'line': {'width': BRAGG_TICK_MARKER_LINE_WIDTH}, 'color': color, }, - name=f'Bragg peaks: {tick_set.phase_id}', + name=f'Bragg peaks: {tick_set.structure_id}', text=hover_text, hovertemplate='%{text}', ) @@ -2470,7 +2470,7 @@ def _configure_bragg_axes( fig.update_yaxes( tickmode='array', tickvals=[float(idx + 1) for idx in range(len(plot_spec.bragg_tick_sets))], - ticktext=[tick_set.phase_id for tick_set in plot_spec.bragg_tick_sets], + ticktext=[tick_set.structure_id for tick_set in plot_spec.bragg_tick_sets], range=[float(len(plot_spec.bragg_tick_sets)) + 0.5, 0.5], showgrid=False, row=layout.bragg_row, diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 1334dbd98..2a53f252a 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -82,7 +82,7 @@ class PosteriorPairPlotStyleEnum(StrEnum): DEFAULT_CORRELATION_THRESHOLD: float | None = None -DEFAULT_CORRELATION_MAX_PARAMETERS = 6 +DEFAULT_CORRELATION_MAX_PARAMETERS = 5 EXPECTED_COVAR_NDIM = 2 DEFAULT_BRAGG_PEAKS_HEIGHT_FRACTION = 0.10 DEFAULT_RESID_HEIGHT = DEFAULT_RESIDUAL_HEIGHT_FRACTION @@ -637,7 +637,7 @@ def plot_meas( experiment, intensity_category_for(experiment), expt_name, - experiment.type, + experiment.experiment_type, plot_options, ) @@ -688,7 +688,7 @@ def plot_calc( experiment, intensity_category_for(experiment), expt_name, - experiment.type, + experiment.experiment_type, plot_options, ) @@ -776,7 +776,10 @@ def plot_calc_comparison( """ self._update_project_categories(expt_name) experiment = self._project.experiments[expt_name] - x_axis, _, sample_form, scattering_type, _ = self._resolve_x_axis(experiment.type, None) + x_axis, _, sample_form, scattering_type, _ = self._resolve_x_axis( + experiment.experiment_type, + None, + ) axes_labels = self._get_axes_labels(sample_form, scattering_type, x_axis) x = np.asarray(intensity_category_for(experiment).x, dtype=float) reference = np.asarray(reference, dtype=float) @@ -939,8 +942,8 @@ def plot_param_series( # Try CSV first (produced by fit_sequential or future fit) csv_path = None - if self._project.info.path is not None: - candidate = pathlib.Path(self._project.info.path) / 'analysis' / 'results.csv' + if self._project.metadata.path is not None: + candidate = pathlib.Path(self._project.metadata.path) / 'analysis' / 'results.csv' if candidate.is_file(): csv_path = str(candidate) @@ -1009,7 +1012,7 @@ def plot_all_param_series( column is used as the x-axis. When ``None``, the experiment sequence number is used instead. """ - unique_names = self._collect_fitted_param_unique_names() + unique_names = self._collect_fitted_parameter_unique_names() if not unique_names: log.warning('No fitted parameters found to plot.') return @@ -1023,7 +1026,7 @@ def plot_all_param_series( continue self.plot_param_series(param=descriptor, versus=versus) - def _collect_fitted_param_unique_names(self) -> list[str]: + def _collect_fitted_parameter_unique_names(self) -> list[str]: """ Return fitted parameter unique names from CSV or snapshots. """ @@ -1032,8 +1035,8 @@ def _collect_fitted_param_unique_names(self) -> list[str]: meta = set(_META_COLUMNS) csv_path = None - if self._project.info.path is not None: - candidate = pathlib.Path(self._project.info.path) / 'analysis' / 'results.csv' + if self._project.metadata.path is not None: + candidate = pathlib.Path(self._project.metadata.path) / 'analysis' / 'results.csv' if candidate.is_file(): csv_path = str(candidate) @@ -1291,7 +1294,7 @@ def _posterior_pair_uncertainty_multiplier( if parameter is None: return None - current = getattr(parameter, 'fit_bounds_uncertainty_multiplier', None) + current = getattr(parameter, 'bounds_uncertainty_multiplier', None) if current is None or not np.isfinite(float(current)): return None @@ -1448,7 +1451,7 @@ def _plot_posterior_predictive_request( self._update_project_categories(expt_name) experiment = self._project.experiments[expt_name] x_axis, _, sample_form, scattering_type, _ = self._resolve_x_axis( - experiment.type, + experiment.experiment_type, plot_options.x, ) @@ -1598,7 +1601,7 @@ def _plot_non_bragg_posterior_predictive( ctx = self._prepare_powder_context( pattern, expt_name, - experiment.type, + experiment.experiment_type, plot_options.x_min, plot_options.x_max, plot_options.x, @@ -1867,7 +1870,10 @@ def _correlation_dataframe_from_persisted_projection( ] for row in correlation_rows: - parameter_names.extend([row.param_unique_name_i.value, row.param_unique_name_j.value]) + parameter_names.extend([ + row.parameter_unique_name_i.value, + row.parameter_unique_name_j.value, + ]) parameter_names = list(dict.fromkeys(parameter_names)) if len(parameter_names) < MIN_POSTERIOR_PARAMETER_COUNT: return None @@ -1880,8 +1886,8 @@ def _correlation_dataframe_from_persisted_projection( ) wrote_any = False for row in correlation_rows: - i_name = row.param_unique_name_i.value - j_name = row.param_unique_name_j.value + i_name = row.parameter_unique_name_i.value + j_name = row.parameter_unique_name_j.value if i_name not in corr_df.index or j_name not in corr_df.index: continue corr_df.loc[i_name, j_name] = float(row.correlation.value) @@ -2836,8 +2842,18 @@ def _cached_posterior_pair_surface( sidecar_data = getattr(analysis, '_persisted_fit_state_sidecar', {}) pair_caches = sidecar_data.get('pair_caches', {}) for cache_data in pair_caches.values(): - cache_x = str(cache_data.get('param_unique_name_x', '')) - cache_y = str(cache_data.get('param_unique_name_y', '')) + cache_x = str( + cache_data.get( + 'parameter_unique_name_x', + cache_data.get('param_unique_name_x', ''), + ) + ) + cache_y = str( + cache_data.get( + 'parameter_unique_name_y', + cache_data.get('param_unique_name_y', ''), + ) + ) if {cache_x, cache_y} != {x_parameter_name, y_parameter_name}: continue @@ -4331,7 +4347,7 @@ def _plot_posterior_predictive_data( ctx = self._prepare_powder_context( pattern, expt_name, - experiment.type, + experiment.experiment_type, plot_options.x_min, plot_options.x_max, plot_options.x, @@ -5663,7 +5679,7 @@ def _plot_meas_vs_calc_data( X-range, residual, and x-axis selection options. """ pattern = intensity_category_for(experiment) - expt_type = experiment.type + expt_type = experiment.experiment_type x_axis, _, sample_form, scattering_type, _ = self._resolve_x_axis( expt_type, @@ -5976,7 +5992,7 @@ def _bragg_tick_arrays( """ arrays: dict[str, np.ndarray] = {} for name in ( - 'phase_id', + 'structure_id', 'index_h', 'index_k', 'index_l', @@ -6014,29 +6030,29 @@ def _group_bragg_tick_sets( mask: np.ndarray, ) -> tuple[BraggTickSet, ...]: """ - Group masked reflection arrays into per-phase tick sets. + Group masked reflection arrays into per-structure tick sets. """ - phase_ids = arrays['phase_id'][mask] - unique_phase_ids = [] - for raw_phase_id in phase_ids: + structure_ids = arrays['structure_id'][mask] + unique_structure_ids = [] + for raw_structure_id in structure_ids: if not any( - np.array_equal(raw_phase_id, existing_phase_id) - for existing_phase_id in unique_phase_ids + np.array_equal(raw_structure_id, existing_structure_id) + for existing_structure_id in unique_structure_ids ): - unique_phase_ids.append(raw_phase_id) + unique_structure_ids.append(raw_structure_id) tick_sets = [] - for raw_phase_id in unique_phase_ids: - phase_mask = mask & (arrays['phase_id'] == raw_phase_id) + for raw_structure_id in unique_structure_ids: + structure_mask = mask & (arrays['structure_id'] == raw_structure_id) tick_sets.append( BraggTickSet( - phase_id=str(raw_phase_id), - x=arrays['x'][phase_mask], - h=arrays['index_h'][phase_mask], - k=arrays['index_k'][phase_mask], - ell=arrays['index_l'][phase_mask], - f_squared_calc=arrays['f_squared_calc'][phase_mask], - f_calc=arrays['f_calc'][phase_mask], + structure_id=str(raw_structure_id), + x=arrays['x'][structure_mask], + h=arrays['index_h'][structure_mask], + k=arrays['index_k'][structure_mask], + ell=arrays['index_l'][structure_mask], + f_squared_calc=arrays['f_squared_calc'][structure_mask], + f_calc=arrays['f_calc'][structure_mask], ) ) @@ -6061,7 +6077,7 @@ def _bragg_tick_d_spacing( refln.time_of_flight, experiment.instrument.calib_d_to_tof_offset.value, experiment.instrument.calib_d_to_tof_linear.value, - experiment.instrument.calib_d_to_tof_quad.value, + experiment.instrument.calib_d_to_tof_quadratic.value, ) return refln.d_spacing diff --git a/src/easydiffraction/display/structure/builder.py b/src/easydiffraction/display/structure/builder.py index f6c067ddc..c755915b6 100644 --- a/src/easydiffraction/display/structure/builder.py +++ b/src/easydiffraction/display/structure/builder.py @@ -262,7 +262,7 @@ def _atom_shape( view = AtomViewEnum(style.atom_view.value) radius, substituted = radius_for(element, view.radius_model()) ball_radius = _display_radius(radius, style) - label = atom.label.value + label = atom.id.value adp_type = AdpTypeEnum(atom.adp_type.value) scale = float(chi.ppf(style.adp_probability.value, 3)) if ( @@ -306,7 +306,7 @@ def _atom_primitive( shape[1], shape[2], colour, - atom.label.value, + atom.id.value, asymmetric=asymmetric, ) else: @@ -314,11 +314,11 @@ def _atom_primitive( _vec3(centre), shape[1], colour, - atom.label.value, + atom.id.value, asymmetric=asymmetric, ) return ( - _SceneAtom(primitive, centre, element, colour, atom.label.value), + _SceneAtom(primitive, centre, element, colour, atom.id.value), substituted, ) @@ -341,7 +341,7 @@ def _wedge_atom( major_atom, _occ, major_element, major_colour, _radius, major_rot = max( rows, key=itemgetter(1) ) - label = '/'.join(r[0].label.value for r in rows) + label = '/'.join(r[0].id.value for r in rows) shape, _ = _atom_shape(major_atom, ctx, major_rot) if shape[0] == 'ellipsoid': primitive = AdpEllipsoid( @@ -502,7 +502,7 @@ def build_scene( cell = structure.cell matrix = ecr.orthogonalization_matrix(*_cell_lengths_angles(cell)) sg = structure.space_group - ops = ecr.symmetry_operators(sg.name_h_m.value, sg.it_coordinate_system_code.value) + ops = ecr.symmetry_operators(sg.name_h_m.value, sg.coord_system_code.value) sites = list(structure.atom_sites) ctx = _RenderContext( @@ -518,7 +518,7 @@ def build_scene( if 'bonds' in features: geom = getattr(structure, 'geom', None) geom_min = geom.min_bond_distance_cutoff.value if geom is not None else 0.0 - geom_incr = geom.bond_distance_incr.value if geom is not None else DEFAULT_BOND_INCR + geom_incr = geom.bond_distance_inc.value if geom is not None else DEFAULT_BOND_INCR bonds = tuple(_build_bonds(scene_atoms, geom_min, geom_incr)) show_atoms = 'atoms' in features diff --git a/src/easydiffraction/display/tablers/base.py b/src/easydiffraction/display/tablers/base.py index b04834835..ec73dac00 100644 --- a/src/easydiffraction/display/tablers/base.py +++ b/src/easydiffraction/display/tablers/base.py @@ -22,6 +22,16 @@ from easydiffraction.display.theme import LIGHT_AXIS_FRAME_COLOR from easydiffraction.utils._vendored.theme_detect import is_dark +# Single source of truth for the spacing of every notebook/site table +# output. Both the Rich (<pre>) and pandas (<table>) backends apply +# these inline so the table looks identical in JupyterLab and in the +# built docs site -- the site's theme CSS cannot reach a standalone +# notebook, so the values must travel inline rather than in a +# stylesheet. Tuned to sit between the host defaults: tight enough to +# group the rows, loose enough to stay readable. +TABLE_CELL_LINE_HEIGHT = '1.2' +TABLE_CELL_PADDING = '0.15em 0.5em' + class TableBackendBase(ABC): """ diff --git a/src/easydiffraction/display/tablers/pandas.py b/src/easydiffraction/display/tablers/pandas.py index fa42ab160..438bf6513 100644 --- a/src/easydiffraction/display/tablers/pandas.py +++ b/src/easydiffraction/display/tablers/pandas.py @@ -16,6 +16,9 @@ HTML = None display = None +from easydiffraction.display.links import TableLink +from easydiffraction.display.tablers.base import TABLE_CELL_LINE_HEIGHT +from easydiffraction.display.tablers.base import TABLE_CELL_PADDING from easydiffraction.display.tablers.base import TableBackendBase from easydiffraction.utils.environment import can_use_ipython_display from easydiffraction.utils.logging import log @@ -32,16 +35,19 @@ BORDER_COLOR = 'rgba(128, 128, 128, 0.4)' INDEX_COLOR = 'rgba(128, 128, 128, 0.7)' -# Compact cell metrics matching the Rich layout. ``border: 0`` and -# ``min-width: 0`` neutralise MkDocs Material's ``table:not([class])`` -# rules, which otherwise inject a per-row ``border-top`` (stray rules -# between rows) and ``th { min-width: 5rem }`` (over-wide columns) onto -# class-less embedded tables. ``white-space: nowrap`` keeps each cell on -# one line so a wide table scrolls horizontally rather than folding into -# multi-line rows. Inline values win over the theme stylesheet, so no -# CSS class or ``<style>`` block is needed. +# Compact cell metrics matching the Rich layout. Spacing comes from the +# shared ``TABLE_CELL_*`` constants so both backends stay in sync. +# ``border: 0`` and ``min-width: 0`` neutralise MkDocs Material's +# ``table:not([class])`` rules, which otherwise inject a per-row +# ``border-top`` (stray rules between rows) and a ``min-width`` on +# ``th`` (over-wide columns) onto class-less embedded tables. +# ``white-space: nowrap`` keeps each cell on one line so a wide table +# scrolls horizontally rather than folding into multi-line rows. Inline +# values win over the theme stylesheet, so no CSS class or ``<style>`` +# block is needed. _CELL_STYLE = ( - 'padding: 0.25em 0.5em; line-height: 1.15em; border: 0; min-width: 0; white-space: nowrap' + f'padding: {TABLE_CELL_PADDING}; line-height: {TABLE_CELL_LINE_HEIGHT}; ' + 'border: 0; min-width: 0; white-space: nowrap' ) _TRANSPARENT_ROW = 'background-color: transparent' @@ -71,6 +77,16 @@ def _cell_html(self, value: object) -> tuple[str, str | None]: tuple[str, str | None] HTML-escaped text and a CSS colour (``None`` when absent). """ + if isinstance(value, TableLink): + text = html.escape(value.text) + url = html.escape(value.url, quote=True) + title = '' + if value.title is not None: + escaped_title = html.escape(value.title, quote=True) + title = f' title="{escaped_title}"' + link = f'<a href="{url}"{title} target="_blank" rel="noopener noreferrer">{text}</a>' + return link, None + text = self._format_value(value) match = _RICH_COLOR_RE.fullmatch(text) colour = None diff --git a/src/easydiffraction/display/tablers/rich.py b/src/easydiffraction/display/tablers/rich.py index ceeb8dd9e..351f6cd05 100644 --- a/src/easydiffraction/display/tablers/rich.py +++ b/src/easydiffraction/display/tablers/rich.py @@ -9,6 +9,7 @@ from rich.box import Box from rich.console import Console from rich.table import Table +from rich.text import Text try: from IPython.display import HTML @@ -17,6 +18,8 @@ HTML = None display = None +from easydiffraction.display.links import TableLink +from easydiffraction.display.tablers.base import TABLE_CELL_LINE_HEIGHT from easydiffraction.display.tablers.base import TableBackendBase from easydiffraction.utils.environment import can_use_ipython_display from easydiffraction.utils.logging import ConsoleManager @@ -39,6 +42,24 @@ class RichTableBackend(TableBackendBase): """Render tables to terminal or Jupyter using the Rich library.""" + def _format_cell(self, value: object) -> object: + """ + Return one Rich-compatible table cell. + + Parameters + ---------- + value : object + Raw cell value. + + Returns + ------- + object + Renderable table cell. + """ + if isinstance(value, TableLink): + return Text(value.text, style=f'link {value.url}') + return self._format_value(value) + @staticmethod def _to_html(table: Table) -> str: """ @@ -60,10 +81,15 @@ def _to_html(table: Table) -> str: tmp = Console(force_jupyter=False, record=True, file=io.StringIO()) tmp.print(table) html = tmp.export_html(inline_styles=True) - # Remove margins inside pre blocks and adjust font size + # Merge the compact spacing into Rich's own ``<pre>`` style + # attribute (rather than prepending a second, conflicting one) + # so the font-family Rich emits is preserved. Line height comes + # from the shared constant, keeping this in sync with the pandas + # table backend. return html.replace( - '<pre ', - "<pre style='margin:0; font-size: 0.9em !important; ' ", + '<pre style="font-family:', + f'<pre style="margin:0; font-size:0.9em !important; ' + f'line-height:{TABLE_CELL_LINE_HEIGHT} !important; font-family:', ) def build_renderable( @@ -104,7 +130,7 @@ def build_renderable( # Rows for idx, row_values in df.iterrows(): - formatted_row = [self._format_value(v) for v in row_values] + formatted_row = [self._format_cell(v) for v in row_values] table.add_row(str(idx), *formatted_row) return table diff --git a/src/easydiffraction/io/ascii.py b/src/easydiffraction/io/ascii.py index dcbea91b3..7cda34217 100644 --- a/src/easydiffraction/io/ascii.py +++ b/src/easydiffraction/io/ascii.py @@ -36,10 +36,9 @@ def extract_project_from_zip( """ Extract a project directory from a ZIP archive. - The archive must contain exactly one directory with a - ``project.cif`` file. Files are extracted into *destination* when - provided, or into a temporary directory that persists for the - lifetime of the process. + The archive must contain a project directory with ``project.edi``. + Files are extracted into *destination* when provided, or into a + temporary directory that persists for the lifetime of the process. Parameters ---------- @@ -54,15 +53,14 @@ def extract_project_from_zip( Returns ------- str - Absolute path to the extracted project directory (the directory - that contains ``project.cif``). + Absolute path to the extracted project directory. Raises ------ FileNotFoundError If *zip_path* does not exist. ValueError - If the archive does not contain a ``project.cif`` file. + If the archive does not contain a project marker file. """ zip_path = Path(zip_path) if not zip_path.exists(): @@ -74,16 +72,17 @@ def extract_project_from_zip( with zipfile.ZipFile(zip_path, 'r') as zf: # Determine the project directory from the archive contents # *before* extraction, so we are not confused by unrelated - # project.cif files already present in the destination. - project_cif_entries = [name for name in zf.namelist() if name.endswith('project.cif')] - if not project_cif_entries: - msg = f'No project.cif found in ZIP archive: {zip_path}' + # project marker files already present in the destination. + names = zf.namelist() + project_entries = [name for name in names if name.endswith('project.edi')] + if not project_entries: + msg = f'No project.edi found in ZIP archive: {zip_path}' raise ValueError(msg) zf.extractall(extract_dir) - project_cif_path = extract_dir / project_cif_entries[0] - return str(project_cif_path.parent.resolve()) + project_marker_path = extract_dir / project_entries[0] + return str(project_marker_path.parent.resolve()) def extract_data_paths_from_zip( diff --git a/src/easydiffraction/io/cif/handler.py b/src/easydiffraction/io/cif/handler.py index bd82771a6..3166720f2 100644 --- a/src/easydiffraction/io/cif/handler.py +++ b/src/easydiffraction/io/cif/handler.py @@ -1,21 +1,47 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Minimal CIF tag handler used by descriptors/parameters.""" +""" +Tag specification used by descriptors/parameters. -from __future__ import annotations +A :class:`TagSpec` records the names a single descriptor/parameter uses +across the two persistence/exchange formats, plus its documentation +location: + +* ``edi_names`` — names searched and written for the **Edi** project + format (``.edi`` files). ``edi_name`` (``edi_names[0]``) is the + canonical name used when writing. +* ``cif_names`` — names searched and written for strict **CIF** + import/export (``.cif`` files, including the report). ``cif_name`` + (``cif_names[0]``) is the canonical name used when exporting; it is + an IUCr/pdCIF dictionary name where one exists and an + ``_easydiffraction_*`` extension otherwise. Remaining entries are + additional spellings accepted on CIF import. Defaults to ``edi_names`` + when not given. +Both lists are ordered by priority: the first entry is canonical for +writing, and the whole list is accepted on read. +""" + +from __future__ import annotations -class CifHandler: - """ - 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. - """ +class TagSpec: + """Per-descriptor tag specification across Edi and CIF formats.""" - def __init__(self, *, names: list[str], iucr_name: str | None = None) -> None: - self._names = names - self._iucr_name = iucr_name + def __init__( + self, + *, + edi_names: list[str], + edi_name: str | None = None, + cif_names: list[str] | None = None, + docs_page: str | None = None, + docs_anchor: str | None = None, + ) -> None: + self._edi_names = edi_names + self._explicit_edi_name = edi_name + self._cif_names = cif_names + self._docs_page = docs_page + self._docs_anchor = docs_anchor self._owner = None # set by attach def attach(self, owner: object) -> None: @@ -23,16 +49,78 @@ def attach(self, owner: object) -> None: self._owner = owner @property - def names(self) -> list[str]: - """List of CIF tag names associated with the owner.""" - return self._names + def edi_names(self) -> list[str]: + """Edi tag names accepted on read; first is canonical.""" + return self._edi_names + + @property + def edi_name(self) -> str: + """Canonical Edi tag used when writing ``.edi`` files.""" + if self._explicit_edi_name is not None: + return self._explicit_edi_name + return self._edi_names[0] + + @property + def edi_read_names(self) -> list[str]: + """Accepted ``.edi`` load names, in lookup order.""" + return list(dict.fromkeys([self.edi_name, *self._edi_names])) @property - def iucr_name(self) -> str: - """IUCr-side CIF tag name for export writers.""" - if self._iucr_name is not None: - return self._iucr_name - return self._names[0] + def cif_names(self) -> list[str]: + """CIF tag names; first is canonical for export.""" + if self._cif_names is not None: + return self._cif_names + return self._edi_names + + @property + def cif_name(self) -> str: + """Canonical CIF tag used by report/strict-CIF export.""" + return self.cif_names[0] + + @property + def cif_read_names(self) -> list[str]: + """Accepted ``.cif`` import names, in lookup order.""" + return list(dict.fromkeys(self.cif_names)) + + @property + def read_names(self) -> list[str]: + """Names accepted on read across both formats (union).""" + return list(dict.fromkeys([self.edi_name, *self._edi_names, *self.cif_names])) + + @property + def category_name(self) -> str: + """Project data category name derived from the Edi tag.""" + return _split_data_name(self.edi_name)[0] + + @property + def category_entry_name(self) -> str: + """Project data item name derived from the Edi tag.""" + return _split_data_name(self.edi_name)[1] + + @property + def docs_page(self) -> str: + """Parameter documentation page name.""" + if self._docs_page is not None: + return self._docs_page + return self.category_name + + @property + def docs_anchor(self) -> str: + """Parameter documentation anchor.""" + if self._docs_anchor is not None: + return self._docs_anchor + return _docs_anchor(self.category_name, self.category_entry_name) + + @property + def url(self) -> str: + """Versioned online documentation URL for this descriptor.""" + from easydiffraction.utils.utils import parameter_docs_url # noqa: PLC0415 + + return parameter_docs_url( + self.edi_name, + page=self._docs_page, + anchor=self._docs_anchor, + ) @property def uid(self) -> str | None: @@ -40,3 +128,15 @@ def uid(self) -> str | None: if self._owner is None: return None return self._owner.unique_name + + +def _split_data_name(data_name: str) -> tuple[str, str]: + """Split a data name into category and item components.""" + category, _, item = data_name.strip().lstrip('_').partition('.') + return category, item + + +def _docs_anchor(category: str, item: str) -> str: + """Return the stable docs anchor for a category item.""" + parts = [part for part in (category, item) if part] + return '-'.join(parts).replace('_', '-').lower() diff --git a/src/easydiffraction/io/cif/iucr_transformers.py b/src/easydiffraction/io/cif/iucr_transformers.py index 830d180ad..fa8dbf5ad 100644 --- a/src/easydiffraction/io/cif/iucr_transformers.py +++ b/src/easydiffraction/io/cif/iucr_transformers.py @@ -151,8 +151,8 @@ def loop(experiment: object) -> IucrLoop | None: for row_id, power, attr_name in ( ('offset', 0, 'calib_d_to_tof_offset'), ('linear', 1, 'calib_d_to_tof_linear'), - ('quad', 2, 'calib_d_to_tof_quad'), - ('recip', -1, 'calib_d_to_tof_recip'), + ('quad', 2, 'calib_d_to_tof_quadratic'), + ('recip', -1, 'calib_d_to_tof_reciprocal'), ): coeff = _attribute_value(instrument, attr_name) if _finite_number(coeff) == 0: @@ -413,18 +413,18 @@ def _iucr_items( def _iucr_descriptor(owner: object, attr_name: str) -> object | None: """Return the descriptor carrying IUCr metadata for *attr_name*.""" descriptor = getattr(owner, attr_name, None) - if getattr(descriptor, '_cif_handler', None) is not None: + if getattr(descriptor, '_tags', None) is not None: return descriptor if attr_name == 'type': private_descriptor = getattr(owner, '_type', None) - if getattr(private_descriptor, '_cif_handler', None) is not None: + if getattr(private_descriptor, '_tags', None) is not None: return private_descriptor return None def _iucr_item(descriptor: object, value: object) -> IucrItem: """Return one IUCr-tagged item for a descriptor.""" - return IucrItem(descriptor._cif_handler.iucr_name, value) + return IucrItem(descriptor._tags.cif_name, value) def _collection_values(collection: object) -> Iterable[object]: diff --git a/src/easydiffraction/io/cif/iucr_writer.py b/src/easydiffraction/io/cif/iucr_writer.py index c6f9d7805..dbfff12f0 100644 --- a/src/easydiffraction/io/cif/iucr_writer.py +++ b/src/easydiffraction/io/cif/iucr_writer.py @@ -47,7 +47,7 @@ def write_iucr_cif( Project instance to export. path : str | pathlib.Path | None, default=None Target CIF path. When omitted, the report is written to - ``<project.info.path>/reports/<project.name>.cif``. + ``<project.metadata.path>/reports/<project.name>.cif``. Returns ------- @@ -209,7 +209,7 @@ def _write_space_group_section(lines: list[str], structure: object) -> None: _write_item( lines, '_space_group.IT_coordinate_system_code', - _attribute_value(space_group, 'it_coordinate_system_code'), + _attribute_value(space_group, 'coord_system_code'), ) _write_item(lines, '_space_group.crystal_system', '?') @@ -225,7 +225,7 @@ def _write_symmetry_operations_section(lines: list[str], structure: object) -> N def _write_diffrn_section(lines: list[str], experiment: object) -> None: """Append diffraction metadata.""" diffrn = getattr(experiment, 'diffrn', None) - expt_type = getattr(experiment, 'type', None) + expt_type = getattr(experiment, 'experiment_type', None) _section(lines, 'Diffraction') _write_item( lines, @@ -287,15 +287,15 @@ def _write_atom_site_sections(lines: list[str], structure: object) -> None: def _write_atom_site_aniso_sections(lines: list[str], structure: object) -> None: """Append anisotropic ADP loops grouped by ADP convention.""" aniso_sites = list(_collection_values(getattr(structure, 'atom_site_aniso', None))) - atom_site_by_label = { - str(_attribute_value(atom_site, 'label')): atom_site + atom_site_by_id = { + str(_attribute_value(atom_site, 'id')): atom_site for atom_site in _collection_values(getattr(structure, 'atom_sites', None)) } for family in ('B', 'U', 'beta'): rows = [ _atom_site_aniso_row(aniso_site) for aniso_site in aniso_sites - if _adp_family(_atom_site_for_aniso(atom_site_by_label, aniso_site)) == family + if _adp_family(_atom_site_for_aniso(atom_site_by_id, aniso_site)) == family ] if not rows: continue @@ -465,7 +465,7 @@ def _write_powder_phase_reference_section( _write_item( lines, '_pd_phase_block.scale', - _attribute_value(phase.linked_phase, 'scale'), + _attribute_value(phase.linked_structure, 'scale'), ) @@ -513,7 +513,7 @@ def _write_powder_measurement_section( ) -> None: """Append powder measurement metadata.""" data_items = list(_collection_values(getattr(experiment, 'data', None))) - expt_type = getattr(experiment, 'type', None) + expt_type = getattr(experiment, 'experiment_type', None) _section(lines, 'Powder measurement') _write_item(lines, '_pd_meas.scan_method', _attribute_value(expt_type, 'beam_mode')) _write_item(lines, '_pd_meas.number_of_points', len(data_items)) @@ -624,7 +624,7 @@ def _write_pref_orient_loop(lines: list[str], experiment: object) -> None: [ ( str(index), - _attribute_value(row, 'phase_id'), + _attribute_value(row, 'structure_id'), _attribute_value(row, 'index_h'), _attribute_value(row, 'index_k'), _attribute_value(row, 'index_l'), @@ -773,7 +773,7 @@ def _report_path( if path is not None: return pathlib.Path(path) - project_path = getattr(getattr(project, 'info', None), 'path', None) + project_path = getattr(getattr(project, 'metadata', None), 'path', None) if project_path is None: msg = 'Project has no saved path. Save the project first.' raise FileNotFoundError(msg) @@ -809,7 +809,13 @@ def _role_descriptor_value(role: object, attr_name: str) -> object: def _software_role_label(project: object, role_name: str) -> str: """Return a persisted software role label or CIF unknown.""" software = _analysis_software(project) - role = getattr(software, role_name, None) + if software is None: + role = None + else: + try: + role = software[role_name] + except (KeyError, TypeError): + role = None name = _role_descriptor_value(role, 'name') if name in {None, ''}: return '?' @@ -822,8 +828,7 @@ def _software_role_label(project: object, role_name: str) -> str: def _software_fit_datetime(project: object) -> object | None: """Return the persisted fit timestamp, if available.""" - software = _analysis_software(project) - timestamp = _descriptor_value(getattr(software, 'timestamp', None)) + timestamp = getattr(getattr(project, 'metadata', None), 'timestamp', None) if timestamp in {None, ''}: return None return timestamp @@ -860,9 +865,13 @@ def _single_crystal_experiments(project: object) -> list[object]: experiment for experiment in experiments if ( - _attribute_value(getattr(experiment, 'type', None), 'sample_form') == 'single crystal' + _attribute_value( + getattr(experiment, 'experiment_type', None), + 'sample_form', + ) + == 'single crystal' and _attribute_value( - getattr(experiment, 'type', None), + getattr(experiment, 'experiment_type', None), 'scattering_type', ) == 'bragg' @@ -874,7 +883,7 @@ def _linked_structure(project: object, experiment: object) -> object: """Return the structure linked to a single-crystal experiment.""" structures = getattr(project, 'structures', None) names = getattr(structures, 'names', ()) - linked_id = _attribute_value(getattr(experiment, 'linked_crystal', None), 'id') + linked_id = _attribute_value(getattr(experiment, 'linked_structure', None), 'structure_id') if linked_id in names: return structures[linked_id] @@ -940,7 +949,7 @@ def _atom_site_tags(family: str) -> tuple[str, ...]: def _atom_site_row(atom_site: object) -> tuple[object, ...]: """Return one atom-site loop row.""" return ( - _attribute_descriptor(atom_site, 'label'), + _attribute_descriptor(atom_site, 'id'), _attribute_descriptor(atom_site, 'type_symbol'), _attribute_descriptor(atom_site, 'fract_x'), _attribute_descriptor(atom_site, 'fract_y'), @@ -969,7 +978,7 @@ def _atom_site_aniso_tags(family: str) -> tuple[str, ...]: def _atom_site_aniso_row(aniso_site: object) -> tuple[object, ...]: """Return one anisotropic-ADP loop row.""" return ( - _attribute_descriptor(aniso_site, 'label'), + _attribute_descriptor(aniso_site, 'id'), _attribute_descriptor(aniso_site, 'adp_11'), _attribute_descriptor(aniso_site, 'adp_22'), _attribute_descriptor(aniso_site, 'adp_33'), @@ -980,12 +989,12 @@ def _atom_site_aniso_row(aniso_site: object) -> tuple[object, ...]: def _atom_site_for_aniso( - atom_site_by_label: dict[str, object], + atom_site_by_id: dict[str, object], aniso_site: object, ) -> object | None: """Return the atom-site row that owns an anisotropic-ADP row.""" - label = str(_attribute_value(aniso_site, 'label')) - return atom_site_by_label.get(label) + atom_id = str(_attribute_value(aniso_site, 'id')) + return atom_site_by_id.get(atom_id) def _adp_family(atom_site: object) -> str: @@ -1041,12 +1050,12 @@ def _finite_number(value: object) -> float | None: def _sc_extension_items(experiment: object) -> list[tuple[str, object]]: """Return EasyDiffraction extension items for a SC block.""" - linked_crystal = getattr(experiment, 'linked_crystal', None) + linked_structure = getattr(experiment, 'linked_structure', None) diffrn = getattr(experiment, 'diffrn', None) - expt_type = getattr(experiment, 'type', None) + expt_type = getattr(experiment, 'experiment_type', None) calculator = getattr(experiment, 'calculator', None) items: list[tuple[str, object]] = [] - items.extend(_iucr_items(linked_crystal, ('id', 'scale'))) + items.extend(_iucr_items(linked_structure, ('structure_id', 'scale'))) items.extend( _iucr_items( diffrn, @@ -1092,9 +1101,13 @@ def _powder_rietveld_experiments(project: object) -> list[object]: experiment for experiment in experiments if ( - _attribute_value(getattr(experiment, 'type', None), 'sample_form') == 'powder' + _attribute_value( + getattr(experiment, 'experiment_type', None), + 'sample_form', + ) + == 'powder' and _attribute_value( - getattr(experiment, 'type', None), + getattr(experiment, 'experiment_type', None), 'scattering_type', ) == 'bragg' @@ -1111,7 +1124,7 @@ def _powder_phases( phases: list[_PowderPhase] = [] seen_structure_names: set[str] = set() for experiment in experiments: - for structure, linked_phase in _linked_powder_structures(project, experiment): + for structure, linked_structure in _linked_powder_structures(project, experiment): structure_name = str(getattr(structure, 'name', len(phases) + 1)) if structure_name in seen_structure_names: continue @@ -1120,7 +1133,7 @@ def _powder_phases( _PowderPhase( block_name=_unique_block_name(structure_name, used_block_names), structure=structure, - linked_phase=linked_phase, + linked_structure=linked_structure, ) ) return phases @@ -1150,25 +1163,31 @@ def _linked_powder_structures( """Return structures linked to a powder experiment.""" structures = getattr(project, 'structures', None) names = getattr(structures, 'names', ()) - linked_phases = list(_collection_values(getattr(experiment, 'linked_phases', None))) - linked_structures: list[tuple[object, object | None]] = [] + linked_structure_rows = list( + _collection_values(getattr(experiment, 'linked_structures', None)) + ) + linked_pairs: list[tuple[object, object | None]] = [] - for linked_phase in linked_phases: - phase_id = _attribute_value(linked_phase, 'id') - if phase_id in names: - linked_structures.append((structures[phase_id], linked_phase)) + for linked_structure in linked_structure_rows: + structure_id = _attribute_value(linked_structure, 'structure_id') + if structure_id in names: + linked_pairs.append((structures[structure_id], linked_structure)) - if linked_structures: - return linked_structures + if linked_pairs: + return linked_pairs structure_values = list(_collection_values(structures)) if len(structure_values) == 1: - return [(structure_values[0], linked_phases[0] if linked_phases else None)] + linked_structure = linked_structure_rows[0] if linked_structure_rows else None + return [(structure_values[0], linked_structure)] experiment_name = getattr(experiment, 'name', type(experiment).__name__) - linked_ids = [_attribute_value(linked_phase, 'id') for linked_phase in linked_phases] + linked_ids = [ + _attribute_value(linked_structure, 'structure_id') + for linked_structure in linked_structure_rows + ] msg = ( - f"Experiment '{experiment_name}' links phases {linked_ids}, " + f"Experiment '{experiment_name}' links structures {linked_ids}, " f'but project structures are {list(names)}.' ) raise ValueError(msg) @@ -1205,7 +1224,7 @@ def _phase_block_names_for_experiment( """Return phase block names linked to one powder pattern.""" linked_structures = _linked_powder_structures(project, experiment) linked_names = { - getattr(structure, 'name', None) for structure, _linked_phase in linked_structures + getattr(structure, 'name', None) for structure, _linked_structure in linked_structures } return [ phase.block_name @@ -1216,7 +1235,10 @@ def _phase_block_names_for_experiment( def _is_tof_experiment(experiment: object) -> bool: """Return whether a pattern uses time-of-flight x coordinates.""" - return _attribute_value(getattr(experiment, 'type', None), 'beam_mode') == 'time-of-flight' + return ( + _attribute_value(getattr(experiment, 'experiment_type', None), 'beam_mode') + == 'time-of-flight' + ) def _powder_x_tag(experiment: object) -> str: @@ -1265,14 +1287,14 @@ def _powder_refln_row(refln: object) -> tuple[object, ...]: _attribute_value(refln, 'index_l'), '?', _attribute_value(refln, 'f_squared_calc'), - _attribute_value(refln, 'phase_id'), + _attribute_value(refln, 'structure_id'), _attribute_value(refln, 'd_spacing'), ) def _powder_extension_items(experiment: object) -> list[tuple[str, object]]: """Return EasyDiffraction extension items for a powder block.""" - expt_type = getattr(experiment, 'type', None) + expt_type = getattr(experiment, 'experiment_type', None) calculator = getattr(experiment, 'calculator', None) peak = getattr(experiment, 'peak', None) background = getattr(experiment, 'background', None) @@ -1310,7 +1332,7 @@ class _PowderPhase: block_name: str structure: object - linked_phase: object | None + linked_structure: object | None @dataclass(frozen=True) @@ -1402,7 +1424,7 @@ def _descriptor_value(value: object) -> object: def _is_cif_descriptor(value: object) -> bool: """Return whether value is a CIF descriptor or parameter.""" - return hasattr(value, 'value') and hasattr(value, '_cif_handler') + return hasattr(value, 'value') and hasattr(value, '_tags') def _iucr_items(owner: object, attr_names: tuple[str, ...]) -> list[tuple[str, object]]: @@ -1413,15 +1435,15 @@ def _iucr_items(owner: object, attr_names: tuple[str, ...]) -> list[tuple[str, o def _iucr_item(owner: object, attr_name: str) -> tuple[str, object]: - """Return one ``(iucr_name, value)`` pair for a descriptor.""" + """Return one ``(cif_name, value)`` pair for a descriptor.""" descriptor = _iucr_descriptor(owner, attr_name) - return descriptor._cif_handler.iucr_name, descriptor + return descriptor._tags.cif_name, descriptor def _iucr_descriptor(owner: object, attr_name: str) -> object: """Return the descriptor carrying CIF metadata for *attr_name*.""" descriptor = getattr(owner, attr_name) - if hasattr(descriptor, '_cif_handler'): + if hasattr(descriptor, '_tags'): return descriptor for descriptor in _owner_descriptors(owner): @@ -1438,21 +1460,21 @@ def _iucr_descriptor_for_tag(owner: object, tag: str) -> object | None: return None private_type = getattr(owner, '_type', None) - if _descriptor_iucr_name(private_type) == tag: + if _descriptor_cif_name(private_type) == tag: return private_type for descriptor in _owner_descriptors(owner): - if _descriptor_iucr_name(descriptor) == tag: + if _descriptor_cif_name(descriptor) == tag: return descriptor return None -def _descriptor_iucr_name(descriptor: object) -> str | None: +def _descriptor_cif_name(descriptor: object) -> str | None: """Return a descriptor's IUCr tag name, if present.""" - handler = getattr(descriptor, '_cif_handler', None) + handler = getattr(descriptor, '_tags', None) if handler is None: return None - return handler.iucr_name + return handler.cif_name def _owner_descriptors(owner: object) -> Iterable[object]: diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index ee275c751..f2303494f 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -15,8 +15,6 @@ from easydiffraction.utils.utils import str_to_ufloat if TYPE_CHECKING: - from collections.abc import Sequence - import gemmi from easydiffraction.core.category import CategoryCollection @@ -32,10 +30,6 @@ # Maximum CIF description length before using semicolon-delimited block _CIF_DESCRIPTION_WRAP_LEN = 60 -_ADP_FAMILY_B = 'B' -_ADP_FAMILY_U = 'U' -_ADP_FAMILY_BETA = 'beta' - def format_value(value: object) -> str: """ @@ -165,12 +159,11 @@ 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``. - Free parameters are written with uncertainty brackets (see + Expects ``param`` to expose ``_tags.edi_name`` and ``value``. Free + parameters are written with uncertainty brackets (see :func:`format_param_value`). """ - tags: Sequence[str] = param._cif_handler.names # type: ignore[attr-defined] - main_key: str = tags[0] + main_key: str = param._tags.edi_name # type: ignore[attr-defined] return f'{main_key} {format_param_value(param)}' @@ -179,7 +172,7 @@ 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``. + ``_tags.edi_name`` and ``value``. """ parameters_hook = getattr(item, '_cif_parameters', None) parameters = parameters_hook() if parameters_hook is not None else item.parameters @@ -193,7 +186,7 @@ def _validate_loop_tags( ) -> None: """Log an error if any row tag disagrees with *header_tags*.""" for col, p in enumerate(parameters): - tag = p._cif_handler.names[0] # type: ignore[attr-defined] + tag = p._tags.edi_name # type: ignore[attr-defined] if tag != header_tags[col]: log.error( f'CIF tag mismatch in loop column {col}: ' @@ -228,144 +221,6 @@ def _emit_loop_rows( return lines -def _emit_rows_without_tag_validation( - items: list, - row_fn: object, - max_display: int | None, -) -> list[str]: - """Build rows for loops whose tag family is chosen externally.""" - if max_display is not None and len(items) > max_display: - half = max_display // 2 - return [ - *_rows_without_tag_validation(items[:half], row_fn), - '...', - *_rows_without_tag_validation(items[-half:], row_fn), - ] - return _rows_without_tag_validation(items, row_fn) - - -def _rows_without_tag_validation(items: list, row_fn: object) -> list[str]: - """Return formatted row strings without header-tag validation.""" - return [' '.join(row_fn(item)) for item in items] - - -def _adp_family_from_type(adp_type: str) -> str: - """Return the CIF ADP tag family for an atom-site ADP type.""" - from easydiffraction.datablocks.structure.categories.atom_sites.enums import ( # noqa: PLC0415 - AdpTypeEnum, - ) - - adp_type_enum = AdpTypeEnum(adp_type) - if adp_type_enum is AdpTypeEnum.BETA: - return _ADP_FAMILY_BETA - if adp_type_enum in {AdpTypeEnum.UISO, AdpTypeEnum.UANI}: - return _ADP_FAMILY_U - return _ADP_FAMILY_B - - -def _adp_family_for_atom_site(item: object) -> str: - """Return the ADP tag family for an atom-site (isotropic) row.""" - family = _adp_family_from_type(item.adp_type.value) - # beta has no _atom_site.beta_iso_or_equiv tag; emit a beta atom's - # equivalent isotropic value in the B_iso_or_equiv column instead. - return _ADP_FAMILY_B if family == _ADP_FAMILY_BETA else family - - -def _adp_family_for_atom_site_aniso(collection: object, item: object) -> str: - """Return the ADP tag family for an atom-site-aniso row.""" - structure = collection._parent - atom_site = structure.atom_sites[item.label.value] - return _adp_family_from_type(atom_site.adp_type.value) - - -def _group_items_by_adp_family( - items: list, - family_fn: object, -) -> list[tuple[str, list]]: - """Group items by B/U ADP tag family in deterministic order.""" - groups = { - _ADP_FAMILY_B: [], - _ADP_FAMILY_U: [], - _ADP_FAMILY_BETA: [], - } - for item in items: - groups[family_fn(item)].append(item) - return [(family, group) for family, group in groups.items() if group] - - -def _atom_site_tag_for_adp_family(parameter: object, family: str) -> str: - """Return the atom_site tag for the selected ADP family.""" - if parameter.name == 'adp_iso': - return f'_atom_site.{family}_iso_or_equiv' - return parameter._cif_handler.names[0] - - -def _atom_site_aniso_tag_for_adp_family(parameter: object, family: str) -> str: - """Return the atom_site_aniso tag for the selected ADP family.""" - if parameter.name.startswith('adp_'): - suffix = parameter.name.removeprefix('adp_') - return f'_atom_site_aniso.{family}_{suffix}' - return parameter._cif_handler.names[0] - - -def _adp_family_loop_to_cif( - items: list, - family: str, - tag_fn: object, - max_display: int | None, -) -> str: - """Render one B-family or U-family ADP loop.""" - first_item = items[0] - parameters = list(first_item.parameters) - lines: list[str] = ['loop_'] - lines.extend(tag_fn(parameter, family) for parameter in parameters) - - def _row(item: object) -> list[str]: - return [format_param_value(parameter) for parameter in item.parameters] - - lines.extend(_emit_rows_without_tag_validation(items, _row, max_display)) - return '\n'.join(lines) - - -def _adp_collection_to_cif( - collection: object, - max_display: int | None, -) -> str | None: - """ - Render ADP-sensitive structure loops with one tag family per row. - """ - items = list(collection.values()) - category_code = collection._item_type._category_code - if category_code == 'atom_site': - groups = _group_items_by_adp_family(items, _adp_family_for_atom_site) - loops = [ - _adp_family_loop_to_cif( - group, - family, - _atom_site_tag_for_adp_family, - max_display, - ) - for family, group in groups - ] - return '\n\n'.join(loops) - if category_code == 'atom_site_aniso': - groups = _group_items_by_adp_family( - items, - lambda item: _adp_family_for_atom_site_aniso(collection, item), - ) - loops = [ - _adp_family_loop_to_cif( - group, - family, - _atom_site_aniso_tag_for_adp_family, - max_display, - ) - for family, group in groups - ] - return '\n\n'.join(loops) - return None - - def category_collection_to_cif( collection: object, max_display: int | None = None, @@ -399,10 +254,6 @@ def category_collection_to_cif( if not len(collection): return '\n'.join(lines) - adp_cif = _adp_collection_to_cif(collection, max_display) - if adp_cif is not None: - return _join_scalar_and_loop_lines(lines, adp_cif) - loop_cif = _standard_collection_loop_to_cif(collection, max_display) return _join_scalar_and_loop_lines(lines, loop_cif) @@ -439,9 +290,9 @@ def _loop_parameters(item: object) -> list[GenericDescriptorBase]: lines = ['loop_'] header_tags: list[str] = [] for p in _loop_parameters(first_item): - tags = p._cif_handler.names # type: ignore[attr-defined] - header_tags.append(tags[0]) - lines.append(tags[0]) + tag = p._tags.edi_name # type: ignore[attr-defined] + header_tags.append(tag) + lines.append(tag) # Allow collections to customise per-item row formatting row_hook = getattr(collection, '_format_cif_row', None) @@ -545,38 +396,40 @@ def _format_project_description(description: str) -> str: return format_value(normalized_description) -def project_info_to_cif(info: object) -> str: - """Render ProjectInfo to CIF text (id, title, description).""" - name = f'{info.name}' +def project_metadata_to_cif(metadata: object) -> str: + """Render project metadata to Edi text.""" + name = f'{metadata.name}' - title = f'{info.title}' + title = f'{metadata.title}' if ' ' in title: - title = format_value(info.title) + title = format_value(metadata.title) - description = _format_project_description(info.description) + description = _format_project_description(metadata.description) - created = format_value(info.created.strftime('%d %b %Y %H:%M:%S')) - last_modified = format_value(info.last_modified.strftime('%d %b %Y %H:%M:%S')) + created = format_value(metadata.created.strftime('%d %b %Y %H:%M:%S')) + last_modified = format_value(metadata.last_modified.strftime('%d %b %Y %H:%M:%S')) + timestamp = format_value(metadata.timestamp) return ( - f'_project.id {name}\n' - f'_project.title {title}\n' - f'_project.description {description}\n' - f'_project.created {created}\n' - f'_project.last_modified {last_modified}' + f'_metadata.name {name}\n' + f'_metadata.title {title}\n' + f'_metadata.description {description}\n' + f'_metadata.created {created}\n' + f'_metadata.last_modified {last_modified}\n' + f'_metadata.timestamp {timestamp}' ) def _as_cif_text(section: object) -> str: - """Return CIF text from either an ``as_cif`` property or method.""" + """Return STAR text from either an ``as_cif`` property or method.""" cif_value = section.as_cif return cif_value() if callable(cif_value) else cif_value def project_config_to_cif(project: object) -> str: - """Render project-level configuration to ``project.cif`` text.""" + """Render project-level configuration to Edi body text.""" sections: list[str] = [] - for attr_name in ('info', 'rendering_plot', 'report'): + for attr_name in ('metadata', 'rendering_plot', 'report'): section = getattr(project, attr_name, None) if section is not None: sections.append(_as_cif_text(section)) @@ -600,9 +453,9 @@ def project_config_to_cif(project: object) -> str: def project_to_cif(project: object) -> str: - """Render a whole project by concatenating sections when present.""" + """Render a whole project Edi body from available sections.""" parts: list[str] = [] - if hasattr(project, 'info'): + if hasattr(project, 'metadata'): parts.append(project_config_to_cif(project)) if getattr(project, 'structures', None): parts.append(_as_cif_text(project.structures)) @@ -619,7 +472,9 @@ def experiment_to_cif(experiment: object) -> str: def analysis_to_cif(analysis: object) -> str: - """Render analysis metadata, aliases, and constraints to CIF.""" + """ + Render analysis metadata, aliases, and constraints as STAR text. + """ return category_owner_to_cif(analysis) @@ -649,56 +504,58 @@ def _project_block_from_cif_text(cif_text: str) -> gemmi.cif.Block: return gemmi.cif.read_string(_wrap_in_data_block(cif_text, 'project')).sole_block() -def _populate_project_info_from_block( - info: object, +def _populate_project_metadata_from_block( + metadata: object, block: gemmi.cif.Block, ) -> None: - """Populate ProjectInfo fields from a parsed CIF block.""" - from_cif = getattr(info, 'from_cif', None) + """Populate ProjectMetadata fields from a parsed block.""" + from_cif = getattr(metadata, 'from_cif', None) if callable(from_cif): from_cif(block) return read_cif_string = _make_cif_string_reader(block) - name = read_cif_string('_project.id') + name = read_cif_string('_metadata.name') or read_cif_string('_project.id') if name is not None: - info.name = name + metadata.name = name - title = read_cif_string('_project.title') + title = read_cif_string('_metadata.title') or read_cif_string('_project.title') if title is not None: - info.title = title + metadata.title = title - description = read_cif_string('_project.description') + description = read_cif_string('_metadata.description') or read_cif_string( + '_project.description' + ) if description is not None: - info.description = description + metadata.description = description -def project_info_from_cif(info: object, cif_text: str) -> None: +def project_metadata_from_cif(metadata: object, cif_text: str) -> None: """ - Populate a ProjectInfo instance from CIF text. + Populate a ProjectMetadata instance from Edi or CIF text. Reads the core project metadata fields from CIF text. Parameters ---------- - info : object - The ``ProjectInfo`` instance to populate. + metadata : object + The ``ProjectMetadata`` instance to populate. cif_text : str - CIF text content of ``project.cif``. + Edi or CIF text content of the project metadata section. """ block = _project_block_from_cif_text(cif_text) - _populate_project_info_from_block(info, block) + _populate_project_metadata_from_block(metadata, block) def project_config_from_cif(project: object, cif_text: str) -> None: """ - Populate project-level configuration from ``project.cif`` text. + Populate project-level configuration from Edi or CIF text. """ block = _project_block_from_cif_text(cif_text) - _populate_project_info_from_block(project.info, block) + _populate_project_metadata_from_block(project.metadata, block) rendering_plot = getattr(project, 'rendering_plot', None) if rendering_plot is not None: @@ -746,7 +603,7 @@ def analysis_from_cif(analysis: object, cif_text: str) -> None: analysis : object The ``Analysis`` instance to populate. cif_text : str - CIF text content of ``analysis.cif``. + Analysis Edi body text or explicit CIF import text. """ import gemmi # noqa: PLC0415 @@ -776,7 +633,10 @@ def analysis_from_cif(analysis: object, cif_text: str) -> None: def _has_fit_parameter_state_sections(block: object) -> bool: """Return True when persisted fit-parameter rows are present.""" - return _has_cif_loop(block, '_fit_parameter.param_unique_name') + return _has_cif_loop( + block, + '_fit_parameter.parameter_unique_name', + ) or _has_cif_loop(block, '_fit_parameter.param_unique_name') def _has_persisted_fit_state_sections(block: object) -> bool: @@ -1025,7 +885,7 @@ def param_from_cif( # Try to find the value(s) from the CIF block iterating over # the possible cif names in order of preference. - for tag in self._cif_handler.names: + for tag in self._tags.read_names: candidates = list(block.find_values(tag)) if candidates: found_values = candidates @@ -1158,8 +1018,11 @@ def _find_loop_for_category( The matching loop, or ``None`` if not found. """ for param in category_item.parameters: - for name in param._cif_handler.names: - loop = block.find_loop(name).get_loop() + for name in param._tags.read_names: + loop_ref = block.find_loop(name) + if loop_ref is None: + continue + loop = loop_ref.get_loop() if hasattr(loop_ref, 'get_loop') else loop_ref if loop is not None: return loop return None @@ -1220,7 +1083,7 @@ def category_collection_from_cif( current_item = self._items[row_idx] for param in current_item.parameters: tag_found = False - for cif_name in param._cif_handler.names: + for cif_name in param._tags.read_names: if cif_name in loop.tags: col_idx = loop.tags.index(cif_name) # TODO: The following is duplication of diff --git a/src/easydiffraction/io/edi/__init__.py b/src/easydiffraction/io/edi/__init__.py new file mode 100644 index 000000000..61a7d33e8 --- /dev/null +++ b/src/easydiffraction/io/edi/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Edi project persistence helpers.""" + +from __future__ import annotations + +from easydiffraction.io.edi.serialize import edi_body_from_text +from easydiffraction.io.edi.serialize import section_to_edi diff --git a/src/easydiffraction/io/edi/serialize.py b/src/easydiffraction/io/edi/serialize.py new file mode 100644 index 000000000..2b7be4ed5 --- /dev/null +++ b/src/easydiffraction/io/edi/serialize.py @@ -0,0 +1,176 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Serialize and validate Edi project sections.""" + +from __future__ import annotations + +from easydiffraction.io.cif.parse import read_cif_str + +_SCHEMA_VERSION_TAG = '_edi.schema_version' +_SCHEMA_VERSION = '1' + +_LINE_SEGMENT_BACKGROUND_TAGS = ( + '_background.position', + '_background.intensity', + '_pd_background.line_segment_X', + '_pd_background_line_segment_X', + '_pd_background.line_segment_intensity', + '_pd_background_line_segment_intensity', +) +_CHEBYSHEV_BACKGROUND_TAGS = ( + '_background.order', + '_background.coef', + '_pd_background.Chebyshev_order', + '_pd_background.Chebyshev_coef', +) +_BACKGROUND_TYPES = frozenset({'line-segment', 'chebyshev'}) + + +def section_to_edi(body: str) -> str: + """ + Add the Edi schema marker to a serialized section body. + + Parameters + ---------- + body : str + STAR/CIF section body, optionally with a ``data_`` block header. + + Returns + ------- + str + Edi text with schema marker lines. + """ + cleaned_body = body.strip() + marker = f'{_SCHEMA_VERSION_TAG} {_SCHEMA_VERSION}' + if cleaned_body.startswith('data_'): + header, _, rest = cleaned_body.partition('\n') + return f'{header}\n\n{marker}\n\n{rest.strip()}\n' + + return f'{marker}\n\n{cleaned_body}\n' + + +def edi_body_from_text(text: str) -> str: + """ + Validate Edi text and return its section body. + + An invalid schema marker or inconsistent selector/body content + raises ``ValueError`` via the validation helpers. + + Parameters + ---------- + text : str + Edi section text containing schema marker lines. + + Returns + ------- + str + Section body with schema marker lines removed. + """ + _validate_schema_marker(_marker_block_from_text(text)) + body = _strip_schema_marker_lines(text).strip() + if body: + _validate_selector_body_consistency(_block_from_body(body)) + return f'{body}\n' if body else '' + + +def _marker_block_from_text(text: str) -> object: + """ + Parse Edi schema marker lines as one anonymous STAR block. + """ + import gemmi # noqa: PLC0415 + + marker_text = '\n'.join(line for line in text.splitlines() if _is_schema_marker_line(line)) + return gemmi.cif.read_string(f'data_edi\n\n{marker_text}').sole_block() + + +def _block_from_body(body: str) -> object: + """Parse a stripped Edi section body.""" + import gemmi # noqa: PLC0415 + + if body.lstrip().startswith('data_'): + return gemmi.cif.read_string(body).sole_block() + return gemmi.cif.read_string(f'data_edi\n\n{body}').sole_block() + + +def _validate_schema_marker(block: object) -> None: + """Validate the required Edi schema marker.""" + schema_version = read_cif_str(block, _SCHEMA_VERSION_TAG) + if schema_version is None: + msg = ( + f'Edi schema version marker {_SCHEMA_VERSION_TAG} is required. ' + 'Saved project sections must be Edi files with a schema marker.' + ) + raise ValueError(msg) + + try: + major_version = int(schema_version.split('.', maxsplit=1)[0]) + except ValueError as exc: + msg = f'Edi schema version must start with an integer, got {schema_version!r}.' + raise ValueError(msg) from exc + + if major_version != 1: + msg = ( + f'Unsupported Edi schema version {schema_version!r}. ' + 'Open this project with a compatible EasyDiffraction version ' + 'and re-save it as Edi schema_version 1.' + ) + raise ValueError(msg) + + +def _validate_selector_body_consistency(block: object) -> None: + """Validate selector fields against implementation-specific body.""" + _validate_background_selector_body(block) + + +def _validate_background_selector_body(block: object) -> None: + """Validate background type against background row fields.""" + background_type = read_cif_str(block, '_background.type') + has_line_segment_fields = _has_any_tag(block, _LINE_SEGMENT_BACKGROUND_TAGS) + has_chebyshev_fields = _has_any_tag(block, _CHEBYSHEV_BACKGROUND_TAGS) + + if background_type is None: + if has_line_segment_fields or has_chebyshev_fields: + msg = 'Background fields require an explicit _background.type selector.' + raise ValueError(msg) + return + + if background_type not in _BACKGROUND_TYPES: + msg = f'Unknown _background.type selector: {background_type!r}.' + raise ValueError(msg) + + if background_type == 'line-segment' and has_chebyshev_fields: + msg = 'line-segment background cannot contain Chebyshev fields.' + raise ValueError(msg) + + if background_type == 'chebyshev' and has_line_segment_fields: + msg = 'chebyshev background cannot contain line-segment fields.' + raise ValueError(msg) + + +def _has_any_tag(block: object, tags: tuple[str, ...]) -> bool: + """Return whether any tag is present as a scalar or loop column.""" + return any(_has_tag(block, tag) for tag in tags) + + +def _has_tag(block: object, tag: str) -> bool: + """Return whether a scalar or loop tag is present in the block.""" + if block.find_value(tag) is not None: + return True + loop_ref = block.find_loop(tag) + if loop_ref is None: + return False + loop = loop_ref.get_loop() if hasattr(loop_ref, 'get_loop') else loop_ref + return loop is not None + + +def _strip_schema_marker_lines(text: str) -> str: + """Remove schema marker lines from Edi text.""" + lines = [line for line in text.splitlines() if not _is_schema_marker_line(line)] + return '\n'.join(lines) + + +def _is_schema_marker_line(line: str) -> bool: + """Return whether a line contains one schema marker item.""" + stripped = line.lstrip() + tag = _SCHEMA_VERSION_TAG + return stripped == tag or stripped.startswith((f'{tag} ', f'{tag}\t')) diff --git a/src/easydiffraction/project/categories/__init__.py b/src/easydiffraction/project/categories/__init__.py index 2aeb05cf2..27242019e 100644 --- a/src/easydiffraction/project/categories/__init__.py +++ b/src/easydiffraction/project/categories/__init__.py @@ -1,3 +1,3 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Project-level categories: info, report, rendering, verbosity.""" +"""Project-level categories: metadata, report, rendering, verbosity.""" diff --git a/src/easydiffraction/project/categories/info/__init__.py b/src/easydiffraction/project/categories/info/__init__.py deleted file mode 100644 index 5b464da22..000000000 --- a/src/easydiffraction/project/categories/info/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> -# SPDX-License-Identifier: BSD-3-Clause -"""Project info category exports.""" - -from __future__ import annotations - -from easydiffraction.project.categories.info.default import ProjectInfo -from easydiffraction.project.categories.info.factory import ProjectInfoFactory diff --git a/src/easydiffraction/project/categories/metadata/__init__.py b/src/easydiffraction/project/categories/metadata/__init__.py new file mode 100644 index 000000000..6b9012ee6 --- /dev/null +++ b/src/easydiffraction/project/categories/metadata/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Project metadata category exports.""" + +from __future__ import annotations + +from easydiffraction.project.categories.metadata.default import ProjectMetadata +from easydiffraction.project.categories.metadata.factory import ProjectMetadataFactory diff --git a/src/easydiffraction/project/categories/info/default.py b/src/easydiffraction/project/categories/metadata/default.py similarity index 71% rename from src/easydiffraction/project/categories/info/default.py rename to src/easydiffraction/project/categories/metadata/default.py index 20aab0a88..43db76464 100644 --- a/src/easydiffraction/project/categories/info/default.py +++ b/src/easydiffraction/project/categories/metadata/default.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Project info category.""" +"""Project metadata category.""" from __future__ import annotations @@ -11,9 +11,9 @@ from easydiffraction.core.metadata import TypeInfo from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler -from easydiffraction.io.cif.serialize import project_info_to_cif -from easydiffraction.project.categories.info.factory import ProjectInfoFactory +from easydiffraction.io.cif.handler import TagSpec +from easydiffraction.io.cif.serialize import project_metadata_to_cif +from easydiffraction.project.categories.metadata.factory import ProjectMetadataFactory from easydiffraction.utils.logging import console from easydiffraction.utils.logging import log from easydiffraction.utils.utils import render_cif @@ -21,11 +21,11 @@ _PROJECT_TIMESTAMP_FORMAT = '%d %b %Y %H:%M:%S' -@ProjectInfoFactory.register -class ProjectInfo(CategoryItem): +@ProjectMetadataFactory.register +class ProjectMetadata(CategoryItem): """Project metadata category.""" - _category_code = 'project' + _category_code = 'metadata' type_info = TypeInfo( tag='default', @@ -46,34 +46,45 @@ def __init__( last_modified = datetime.datetime.now(tz=datetime.UTC) self._project_id = StringDescriptor( - name='id', + name='name', description='Project identifier', value_spec=AttributeSpec(default=name), - cif_handler=CifHandler(names=['_project.id']), + tags=TagSpec(edi_names=['_metadata.name'], cif_names=['_project.id']), ) self._title_descriptor = StringDescriptor( name='title', description='Project title', value_spec=AttributeSpec(default=title), - cif_handler=CifHandler(names=['_project.title']), + tags=TagSpec(edi_names=['_metadata.title'], cif_names=['_project.title']), ) self._description_descriptor = StringDescriptor( name='description', description='Project description', value_spec=AttributeSpec(default=' '.join(description.split())), - cif_handler=CifHandler(names=['_project.description']), + tags=TagSpec(edi_names=['_metadata.description'], cif_names=['_project.description']), ) self._created_descriptor = StringDescriptor( name='created', description='Project creation timestamp', value_spec=AttributeSpec(default=created.strftime(_PROJECT_TIMESTAMP_FORMAT)), - cif_handler=CifHandler(names=['_project.created']), + tags=TagSpec(edi_names=['_metadata.created'], cif_names=['_project.created']), ) self._last_modified_descriptor = StringDescriptor( name='last_modified', description='Project last-modified timestamp', value_spec=AttributeSpec(default=last_modified.strftime(_PROJECT_TIMESTAMP_FORMAT)), - cif_handler=CifHandler(names=['_project.last_modified']), + tags=TagSpec( + edi_names=['_metadata.last_modified'], cif_names=['_project.last_modified'] + ), + ) + self._timestamp_descriptor = StringDescriptor( + name='timestamp', + description='Project fit timestamp', + value_spec=AttributeSpec(default=None, allow_none=True), + tags=TagSpec( + edi_names=['_metadata.timestamp'], + cif_names=['_easydiffraction_project.timestamp', '_software.timestamp'], + ), ) self._path: pathlib.Path | None = None @@ -88,7 +99,7 @@ def _validate_name(value: str) -> None: @staticmethod def _parse_timestamp(value: str) -> datetime.datetime: - """Parse project timestamp text from CIF storage format.""" + """Parse project timestamp text from STAR storage format.""" return datetime.datetime.strptime(value, _PROJECT_TIMESTAMP_FORMAT).replace( tzinfo=datetime.UTC, ) @@ -102,8 +113,8 @@ def _normalize_timestamp(value: datetime.datetime) -> datetime.datetime: @staticmethod def _format_timestamp(value: datetime.datetime) -> str: - """Format a project timestamp for CIF storage.""" - return ProjectInfo._normalize_timestamp(value).strftime(_PROJECT_TIMESTAMP_FORMAT) + """Format a project timestamp for STAR storage.""" + return ProjectMetadata._normalize_timestamp(value).strftime(_PROJECT_TIMESTAMP_FORMAT) @property def unique_name(self) -> str: @@ -158,8 +169,19 @@ def last_modified(self) -> datetime.datetime: """Return the last modified timestamp.""" return self._parse_timestamp(self._last_modified_descriptor.value) + @property + def timestamp(self) -> str | None: + """Return the latest fit timestamp.""" + return self._timestamp_descriptor.value + + @timestamp.setter + def timestamp(self, value: str | None) -> None: + self._timestamp_descriptor.value = value + def _set_last_modified(self, value: datetime.datetime | str) -> None: - """Set the last-modified timestamp from runtime or CIF input.""" + """ + Set the last-modified timestamp from runtime or STAR input. + """ if isinstance(value, datetime.datetime): self._last_modified_descriptor.value = self._format_timestamp(value) return @@ -171,11 +193,11 @@ def update_last_modified(self) -> None: @property def as_cif(self) -> str: - """Export project metadata to CIF.""" - return project_info_to_cif(self) + """Export project metadata to Edi.""" + return project_metadata_to_cif(self) - def show_as_cif(self) -> None: - """Pretty-print CIF via shared utilities.""" - paragraph_title = f"Project 📦 '{self.name}' info as CIF" + def show_as_text(self) -> None: + """Pretty-print the project metadata as text.""" + paragraph_title = f"Project 📦 '{self.name}' metadata as text" console.paragraph(paragraph_title) render_cif(self.as_cif) diff --git a/src/easydiffraction/project/categories/info/factory.py b/src/easydiffraction/project/categories/metadata/factory.py similarity index 69% rename from src/easydiffraction/project/categories/info/factory.py rename to src/easydiffraction/project/categories/metadata/factory.py index 1a6bccb4f..062d3c5c4 100644 --- a/src/easydiffraction/project/categories/info/factory.py +++ b/src/easydiffraction/project/categories/metadata/factory.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Factory for project info categories.""" +"""Factory for project metadata categories.""" from __future__ import annotations @@ -9,8 +9,8 @@ from easydiffraction.core.factory import FactoryBase -class ProjectInfoFactory(FactoryBase): - """Create project info category instances.""" +class ProjectMetadataFactory(FactoryBase): + """Create project metadata category instances.""" _default_rules: ClassVar[dict] = { frozenset(): 'default', diff --git a/src/easydiffraction/project/categories/rendering_plot/default.py b/src/easydiffraction/project/categories/rendering_plot/default.py index bf058a661..e292ae468 100644 --- a/src/easydiffraction/project/categories/rendering_plot/default.py +++ b/src/easydiffraction/project/categories/rendering_plot/default.py @@ -13,7 +13,7 @@ from easydiffraction.display.plotting import Plotter from easydiffraction.display.plotting import PlotterEngineEnum from easydiffraction.display.plotting import PlotterFactory -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.io.cif.parse import read_cif_str from easydiffraction.project.categories.rendering_plot.factory import RenderingPlotFactory from easydiffraction.utils.logging import log @@ -49,7 +49,7 @@ def __init__(self) -> None: allowed=CHART_ENGINE_OPTIONS, ), ), - cif_handler=CifHandler(names=['_rendering_plot.type']), + tags=TagSpec(edi_names=['_rendering_plot.type']), ) @staticmethod diff --git a/src/easydiffraction/project/categories/rendering_structure/default.py b/src/easydiffraction/project/categories/rendering_structure/default.py index 5a59ec7d4..d2b5894e0 100644 --- a/src/easydiffraction/project/categories/rendering_structure/default.py +++ b/src/easydiffraction/project/categories/rendering_structure/default.py @@ -13,7 +13,7 @@ from easydiffraction.display.structure.enums import ViewerEngineEnum from easydiffraction.display.structure.viewing import Viewer from easydiffraction.display.structure.viewing import ViewerFactory -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.io.cif.parse import read_cif_str from easydiffraction.project.categories.rendering_structure.factory import ( RenderingStructureFactory, @@ -49,7 +49,7 @@ def __init__(self) -> None: default=AUTO_ENGINE, validator=MembershipValidator(allowed=VIEW_ENGINE_OPTIONS), ), - cif_handler=CifHandler(names=['_rendering_structure.type']), + tags=TagSpec(edi_names=['_rendering_structure.type']), ) @staticmethod diff --git a/src/easydiffraction/project/categories/rendering_table/default.py b/src/easydiffraction/project/categories/rendering_table/default.py index 98ef5ca47..01ebc202f 100644 --- a/src/easydiffraction/project/categories/rendering_table/default.py +++ b/src/easydiffraction/project/categories/rendering_table/default.py @@ -13,7 +13,7 @@ from easydiffraction.display.tables import TableEngineEnum from easydiffraction.display.tables import TableRenderer from easydiffraction.display.tables import TableRendererFactory -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.io.cif.parse import read_cif_str from easydiffraction.project.categories.rendering_table.factory import RenderingTableFactory from easydiffraction.utils.logging import log @@ -49,7 +49,7 @@ def __init__(self) -> None: allowed=TABLE_ENGINE_OPTIONS, ), ), - cif_handler=CifHandler(names=['_rendering_table.type']), + tags=TagSpec(edi_names=['_rendering_table.type']), ) @staticmethod diff --git a/src/easydiffraction/project/categories/report/default.py b/src/easydiffraction/project/categories/report/default.py index 156cd2f98..4ae787871 100644 --- a/src/easydiffraction/project/categories/report/default.py +++ b/src/easydiffraction/project/categories/report/default.py @@ -10,7 +10,7 @@ from easydiffraction.core.metadata import TypeInfo from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import BoolDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.io.cif.iucr_writer import write_iucr_cif from easydiffraction.project.categories.report.factory import ReportFactory from easydiffraction.report.data_context import build_report_data_context @@ -52,31 +52,31 @@ def __init__(self) -> None: name='cif', description='Whether to write CIF reports when saving.', value_spec=AttributeSpec(default=False), - cif_handler=CifHandler(names=['_report.cif']), + tags=TagSpec(edi_names=['_report.cif']), ) self._html = BoolDescriptor( name='html', description='Whether to write HTML reports when saving.', value_spec=AttributeSpec(default=True), - cif_handler=CifHandler(names=['_report.html']), + tags=TagSpec(edi_names=['_report.html']), ) self._tex = BoolDescriptor( name='tex', description='Whether to write TeX reports when saving.', value_spec=AttributeSpec(default=False), - cif_handler=CifHandler(names=['_report.tex']), + tags=TagSpec(edi_names=['_report.tex']), ) self._pdf = BoolDescriptor( name='pdf', description='Whether to write PDF reports when saving.', value_spec=AttributeSpec(default=False), - cif_handler=CifHandler(names=['_report.pdf']), + tags=TagSpec(edi_names=['_report.pdf']), ) self._html_offline = BoolDescriptor( name='html_offline', description='Whether HTML reports should embed assets.', value_spec=AttributeSpec(default=False), - cif_handler=CifHandler(names=['_report.html_offline']), + tags=TagSpec(edi_names=['_report.html_offline']), ) @property diff --git a/src/easydiffraction/project/categories/structure_style/default.py b/src/easydiffraction/project/categories/structure_style/default.py index 10ec8ac5f..f9729f149 100644 --- a/src/easydiffraction/project/categories/structure_style/default.py +++ b/src/easydiffraction/project/categories/structure_style/default.py @@ -14,7 +14,7 @@ from easydiffraction.core.variable import NumericDescriptor from easydiffraction.display.structure.enums import AtomViewEnum from easydiffraction.display.structure.enums import ColorSchemeEnum -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.project.categories.structure_style.factory import StructureStyleFactory @@ -36,13 +36,13 @@ def __init__(self) -> None: name='atom_view', enum=AtomViewEnum, description='How atoms are sized and shaped in the structure view.', - cif_handler=CifHandler(names=['_structure_style.atom_view']), + tags=TagSpec(edi_names=['_structure_style.atom_view']), ) self._color_scheme = EnumDescriptor( name='color_scheme', enum=ColorSchemeEnum, description='Standard element colour scheme.', - cif_handler=CifHandler(names=['_structure_style.color_scheme']), + tags=TagSpec(edi_names=['_structure_style.color_scheme']), ) self._adp_probability = NumericDescriptor( name='adp_probability', @@ -51,7 +51,7 @@ def __init__(self) -> None: default=0.99, validator=RangeValidator(gt=0.0, lt=1.0), ), - cif_handler=CifHandler(names=['_structure_style.adp_probability']), + tags=TagSpec(edi_names=['_structure_style.adp_probability']), ) self._atom_scale = NumericDescriptor( name='atom_scale', @@ -60,7 +60,7 @@ def __init__(self) -> None: default=0.3, validator=RangeValidator(gt=0.0, le=1.0), ), - cif_handler=CifHandler(names=['_structure_style.atom_scale']), + tags=TagSpec(edi_names=['_structure_style.atom_scale']), ) @property diff --git a/src/easydiffraction/project/categories/structure_view/default.py b/src/easydiffraction/project/categories/structure_view/default.py index bf8ef46a4..386712a78 100644 --- a/src/easydiffraction/project/categories/structure_view/default.py +++ b/src/easydiffraction/project/categories/structure_view/default.py @@ -11,7 +11,7 @@ from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import BoolDescriptor from easydiffraction.core.variable import NumericDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.project.categories.structure_view.factory import StructureViewFactory from easydiffraction.utils.logging import log @@ -21,7 +21,7 @@ def _range_descriptor(name: str, default: float) -> NumericDescriptor: name=name, description='Per-axis fractional view-range bound.', value_spec=AttributeSpec(default=default), - cif_handler=CifHandler(names=[f'_structure_view.{name}']), + tags=TagSpec(edi_names=[f'_structure_view.{name}']), ) @@ -43,13 +43,13 @@ def __init__(self) -> None: name='show_labels', description='Show atom labels when the view opens.', value_spec=AttributeSpec(default=False), - cif_handler=CifHandler(names=['_structure_view.show_labels']), + tags=TagSpec(edi_names=['_structure_view.show_labels']), ) self._show_moments = BoolDescriptor( name='show_moments', description='Show magnetic-moment arrows where the data exists.', value_spec=AttributeSpec(default=True), - cif_handler=CifHandler(names=['_structure_view.show_moments']), + tags=TagSpec(edi_names=['_structure_view.show_moments']), ) self._range_a_min = _range_descriptor('range_a_min', 0.0) self._range_a_max = _range_descriptor('range_a_max', 1.0) diff --git a/src/easydiffraction/project/categories/verbosity/default.py b/src/easydiffraction/project/categories/verbosity/default.py index ad7d6b78b..af7f94f5a 100644 --- a/src/easydiffraction/project/categories/verbosity/default.py +++ b/src/easydiffraction/project/categories/verbosity/default.py @@ -7,7 +7,7 @@ from easydiffraction.core.category import CategoryItem from easydiffraction.core.metadata import TypeInfo from easydiffraction.core.variable import EnumDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.project.categories.verbosity.factory import VerbosityFactory from easydiffraction.utils.enums import VerbosityEnum @@ -30,7 +30,7 @@ def __init__(self) -> None: name='fit', enum=VerbosityEnum, description='Fitting process output verbosity', - cif_handler=CifHandler(names=['_verbosity.fit']), + tags=TagSpec(edi_names=['_verbosity.fit']), ) @property diff --git a/src/easydiffraction/project/display.py b/src/easydiffraction/project/display.py index 6515a3545..5ebefa541 100644 --- a/src/easydiffraction/project/display.py +++ b/src/easydiffraction/project/display.py @@ -89,9 +89,17 @@ def access(self) -> None: """Show Python access paths for all parameters.""" self._project.analysis.display.how_to_access_parameters() - def cif_uids(self) -> None: - """Show CIF unique identifiers for all parameters.""" - self._project.analysis.display.parameter_cif_uids() + def uid(self) -> None: + """Show the constraint unique identifier for all parameters.""" + self._project.analysis.display.parameter_uids() + + def edi(self) -> None: + """Show the Edi persistence tag for all parameters.""" + self._project.analysis.display.parameter_edi_tags() + + def cif(self) -> None: + """Show the report CIF tag for all parameters.""" + self._project.analysis.display.parameter_cif_tags() def help(self) -> None: """Print available parameter-display methods.""" @@ -145,7 +153,7 @@ def correlations( threshold: float | None = None, precision: int = 2, *, - max_parameters: int = 6, + max_parameters: int = 5, show_diagonal: bool = True, ) -> None: """Show parameter correlations from the latest fit.""" @@ -217,7 +225,7 @@ def _predictive_needs_processing_indicator( analysis = self._project.analysis experiment = self._project.experiments[expt_name] plotter = self._project.rendering_plot.plotter - _, x_axis_name, _, _, _ = plotter._resolve_x_axis(experiment.type, x) + _, x_axis_name, _, _, _ = plotter._resolve_x_axis(experiment.experiment_type, x) x_axis_name = str(x_axis_name) require_draws = plotter.engine == PlotterEngineEnum.PLOTLY.value and style in { 'draws', @@ -851,8 +859,8 @@ def _pattern_option_statuses(self, expt_name: str) -> list[PatternOptionStatus]: self._project.rendering_plot.plotter._update_project_categories(expt_name) experiment = self._project.experiments[expt_name] pattern = intensity_category_for(experiment) - sample_form = experiment.type.sample_form.value - scattering_type = experiment.type.scattering_type.value + sample_form = experiment.experiment_type.sample_form.value + scattering_type = experiment.experiment_type.scattering_type.value has_linked_structure = self._has_linked_structure_for_calculation(experiment) measured_available = experiment._has_measured_data() @@ -1027,17 +1035,21 @@ def _has_linked_structure_for_calculation(self, experiment: object) -> bool: """Return whether the experiment links to a known structure.""" structure_names = set(getattr(self._project.structures, 'names', ())) - linked_phases = getattr(experiment, 'linked_phases', None) - if self._has_nonempty_value(linked_phases): - for linked_phase in linked_phases: - identity = getattr(linked_phase, '_identity', None) + linked_structures = getattr(experiment, 'linked_structures', None) + if self._has_nonempty_value(linked_structures): + for linked_structure in linked_structures: + identity = getattr(linked_structure, '_identity', None) category_entry_name = getattr(identity, 'category_entry_name', None) if category_entry_name in structure_names: return True - linked_crystal = getattr(experiment, 'linked_crystal', None) - linked_crystal_id = getattr(getattr(linked_crystal, 'id', None), 'value', None) - return linked_crystal_id in structure_names + linked_structure = getattr(experiment, 'linked_structure', None) + linked_structure_id = getattr( + getattr(linked_structure, 'structure_id', None), + 'value', + None, + ) + return linked_structure_id in structure_names def _uncertainty_status( self, diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 6cffbd76a..e27036599 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -9,6 +9,7 @@ import tempfile from typing import TYPE_CHECKING from typing import ClassVar +from typing import NoReturn from typeguard import typechecked from varname import varname @@ -21,6 +22,8 @@ from easydiffraction.io.cif.serialize import project_config_from_cif from easydiffraction.io.cif.serialize import project_config_to_cif from easydiffraction.io.cif.serialize import project_to_cif +from easydiffraction.io.edi import edi_body_from_text +from easydiffraction.io.edi import section_to_edi from easydiffraction.io.results_sidecar import read_analysis_results_sidecar from easydiffraction.io.results_sidecar import write_analysis_results_sidecar from easydiffraction.project.display import ProjectDisplay @@ -40,10 +43,24 @@ from easydiffraction.project.categories.structure_style import StructureStyle from easydiffraction.project.categories.structure_view import StructureView from easydiffraction.project.categories.verbosity import Verbosity - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata from easydiffraction.report import Report +def _raise_legacy_project_cif_error( + path: pathlib.Path, + *, + replacement: str, +) -> NoReturn: + """Raise an explicit migration error for beta project CIF input.""" + msg = ( + f"Legacy beta project CIF file '{path}' is no longer supported as " + 'project persistence. Open it in an EasyDiffraction version that ' + f'can read beta project CIF, then save it again to create {replacement}.' + ) + raise ValueError(msg) + + def _apply_csv_row_to_params( row: object, columns: object, @@ -122,16 +139,28 @@ def _resolve_data_path_from_results_csv( return project_path / path -def _load_cif_directory( - cif_dir: pathlib.Path, - add_from_cif_path: Callable[[str], None], +def _load_edi_directory( + section_dir: pathlib.Path, + add_from_edi_path: Callable[[str], None], + *, + replacement: str, ) -> None: - """Load all CIF files from one directory using the given loader.""" - if not cif_dir.is_dir(): + """Load Edi files and reject legacy-only project CIF files.""" + if not section_dir.is_dir(): + return + + edi_files = sorted(section_dir.glob('*.edi')) + if edi_files: + for edi_file in edi_files: + add_from_edi_path(str(edi_file)) return - for cif_file in sorted(cif_dir.glob('*.cif')): - add_from_cif_path(str(cif_file)) + legacy_files = sorted(section_dir.glob('*.cif')) + if legacy_files: + _raise_legacy_project_cif_error( + legacy_files[0], + replacement=replacement, + ) def _create_loading_project(project_cls: type[Project]) -> Project: @@ -143,37 +172,67 @@ def _create_loading_project(project_cls: type[Project]) -> Project: project_cls._loading = False -def _load_project_info(project: Project, project_path: pathlib.Path) -> None: +def _load_project_metadata(project: Project, project_path: pathlib.Path) -> None: """ - Restore project configuration from ``project.cif`` when present. + Restore project configuration from Edi. """ + project_edi_path = project_path / 'project.edi' + if project_edi_path.is_file(): + body = edi_body_from_text(project_edi_path.read_text()) + project_config_from_cif(project, body) + return + project_cif_path = project_path / 'project.cif' if project_cif_path.is_file(): - project_config_from_cif(project, project_cif_path.read_text()) + _raise_legacy_project_cif_error( + project_cif_path, + replacement='project.edi', + ) + + msg = f"Project directory '{project_path}' must contain project.edi." + raise FileNotFoundError(msg) + + +def _resolved_analysis_path(project_path: pathlib.Path) -> pathlib.Path | None: + """Return the preferred analysis path for a saved project.""" + for analysis_path in ( + project_path / 'analysis' / 'analysis.edi', + project_path / 'analysis.edi', + ): + if analysis_path.is_file(): + return analysis_path + + for analysis_path in ( + project_path / 'analysis' / 'analysis.cif', + project_path / 'analysis.cif', + ): + if analysis_path.is_file(): + _raise_legacy_project_cif_error( + analysis_path, + replacement='analysis/analysis.edi', + ) + return None -def _resolved_analysis_cif_path(project_path: pathlib.Path) -> pathlib.Path | None: - """Return the preferred analysis CIF path for a saved project.""" - analysis_cif_path = project_path / 'analysis' / 'analysis.cif' - if analysis_cif_path.is_file(): - return analysis_cif_path +def _persistence_body_from_path(path: pathlib.Path) -> str: + """Read Edi text for a project section.""" + if path.suffix == '.edi': + text = path.read_text(encoding='utf-8') + return edi_body_from_text(text) - analysis_cif_path = project_path / 'analysis.cif' - if analysis_cif_path.is_file(): - return analysis_cif_path - return None + _raise_legacy_project_cif_error(path, replacement='a .edi file') def _load_project_analysis(project: Project, project_path: pathlib.Path) -> None: """Restore analysis categories and sidecar state from disk.""" - analysis_cif_path = _resolved_analysis_cif_path(project_path) - if analysis_cif_path is None: + analysis_path = _resolved_analysis_path(project_path) + if analysis_path is None: return - analysis_from_cif(project._analysis, analysis_cif_path.read_text()) + analysis_from_cif(project._analysis, _persistence_body_from_path(analysis_path)) read_analysis_results_sidecar( analysis=project._analysis, - analysis_dir=analysis_cif_path.parent, + analysis_dir=analysis_path.parent, ) param_map = project._build_parameter_map() if project._analysis.fit_parameters: @@ -205,7 +264,7 @@ def __init__( super().__init__() self._config = ProjectConfig(name, title, description) - object.__setattr__(self, '_info', self._config.info) + object.__setattr__(self, '_metadata', self._config.metadata) self._structures = Structures() self._experiments = Experiments() object.__setattr__(self, '_rendering_plot', self._config.rendering_plot) @@ -258,7 +317,7 @@ def current_project_path(cls) -> pathlib.Path | None: current_project = cls._current_project if current_project is None: return None - return current_project.info.path + return current_project.metadata.path # ------------------------------------------------------------------ # Dunder methods @@ -280,14 +339,14 @@ def __str__(self) -> str: # ------------------------------------------------------------------ @property - def info(self) -> ProjectInfo: + def metadata(self) -> ProjectMetadata: """Project metadata container.""" - return self._info + return self._metadata @property def name(self) -> str: """Convenience property for the project name.""" - return self._info.name + return self._metadata.name @property def full_name(self) -> str: @@ -373,8 +432,7 @@ def free_parameters(self) -> list: @property def as_cif(self) -> str: - """Export whole project as CIF text.""" - # Concatenate sections using centralized CIF serializers + """Serialize the whole project as EasyDiffraction STAR text.""" return project_to_cif(self) @property @@ -404,10 +462,10 @@ def load(cls, dir_path: str) -> Project: """ Load a project from a saved directory. - Reads ``project.cif``, ``structures/*.cif``, - ``experiments/*.cif``, and ``analysis.cif`` from *dir_path* and - reconstructs the full project state, including project-level - display configuration. + Reads Edi project files from *dir_path* and reconstructs the + full project state, including project-level display + configuration. Legacy beta CIF project files are rejected with + an explicit migration error. Parameters ---------- @@ -433,10 +491,18 @@ def load(cls, dir_path: str) -> Project: project = _create_loading_project(cls) project._saved = True - _load_project_info(project, project_path) - project.info.path = project_path - _load_cif_directory(project_path / 'structures', project._structures.add_from_cif_path) - _load_cif_directory(project_path / 'experiments', project._experiments.add_from_cif_path) + _load_project_metadata(project, project_path) + project.metadata.path = project_path + _load_edi_directory( + project_path / 'structures', + project._structures.add_from_edi_path, + replacement='structures/<structure>.edi', + ) + _load_edi_directory( + project_path / 'experiments', + project._experiments.add_from_edi_path, + replacement='experiments/<experiment>.edi', + ) _load_project_analysis(project, project_path) # 5. Resolve alias param references @@ -451,12 +517,12 @@ def load(cls, dir_path: str) -> Project: def _resolve_alias_references(self) -> None: """ - Resolve alias ``param_unique_name`` strings to live objects. + Resolve alias ``parameter_unique_name`` strings to live objects. After loading structures and experiments from CIF, aliases only - contain the ``param_unique_name`` string. This method builds a - ``{unique_name: param}`` map from all project parameters and - wires each alias's ``_param_ref``. + contain the ``parameter_unique_name`` string. This method + builds a ``{unique_name: param}`` map from all project + parameters and wires each alias's ``_param_ref``. """ aliases = self._analysis.aliases if not aliases._items: @@ -465,12 +531,12 @@ def _resolve_alias_references(self) -> None: param_map = self._build_parameter_map() for alias in aliases: - uname = alias.param_unique_name.value + uname = alias.parameter_unique_name.value if uname in param_map: alias._set_param(param_map[uname]) else: log.warning( - f"Alias '{alias.label.value}' references unknown " + f"Alias '{alias.id.value}' references unknown " f"parameter '{uname}'. Reference not resolved." ) @@ -494,11 +560,13 @@ def save(self) -> None: """ Save the project into the existing project directory. """ - if self.info.path is None: + if self.metadata.path is None: log.error('Project path not specified. Use save_as() to define the path first.') return - console.paragraph(f"Saving project 📦 '{self.name}' to '{display_path(self.info.path)}'") + console.paragraph( + f"Saving project 📦 '{self.name}' to '{display_path(self.metadata.path)}'" + ) # Apply constraints so dependent parameters are flagged # before serialization (user-constrained params are written @@ -506,40 +574,40 @@ def save(self) -> None: self._analysis._update_categories() # Ensure project directory exists - self.info.path.mkdir(parents=True, exist_ok=True) + self.metadata.path.mkdir(parents=True, exist_ok=True) # Save project-level configuration - with (self.info.path / 'project.cif').open('w') as f: - f.write(project_config_to_cif(self)) - console.print('├── 📄 project.cif') + with (self.metadata.path / 'project.edi').open('w') as f: + f.write(section_to_edi(project_config_to_cif(self))) + console.print('├── 📄 project.edi') # Save structures - sm_dir = self.info.path / 'structures' + sm_dir = self.metadata.path / 'structures' sm_dir.mkdir(parents=True, exist_ok=True) console.print('├── 📁 structures/') for structure in self.structures.values(): - file_name: str = f'{structure.name}.cif' + file_name: str = f'{structure.name}.edi' file_path = sm_dir / file_name with file_path.open('w') as f: - f.write(structure.as_cif) + f.write(section_to_edi(structure.as_cif)) console.print(f'│ └── 📄 {file_name}') # Save experiments - expt_dir = self.info.path / 'experiments' + expt_dir = self.metadata.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_name: str = f'{experiment.name}.edi' file_path = expt_dir / file_name with file_path.open('w') as f: - f.write(experiment.as_cif) + f.write(section_to_edi(experiment.as_cif)) console.print(f'│ └── 📄 {file_name}') # Save analysis - analysis_dir = self.info.path / 'analysis' + analysis_dir = self.metadata.path / 'analysis' analysis_dir.mkdir(parents=True, exist_ok=True) - with (analysis_dir / 'analysis.cif').open('w') as f: - f.write(self.analysis.as_cif) + with (analysis_dir / 'analysis.edi').open('w') as f: + f.write(section_to_edi(self.analysis.as_cif)) console.print('├── 📁 analysis/') write_analysis_results_sidecar( analysis=self.analysis, @@ -547,7 +615,9 @@ def save(self) -> None: ) analysis_file_names = sorted( - path.name for path in analysis_dir.iterdir() if path.is_file() + path.name + for path in analysis_dir.iterdir() + if path.is_file() and path.suffix in {'.edi', '.csv', '.h5'} ) for index, file_name in enumerate(analysis_file_names): branch = '└──' if index == len(analysis_file_names) - 1 else '├──' @@ -555,14 +625,14 @@ def save(self) -> None: report_paths = self.report._save_configured() if report_paths: - reports_dir = self.info.path / 'reports' + reports_dir = self.metadata.path / 'reports' console.print('└── 📁 reports/') for index, report_path in enumerate(report_paths): branch = '└──' if index == len(report_paths) - 1 else '├──' relative_path = report_path.relative_to(reports_dir) console.print(f' {branch} 📄 {relative_path}') - self.info.update_last_modified() + self.metadata.update_last_modified() self._saved = True def save_as( @@ -603,7 +673,7 @@ def save_as( else: shutil.rmtree(project_dir) - self.info.path = project_dir + self.metadata.path = project_dir self.save() def apply_params_from_csv(self, row_index: int) -> None: @@ -637,11 +707,11 @@ def apply_params_from_csv(self, row_index: int) -> None: from easydiffraction.analysis.sequential import _META_COLUMNS # noqa: PLC0415 from easydiffraction.core.variable import Parameter # noqa: PLC0415 - if self.info.path is None: + if self.metadata.path is None: msg = 'Project has no saved path. Save the project first.' raise FileNotFoundError(msg) - csv_path = pathlib.Path(self.info.path) / 'analysis' / 'results.csv' + csv_path = pathlib.Path(self.metadata.path) / 'analysis' / 'results.csv' if not csv_path.is_file(): msg = f"Results CSV not found: '{csv_path}'" raise FileNotFoundError(msg) @@ -663,7 +733,7 @@ def apply_params_from_csv(self, row_index: int) -> None: # 1. Reload data if file_path points to a real file file_path = row.get('file_path', '') - data_path = _resolve_data_path_from_results_csv(self.info.path, file_path) + data_path = _resolve_data_path_from_results_csv(self.metadata.path, file_path) if data_path is not None and data_path.is_file(): experiment._load_ascii_data_to_experiment(str(data_path)) diff --git a/src/easydiffraction/project/project_config.py b/src/easydiffraction/project/project_config.py index a8f13af08..cc37a88a0 100644 --- a/src/easydiffraction/project/project_config.py +++ b/src/easydiffraction/project/project_config.py @@ -5,8 +5,8 @@ from __future__ import annotations from easydiffraction.core.category_owner import CategoryOwner -from easydiffraction.project.categories.info import ProjectInfo -from easydiffraction.project.categories.info import ProjectInfoFactory +from easydiffraction.project.categories.metadata import ProjectMetadata +from easydiffraction.project.categories.metadata import ProjectMetadataFactory from easydiffraction.project.categories.rendering_plot import RenderingPlot from easydiffraction.project.categories.rendering_plot import RenderingPlotFactory from easydiffraction.project.categories.rendering_structure import RenderingStructure @@ -33,8 +33,8 @@ def __init__( description: str = '', ) -> None: super().__init__() - self._info = ProjectInfoFactory.create( - ProjectInfoFactory.default_tag(), + self._metadata = ProjectMetadataFactory.create( + ProjectMetadataFactory.default_tag(), name=name, title=title, description=description, @@ -50,9 +50,9 @@ def __init__( self._structure_style = StructureStyleFactory.create(StructureStyleFactory.default_tag()) @property - def info(self) -> ProjectInfo: + def metadata(self) -> ProjectMetadata: """Project metadata category.""" - return self._info + return self._metadata @property def rendering_plot(self) -> RenderingPlot: @@ -91,7 +91,7 @@ def structure_style(self) -> StructureStyle: @property def as_cif(self) -> str: - """Serialize singleton project categories to CIF.""" + """Serialize singleton project categories to STAR text.""" from easydiffraction.io.cif.serialize import category_owner_to_cif # noqa: PLC0415 return category_owner_to_cif(self) diff --git a/src/easydiffraction/project/project_info.py b/src/easydiffraction/project/project_metadata.py similarity index 60% rename from src/easydiffraction/project/project_info.py rename to src/easydiffraction/project/project_metadata.py index 215e8ed54..0a623aa56 100644 --- a/src/easydiffraction/project/project_info.py +++ b/src/easydiffraction/project/project_metadata.py @@ -4,6 +4,6 @@ from __future__ import annotations -from easydiffraction.project.categories.info.default import ProjectInfo as _ProjectInfo +from easydiffraction.project.categories.metadata.default import ProjectMetadata as _ProjectMetadata -ProjectInfo = _ProjectInfo +ProjectMetadata = _ProjectMetadata diff --git a/src/easydiffraction/report/data_context.py b/src/easydiffraction/report/data_context.py index 09aa940f4..ab8d9def8 100644 --- a/src/easydiffraction/report/data_context.py +++ b/src/easydiffraction/report/data_context.py @@ -59,7 +59,7 @@ ) _REPORT_LOOP_DISPLAY_LIMIT = DEFAULT_LOOP_DISPLAY_LIMIT _FULL_WIDTH_TABLE_CHAR_LIMIT = 40 -_TRUNCATED_DATA_CATEGORY_CODES = frozenset({'pd_data', 'total_data'}) +_TRUNCATED_DATA_CATEGORY_CODES = frozenset({'data'}) _NUMERIC_TEXT_RE = re.compile(r'^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:\(\d+\))?(?:[eE][+-]?\d+)?$') _ADP_ANISO_CIF_RE = re.compile(r'^_atom_site_aniso\.([BU])_(\d{2})$') _NUMBER_PARTS_RE = re.compile( @@ -184,7 +184,7 @@ def _structure_context(self, structure: object) -> dict[str, object]: def _atom_site_context(atom_site: object) -> dict[str, object]: """Return one atom-site row.""" return { - 'label': _attr_value(atom_site, 'label'), + 'label': _attr_value(atom_site, 'id'), 'type_symbol': _attr_value(atom_site, 'type_symbol'), 'fract_x': _attr_display_value(atom_site, 'fract_x'), 'fract_y': _attr_display_value(atom_site, 'fract_y'), @@ -198,7 +198,7 @@ def _atom_site_context(atom_site: object) -> dict[str, object]: def _atom_site_aniso_context(aniso_site: object) -> dict[str, object]: """Return one atom-site-aniso row.""" return { - 'label': _attr_value(aniso_site, 'label'), + 'label': _attr_value(aniso_site, 'id'), 'adp_11': _attr_display_value(aniso_site, 'adp_11'), 'adp_22': _attr_display_value(aniso_site, 'adp_22'), 'adp_33': _attr_display_value(aniso_site, 'adp_33'), @@ -215,7 +215,7 @@ def _experiment_context(experiment: object) -> dict[str, object]: return { 'id': _safe_attr(experiment, 'name'), 'type': _field_values( - _safe_attr(experiment, 'type'), + _safe_attr(experiment, 'experiment_type'), _EXPERIMENT_TYPE_FIELDS, ), 'calculator': { @@ -262,10 +262,8 @@ def _software_context(self) -> dict[str, object]: analysis = _safe_attr(self._project, 'analysis') software = _safe_attr(analysis, 'software') return { - 'framework': _software_role_context(_safe_attr(software, 'framework')), - 'calculator': _software_role_context(_safe_attr(software, 'calculator')), - 'minimizer': _software_role_context(_safe_attr(software, 'minimizer')), - 'fit_datetime': _attr_value(software, 'timestamp'), + 'roles': _software_roles_context(software), + 'fit_datetime': _safe_attr(_safe_attr(self._project, 'metadata'), 'timestamp'), } @@ -900,9 +898,28 @@ def _adp_label_context(parameter: object) -> dict[str, str] | None: } +def _active_adp_cif_name(parameter: object) -> str | None: + """ + Return the ADP CIF tag for the descriptor's active B/U family. + + Isotropic ADPs persist under the type-neutral ``_atom_site.adp_iso`` + tag, so the active B/U convention is taken from the owning atom's + ``adp_type`` rather than the (type-neutral) CIF name. + """ + cif_name = _first_cif_name(parameter) + if getattr(parameter, 'name', None) != 'adp_iso': + return cif_name + parent = getattr(parameter, '_parent', None) + adp_type = getattr(getattr(parent, 'adp_type', None), 'value', None) + if adp_type is None: + return cif_name + family = 'U' if str(adp_type).lower().startswith('u') else 'B' + return f'_atom_site.{family}_iso_or_equiv' + + def _adp_display_label(parameter: object, *, context: str) -> str | None: """Return a B/U-aware ADP display label when applicable.""" - cif_name = _first_cif_name(parameter) + cif_name = _active_adp_cif_name(parameter) if cif_name == '_atom_site.B_iso_or_equiv': return _adp_iso_label('B', context=context) if cif_name == '_atom_site.U_iso_or_equiv': @@ -926,12 +943,12 @@ def _adp_iso_label(family: str, *, context: str) -> str: def _first_cif_name(parameter: object) -> str | None: - """Return the first CIF tag for a descriptor.""" - cif_handler = getattr(parameter, '_cif_handler', None) - names = getattr(cif_handler, 'names', ()) - if not names: + """Return the active (canonical) CIF export tag for a descriptor.""" + tags = getattr(parameter, '_tags', None) + cif_names = getattr(tags, 'cif_names', ()) + if not cif_names: return None - return str(names[0]) + return str(cif_names[0]) def _display_units(units: object) -> str: @@ -1068,15 +1085,31 @@ def _category_code(category: object) -> str | None: return getattr(item_type, '_category_code', None) -def _software_role_context(role: object) -> dict[str, object]: +def _software_role_context( + role: object, + role_id: str | None = None, +) -> dict[str, object]: """Return one software role context.""" return { + 'id': _attr_value(role, 'id') or role_id, 'name': _attr_value(role, 'name'), 'version': _attr_value(role, 'version'), 'url': _attr_value(role, 'url'), } +def _software_roles_context(software: object) -> list[dict[str, object]]: + """Return report contexts for the canonical software roles.""" + roles: list[dict[str, object]] = [] + for role_id in ('framework', 'calculator', 'minimizer'): + try: + role = software[role_id] if software is not None else None + except (KeyError, TypeError): + role = None + roles.append(_software_role_context(role, role_id)) + return roles + + def _fit_data_context(experiment: object) -> dict[str, object] | None: """Return descriptor-driven fit data for one experiment.""" x_descriptor = _safe_attr(experiment, 'x_descriptor') @@ -1117,7 +1150,7 @@ def _fit_data_context(experiment: object) -> dict[str, object] | None: def _fit_data_axes_labels(experiment: object, x_descriptor: object) -> list[str]: """Return Plotly display-axis labels for a report fit figure.""" - experiment_type = _safe_attr(experiment, 'type') + experiment_type = _safe_attr(experiment, 'experiment_type') try: sample_form = experiment_type.sample_form.value scattering_type = experiment_type.scattering_type.value @@ -1156,7 +1189,7 @@ def _fit_data_bragg_tick_sets( def _is_powder_bragg_experiment(experiment: object) -> bool: """Return whether an experiment can use powder Bragg plot panels.""" - experiment_type = _safe_attr(experiment, 'type') + experiment_type = _safe_attr(experiment, 'experiment_type') sample_form = _value(_safe_attr(experiment_type, 'sample_form')) scattering_type = _value(_safe_attr(experiment_type, 'scattering_type')) return sample_form == 'powder' and scattering_type == 'bragg' @@ -1164,7 +1197,7 @@ def _is_powder_bragg_experiment(experiment: object) -> bool: def _is_single_crystal_bragg_experiment(experiment: object) -> bool: """Return whether an experiment is single-crystal Bragg.""" - experiment_type = _safe_attr(experiment, 'type') + experiment_type = _safe_attr(experiment, 'experiment_type') sample_form = _value(_safe_attr(experiment_type, 'sample_form')) scattering_type = _value(_safe_attr(experiment_type, 'scattering_type')) return sample_form == 'single crystal' and scattering_type == 'bragg' diff --git a/src/easydiffraction/report/html_renderer.py b/src/easydiffraction/report/html_renderer.py index bd07da02c..0a87e73d9 100644 --- a/src/easydiffraction/report/html_renderer.py +++ b/src/easydiffraction/report/html_renderer.py @@ -51,7 +51,7 @@ def html_report_path( if path is not None: return pathlib.Path(path) - project_path = getattr(getattr(project, 'info', None), 'path', None) + project_path = getattr(getattr(project, 'metadata', None), 'path', None) if project_path is None: msg = 'Project has no saved path. Save the project first.' raise FileNotFoundError(msg) diff --git a/src/easydiffraction/report/templates/html/report.html.j2 b/src/easydiffraction/report/templates/html/report.html.j2 index 1f9069788..62de8f3d8 100644 --- a/src/easydiffraction/report/templates/html/report.html.j2 +++ b/src/easydiffraction/report/templates/html/report.html.j2 @@ -99,21 +99,13 @@ </tr> </thead> <tbody> + {% for role in analysis.software.roles %} <tr> - <td>Framework</td> - <td>{{ software_name(analysis.software.framework) }}</td> - <td>{{ analysis.software.framework.version or "" }}</td> - </tr> - <tr> - <td>Calculator</td> - <td>{{ software_name(analysis.software.calculator) }}</td> - <td>{{ analysis.software.calculator.version or "" }}</td> - </tr> - <tr> - <td>Minimizer</td> - <td>{{ software_name(analysis.software.minimizer) }}</td> - <td>{{ analysis.software.minimizer.version or "" }}</td> + <td>{{ role.id | title }}</td> + <td>{{ software_name(role) }}</td> + <td>{{ role.version or "" }}</td> </tr> + {% endfor %} </tbody> </table> diff --git a/src/easydiffraction/report/templates/tex/figure.tex.j2 b/src/easydiffraction/report/templates/tex/figure.tex.j2 index 85e4dea98..abece8399 100644 --- a/src/easydiffraction/report/templates/tex/figure.tex.j2 +++ b/src/easydiffraction/report/templates/tex/figure.tex.j2 @@ -59,7 +59,7 @@ legend style={draw=none, fill=white, fill opacity=0.5, text opacity=1, font=\foo {% for tick_set in bragg_tick_sources %} {% set tick_style = bragg_styles[loop.index0 % (bragg_styles | length)] -%} \addlegendimage{color={{ tick_style.color_name }}, only marks, mark=|, mark size=5pt} -\addlegendentry{Bragg peaks: {{ tick_set.phase_id | tex }}} +\addlegendentry{Bragg peaks: {{ tick_set.structure_id | tex }}} {% endfor %} {% if bragg_tick_sources %} \nextgroupplot[ @@ -68,7 +68,7 @@ ymin=0.5, ymax={{ (bragg_tick_sources | length) + 0.5 }}, y dir=reverse, ytick={{ "{" }}{% for tick_set in bragg_tick_sources %}{{ loop.index }}{% if not loop.last %},{% endif %}{% endfor %}{{ "}" }}, -yticklabels={{ "{" }}{% for tick_set in bragg_tick_sources %}{{ "{" }}{{ tick_set.phase_id | tex }}{{ "}" }}{% if not loop.last %},{% endif %}{% endfor %}{{ "}" }}, +yticklabels={{ "{" }}{% for tick_set in bragg_tick_sources %}{{ "{" }}{{ tick_set.structure_id | tex }}{{ "}" }}{% if not loop.last %},{% endif %}{% endfor %}{{ "}" }}, yticklabel style={font=\footnotesize}, ymajorgrids=false, xticklabels=\empty, diff --git a/src/easydiffraction/report/templates/tex/report.tex.j2 b/src/easydiffraction/report/templates/tex/report.tex.j2 index 548dfbcd4..1221a0dac 100644 --- a/src/easydiffraction/report/templates/tex/report.tex.j2 +++ b/src/easydiffraction/report/templates/tex/report.tex.j2 @@ -160,9 +160,9 @@ Generated & {{ metadata.generated_at | tex }} \\ \hline Role & Name & Version \\ \hline -Framework & {{ tex_software_name(analysis.software.framework) }} & {{ analysis.software.framework.version | tex }} \\ -Calculator & {{ tex_software_name(analysis.software.calculator) }} & {{ analysis.software.calculator.version | tex }} \\ -Minimizer & {{ tex_software_name(analysis.software.minimizer) }} & {{ analysis.software.minimizer.version | tex }} \\ +{% for role in analysis.software.roles -%} +{{ role.id | title | tex }} & {{ tex_software_name(role) }} & {{ role.version | tex }} \\ +{% endfor -%} \hline \end{tabular} \end{table} diff --git a/src/easydiffraction/report/tex_renderer.py b/src/easydiffraction/report/tex_renderer.py index c30d6243c..1236f1dbf 100644 --- a/src/easydiffraction/report/tex_renderer.py +++ b/src/easydiffraction/report/tex_renderer.py @@ -45,7 +45,7 @@ 'r': '_pd_proc.r', } _FIT_CSV_FIELD_TAGS = ( - ('point_id', '_pd_data.point_id'), + ('id', '_pd_data.point_id'), ('d_spacing', '_pd_proc.d_spacing'), ('intensity_meas', '_pd_meas.intensity_total'), ('intensity_meas_su', '_pd_meas.intensity_total_su'), @@ -55,7 +55,7 @@ ) _REFLN_CSV_FIELD_TAGS = ( ('id', '_refln.id'), - ('phase_id', '_refln.phase_id'), + ('structure_id', '_pd_refln.phase_id'), ('d_spacing', '_refln.d_spacing'), ('sin_theta_over_lambda', '_refln.sin_theta_over_lambda'), ('index_h', '_refln.index_h'), @@ -95,7 +95,7 @@ def tex_report_path( if path is not None: return pathlib.Path(path) - project_path = getattr(getattr(project, 'info', None), 'path', None) + project_path = getattr(getattr(project, 'metadata', None), 'path', None) if project_path is None: msg = 'Project has no saved path. Save the project first.' raise FileNotFoundError(msg) @@ -521,7 +521,7 @@ def _fit_csv_columns( category_values = _category_values( source_experiment, experiment, - code='pd_data', + code='data', ) x_field = _fit_x_field(category_values, fit_data) row_count = len(list(x_data['values'])) @@ -537,7 +537,7 @@ def _fit_csv_columns( ), ] fallback_values = { - 'point_id': [str(index + 1) for index in range(row_count)], + 'id': [str(index + 1) for index in range(row_count)], 'd_spacing': _empty_csv_values(row_count), 'intensity_meas': list(meas['values']), 'intensity_meas_su': _series_values_or_empty(meas.get('su'), row_count), @@ -579,7 +579,7 @@ def _fit_csv_plot_columns( fit_data: dict[str, object], ) -> dict[str, str]: """Return CSV column tags used by the standalone fit figure.""" - x_field = _fit_x_field(_context_category_values(experiment, 'pd_data'), fit_data) + x_field = _fit_x_field(_context_category_values(experiment, 'data'), fit_data) return { 'x': _FIT_X_FIELD_TAGS[x_field], 'meas': '_pd_meas.intensity_total', @@ -632,25 +632,25 @@ def _write_refln_category_csvs( values: dict[str, list[object]], data_dir: pathlib.Path, ) -> dict[str, dict[str, str]]: - """Write reflection-category rows split by phase id.""" - phase_values = values.get('phase_id') - if phase_values is None: + """Write reflection-category rows split by structure id.""" + structure_values = values.get('structure_id') + if structure_values is None: return {} - row_indexes_by_phase: dict[str, list[int]] = {} - for row_index, phase_value in enumerate(phase_values): - if _is_csv_empty(phase_value): + row_indexes_by_structure: dict[str, list[int]] = {} + for row_index, structure_value in enumerate(structure_values): + if _is_csv_empty(structure_value): continue - phase_id = str(phase_value) - row_indexes_by_phase.setdefault(phase_id, []).append(row_index) + structure_id = str(structure_value) + row_indexes_by_structure.setdefault(structure_id, []).append(row_index) csvs: dict[str, dict[str, str]] = {} x_column = _refln_x_column(values) - for phase_id, row_indexes in row_indexes_by_phase.items(): - csv_path = data_dir / _bragg_csv_filename(expt_id, phase_id) + for structure_id, row_indexes in row_indexes_by_structure.items(): + csv_path = data_dir / _bragg_csv_filename(expt_id, structure_id) columns = _refln_csv_columns(values, row_indexes) _write_csv(csv_path, expt_id, columns) - csvs[phase_id] = { + csvs[structure_id] = { 'filename': csv_path.name, 'x_column': x_column, } @@ -689,11 +689,11 @@ def _write_bragg_tick_set_csvs( """Write fallback Bragg CSVs from plot tick-set data.""" csvs: dict[str, dict[str, str]] = {} for tick_set in fit_data.get('bragg_tick_sets') or (): - phase_id = str(tick_set.phase_id) - csv_path = data_dir / _bragg_csv_filename(expt_id, phase_id) + structure_id = str(tick_set.structure_id) + csv_path = data_dir / _bragg_csv_filename(expt_id, structure_id) columns = _bragg_tick_set_columns(tick_set) _write_csv(csv_path, expt_id, columns) - csvs[phase_id] = { + csvs[structure_id] = { 'filename': csv_path.name, 'x_column': '_refln.two_theta', } @@ -705,7 +705,7 @@ def _bragg_tick_set_columns(tick_set: object) -> list[tuple[str, list[object]]]: row_count = len(tick_set.x) return [ ('_refln.id', [str(index + 1) for index in range(row_count)]), - ('_refln.phase_id', [tick_set.phase_id] * row_count), + ('_pd_refln.phase_id', [tick_set.structure_id] * row_count), ('_refln.index_h', list(tick_set.h)), ('_refln.index_k', list(tick_set.k)), ('_refln.index_l', list(tick_set.ell)), @@ -715,9 +715,9 @@ def _bragg_tick_set_columns(tick_set: object) -> list[tuple[str, list[object]]]: ] -def _bragg_csv_filename(expt_id: str, phase_id: str) -> str: - """Return the Bragg-position CSV filename for one phase.""" - return f'{_safe_asset_stem(expt_id)}_{_safe_asset_stem(phase_id)}.csv' +def _bragg_csv_filename(expt_id: str, structure_id: str) -> str: + """Return the Bragg-position CSV filename for one structure.""" + return f'{_safe_asset_stem(expt_id)}_{_safe_asset_stem(structure_id)}.csv' def _bragg_tick_sources( @@ -727,12 +727,12 @@ def _bragg_tick_sources( """Return template context for Bragg-position CSV sources.""" sources = [] for tick_set in fit_data.get('bragg_tick_sets') or (): - phase_id = str(tick_set.phase_id) - bragg_csv = bragg_csvs.get(phase_id) + structure_id = str(tick_set.structure_id) + bragg_csv = bragg_csvs.get(structure_id) if bragg_csv is None: continue sources.append({ - 'phase_id': phase_id, + 'structure_id': structure_id, 'csv_filename': bragg_csv['filename'], 'x_column': bragg_csv['x_column'], }) diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index a5e57980a..9e62ea631 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -5,10 +5,14 @@ from __future__ import annotations import functools +import hashlib +import importlib.resources import json import pathlib +import re import shutil import urllib.request +from enum import StrEnum from importlib.metadata import PackageNotFoundError from importlib.metadata import version from urllib.parse import urlparse @@ -130,15 +134,223 @@ def format_bulleted_warning(header: str, items: list[str]) -> str: _DATA_REPO = 'easyscience/diffraction' _DATA_ROOT = 'data' -# commit SHA preferred -_DATA_INDEX_REF = '83657ee120fc6a30fda231649692930eaa038758' -# macOS: sha256sum index.json -_DATA_INDEX_HASH = 'sha256:e7685d7c81c3b3559a7f630178f4d1b7f441fb1ed14388c10ab9f6aeb93927a7' +_DOCS_BASE_URL = 'https://easyscience.github.io/diffraction-lib' +_PARAMETER_DOCS_BLOCKS = { + 'project': frozenset({ + 'alias', + 'metadata', + 'rendering_plot', + 'rendering_structure', + 'rendering_table', + 'report', + 'structure_style', + 'structure_view', + 'verbosity', + }), + 'structure': frozenset({ + 'atom_site', + 'atom_site_aniso', + 'cell', + 'geom', + 'space_group', + 'space_group_Wyckoff', + }), + 'experiment': frozenset({ + 'absorption', + 'background', + 'calculator', + 'data', + 'diffrn', + 'excluded_region', + 'experiment_type', + 'extinction', + 'instrument', + 'linked_structure', + 'pd_background', + 'pd_meas', + 'peak', + 'preferred_orientation', + 'refln', + }), + 'analysis': frozenset({ + 'constraint', + 'fit_parameter', + 'fit_parameter_correlation', + 'fit_result', + 'fitting_mode', + 'joint_fit', + 'minimizer', + 'sequential_fit', + 'sequential_fit_extract', + 'software', + }), +} +_PARAMETER_DOCS_CATEGORY_PAGES = { + 'excluded_regions': 'excluded_region', +} +_PARAMETER_DOCS_ITEM_ROUTES = { + ('data_range', 'two_theta_inc'): ('experiment/pd_meas', 'pd-meas-2theta-range-inc'), + ('data_range', 'two_theta_max'): ('experiment/pd_meas', 'pd-meas-2theta-range-max'), + ('data_range', 'two_theta_min'): ('experiment/pd_meas', 'pd-meas-2theta-range-min'), + ( + 'data_range', + 'time_of_flight_inc', + ): ('experiment/pd_meas', 'pd-meas-time-of-flight-range-inc'), + ( + 'data_range', + 'time_of_flight_max', + ): ('experiment/pd_meas', 'pd-meas-time-of-flight-range-max'), + ( + 'data_range', + 'time_of_flight_min', + ): ('experiment/pd_meas', 'pd-meas-time-of-flight-range-min'), + ( + 'data_range', + 'sin_theta_over_lambda_max', + ): ('experiment/refln', 'refln-sin-theta-over-lambda-range-max'), + ( + 'data_range', + 'sin_theta_over_lambda_min', + ): ('experiment/refln', 'refln-sin-theta-over-lambda-range-min'), +} +# The downloadable data is pinned to one git commit of the data +# repository, stored in this packaged file and read at runtime (see the +# data-source-pinning ADR). It is the single source of truth for which +# data snapshot a build uses; edit the file to bump the data. +_DATA_INDEX_REF_RESOURCE = '_data_index_ref.txt' +_FULL_SHA_LENGTH = 40 +_HEX_DIGITS = frozenset('0123456789abcdef') + + +@functools.lru_cache(maxsize=1) +def _data_index_ref() -> str: + """ + Return the validated commit pinning the downloadable data. + + Returns + ------- + str + The full 40-character hexadecimal commit SHA read from the + packaged ``_data_index_ref.txt`` file. + + Raises + ------ + ValueError + If the stored value is not a full 40-character hex commit SHA. + """ + raw = ( + importlib.resources + .files('easydiffraction') + .joinpath(_DATA_INDEX_REF_RESOURCE) + .read_text(encoding='utf-8') + ) + ref = raw.strip() + if len(ref) != _FULL_SHA_LENGTH or not set(ref.lower()) <= _HEX_DIGITS: + msg = ( + f"Invalid data index ref '{ref}' in {_DATA_INDEX_REF_RESOURCE}: " + 'expected a full 40-character hexadecimal git commit SHA.' + ) + raise ValueError(msg) + return ref def _build_data_url(path: str) -> str: path = path.lstrip('/') - return f'https://raw.githubusercontent.com/{_DATA_REPO}/{_DATA_INDEX_REF}/{_DATA_ROOT}/{path}' + return ( + f'https://raw.githubusercontent.com/{_DATA_REPO}/{_data_index_ref()}/{_DATA_ROOT}/{path}' + ) + + +class DataCategoryEnum(StrEnum): + """ + The fixed category prefixes a downloadable dataset name carries. + """ + + STRUCTURE = 'struct' + EXPERIMENT = 'expt' + MEASURED = 'meas' + PROJECT = 'proj' + + +_DATA_CATEGORIES = frozenset(member.value for member in DataCategoryEnum) +# One slug: lowercase ASCII letters/digits in dash-separated groups, no +# leading/trailing/doubled dashes (resource-naming ADR). +_SLUG_SEGMENT_RE = re.compile(r'[a-z0-9]+(?:-[a-z0-9]+)*') + + +def _validate_slug_segment(segment: str, *, kind: str) -> None: + """Raise ValueError unless ``segment`` is a valid slug segment.""" + if not _SLUG_SEGMENT_RE.fullmatch(segment): + msg = ( + f"Invalid {kind} '{segment}': expected lowercase ASCII letters and " + 'digits in dash-separated groups (e.g. lbco-hrpt), with no file ' + 'extension, empty segments, or other characters.' + ) + raise ValueError(msg) + + +def _validate_dataset_id(name: str) -> None: + """Validate a dataset name of the form ``<category>-<slug>``.""" + if '/' in name: + msg = ( + f"Invalid dataset name '{name}': use a single dash-joined name such as " + "'meas-lbco-hrpt', not a path with '/'." + ) + raise ValueError(msg) + _validate_slug_segment(name, kind='dataset name') + category = name.split('-', 1)[0] + if category == name or category not in _DATA_CATEGORIES: + msg = ( + f"Invalid dataset category in '{name}': expected a '<category>-<name>' " + f'name with a category prefix from {sorted(_DATA_CATEGORIES)}.' + ) + raise ValueError(msg) + + +def _validate_tutorial_id(name: str) -> None: + """Validate a tutorial name (a single slug segment, no '/').""" + if '/' in name: + msg = f"Invalid tutorial name '{name}': tutorials use a bare name with no '/'." + raise ValueError(msg) + _validate_slug_segment(name, kind='tutorial name') + + +_DEFAULT_LISTING_ORDER = 1_000_000 + + +def _ordered_keys(index: dict) -> list[str]: + """ + Return index keys in the deterministic order listings show. + + Records may carry an explicit ``order`` field (e.g. the tutorial + learning order from the MkDocs nav, per resource-naming ADR Decision + 4); those sort first by that order. Records without one (e.g. + datasets) fall back to alphabetical by name. + """ + return sorted(index, key=lambda key: (index[key].get('order', _DEFAULT_LISTING_ORDER), key)) + + +def _is_positional(name: int | str) -> bool: + """ + Return True when ``name`` is an interactive positional shortcut. + """ + return isinstance(name, int) or (isinstance(name, str) and name.isdigit()) + + +def _resolve_positional(position: int, keys: list[str], *, kind: str) -> str: + """ + Resolve a 1-based listing row number to an index key. + + The number is a transient row index, never a stored identity + (resource-naming ADR, Decision 6). + """ + if not 1 <= position <= len(keys): + msg = ( + f'Invalid {kind} number {position}: expected 1..{len(keys)} as shown ' + f'by the listing. Use the name for saved code.' + ) + raise IndexError(msg) + return keys[position - 1] def _record_path(record: dict) -> str: @@ -169,15 +381,15 @@ def _validate_url(url: str) -> None: raise ValueError(msg) -def _filename_for_id_from_path(data_id: int | str, record_path: str) -> str: +def _local_filename(resource_id: str, record_path: str) -> str: """ - Return local filename using the extension from the record path. + Return the local download filename, ``<name>.<ext>``. + + The repository path may live under a category folder, but the saved + file is named after the dataset name so it matches the id the user + typed (e.g. ``meas-lbco-hrpt.xye``). """ - suffix = pathlib.PurePosixPath( - record_path - ).suffix # includes leading dot ('.cif', '.xye', ...) - # If URL has no suffix, fall back to no extension. - return f'ed-{data_id}{suffix}' + return f'{resource_id}{pathlib.PurePosixPath(record_path).suffix}' def _normalize_known_hash(value: str | None) -> str | None: @@ -194,18 +406,48 @@ def _normalize_known_hash(value: str | None) -> str | None: return value +def _record_hash(record: dict) -> str | None: + """Return the normalized ``sha256:`` content hash for a record.""" + return _normalize_known_hash(record.get('hash')) + + +def _sha256_of_file(path: pathlib.Path) -> str: + """Return the ``sha256:<hex>`` digest of a file's contents.""" + digest = hashlib.sha256() + with path.open('rb') as handle: + for chunk in iter(lambda: handle.read(65536), b''): + digest.update(chunk) + return f'sha256:{digest.hexdigest()}' + + +def _content_tag(record: dict) -> str: + """ + Return a short content tag for keying extracted project directories. + + Derived from the record's ``sha256`` so that a replaced archive + extracts to a fresh directory instead of reusing a stale one + (data-source-pinning ADR, Decision 7). Returns an empty string when + the record carries no usable hash. + """ + known = _record_hash(record) + if known is None: + return '' + return known.split(':', 1)[-1][:12] + + def _fetch_data_index() -> dict: """Fetch and cache the diffraction data index.json.""" index_url = _build_data_url('index.json') _validate_url(index_url) - destination_dirname = 'easydiffraction' - destination_fname = 'data-index.json' - cache_dir = pooch.os_cache(destination_dirname) + cache_dir = pooch.os_cache('easydiffraction') + # Cache under a commit-named file so a ref bump downloads a fresh + # index instead of reusing a stale one (data-source-pinning ADR). + destination_fname = f'data-index-{_data_index_ref()}.json' index_path = pooch.retrieve( url=index_url, - known_hash=_DATA_INDEX_HASH, + known_hash=None, fname=destination_fname, path=cache_dir, progressbar=False, @@ -217,23 +459,30 @@ def _fetch_data_index() -> dict: def _existing_project_dir(extraction_dir: pathlib.Path) -> pathlib.Path | None: """Return one extracted project directory from a destination.""" - project_files = sorted(extraction_dir.rglob('project.cif')) + project_files = sorted(extraction_dir.rglob('project.edi')) if not project_files: return None return project_files[0].parent.resolve() -def _download_data_message(data_id: int | str, record: dict) -> str: +def _download_data_message(name: str, record: dict) -> str: """Return the console message for one downloadable data record.""" description = record.get('description', '') - message = f'Data #{data_id}' + message = f"Data '{name}'" if description: message += f': {description}' return message +def _is_project_id(resource_id: str) -> bool: + """ + Return True for project-archive names (the ``proj-`` category). + """ + return resource_id.startswith(f'{DataCategoryEnum.PROJECT.value}-') + + def _download_data_targets( - data_id: int | str, + resource_id: str, destination: str, record: dict, ) -> tuple[str, bool, pathlib.Path, pathlib.Path, pathlib.Path, str]: @@ -242,12 +491,19 @@ def _download_data_targets( url = _build_data_url(record_path) _validate_url(url) - fname = _filename_for_id_from_path(data_id, record_path) - is_project_archive = record.get('kind') == 'project' and fname.endswith('.zip') + fname = _local_filename(resource_id, record_path) + # The category prefix carries the kind, so a project archive is any + # ``proj-`` name delivered as a ZIP (resource-naming ADR). + is_project_archive = _is_project_id(resource_id) and fname.endswith('.zip') dest_path = resolve_artifact_path(destination) dest_path.mkdir(parents=True, exist_ok=True) file_path = dest_path / fname - extraction_dir = dest_path / pathlib.Path(fname).stem + # Key the extraction directory by content so a replaced project + # archive extracts to a fresh directory instead of reusing a stale + # one (data-source-pinning ADR, Decision 7). + stem = pathlib.Path(fname).stem + tag = _content_tag(record) + extraction_dir = dest_path / (f'{stem}-{tag}' if tag else stem) return url, is_project_archive, dest_path, file_path, extraction_dir, fname @@ -283,21 +539,37 @@ def _fetch_tutorials_index() -> dict: return {} +def _resolve_data_id(name: int | str, index: dict) -> str: + """ + Resolve a dataset name or interactive row number to an index key. + """ + if _is_positional(name): + return _resolve_positional(int(name), _ordered_keys(index), kind='dataset') + name = str(name) + _validate_dataset_id(name) + if name not in index: + msg = f"Unknown dataset '{name}'. Run list_data() to see available datasets." + raise KeyError(msg) + return name + + def download_data( - id: int | str, + name: int | str, destination: str = 'data', *, overwrite: bool = False, ) -> str: """ - Download a dataset by numeric ID using the remote diffraction index. + Download a dataset by its name from the diffraction data index. - Example: path = download_data(id=12, destination="data") + Example: path = download_data('expt-lbco-hrpt') Parameters ---------- - id : int | str - Numeric dataset id (e.g. 12). + name : int | str + Dataset name ``<category>-<name>`` (e.g. ``'struct-lbco'``, + ``'meas-lbco-hrpt'``). Interactively, the row number shown by + :func:`list_data` is also accepted; use the name in saved code. destination : str, default='data' Directory to save the downloaded file or extracted project into (created if missing). Relative destinations are resolved against @@ -310,29 +582,16 @@ def download_data( ------- str Full path to the downloaded file, or to the extracted project - directory for project ZIP archives, as string. - - Raises - ------ - KeyError - If the id is not found in the index. + directory for project ZIP archives, as string. A malformed name + raises ``ValueError`` and an unknown name raises ``KeyError``. """ index = _fetch_data_index() - key = str(id) - - if key not in index: - # Provide a helpful message (and keep KeyError semantics) - available = ', '.join( - sorted(index.keys(), key=lambda s: int(s) if s.isdigit() else s)[:20] - ) - msg = f'Unknown dataset id={id}. Example available ids: {available} ...' - raise KeyError(msg) - - record = index[key] + resource_id = _resolve_data_id(name, index) + record = index[resource_id] url, is_project_archive, dest_path, file_path, extraction_dir, fname = _download_data_targets( - id, destination, record + resource_id, destination, record ) - message = _download_data_message(id, record) + message = _download_data_message(resource_id, record) console.paragraph('Getting data...') console.print(f'{message}') @@ -341,29 +600,37 @@ def download_data( existing_project_dir = _existing_project_dir(extraction_dir) if existing_project_dir is not None: console.print( - f"✅ Data #{id} already extracted at '{display_path(existing_project_dir)}'. " - 'Keeping existing.' + f"✅ Data '{resource_id}' already extracted at " + f"'{display_path(existing_project_dir)}'. Keeping existing." ) return str(existing_project_dir) + known_hash = _record_hash(record) + if file_path.exists(): - if is_project_archive and not overwrite: + # Reuse a local file only when its bytes match the pinned index; + # a content mismatch means the dataset was replaced upstream, so + # re-download instead of serving stale data — including a stale + # project ZIP, which must be verified before it is extracted + # (data-source-pinning ADR, Decision 7). + stale = known_hash is not None and _sha256_of_file(file_path) != known_hash + if is_project_archive and not overwrite and not stale: project_dir = extract_project_from_zip(file_path, destination=extraction_dir) file_path.unlink() - console.print(f"✅ Data #{id} extracted to '{display_path(project_dir)}'") + console.print(f"✅ Data '{resource_id}' extracted to '{display_path(project_dir)}'") return str(project_dir) - if not overwrite: + if not is_project_archive and not overwrite and not stale: console.print( - f"✅ Data #{id} already present at '{display_path(file_path)}'. Keeping existing." + f"✅ Data '{resource_id}' already present at " + f"'{display_path(file_path)}'. Keeping existing." ) return str(file_path) + reason = 'is stale' if stale and not overwrite else 'will be overwritten' log.debug( - f"Data #{id} already present at '{display_path(file_path)}', but will be overwritten." + f"Data '{resource_id}' already present at '{display_path(file_path)}', but {reason}." ) file_path.unlink() - known_hash = _normalize_known_hash(record.get('hash')) - if is_project_archive and extraction_dir.exists() and overwrite: shutil.rmtree(extraction_dir) @@ -378,32 +645,37 @@ def download_data( if is_project_archive: project_dir = extract_project_from_zip(file_path, destination=extraction_dir) file_path.unlink() - console.print(f"✅ Data #{id} downloaded and extracted to '{display_path(project_dir)}'") + console.print( + f"✅ Data '{resource_id}' downloaded and extracted to '{display_path(project_dir)}'" + ) return str(project_dir) - console.print(f"✅ Data #{id} downloaded to '{display_path(file_path)}'") + console.print(f"✅ Data '{resource_id}' downloaded to '{display_path(file_path)}'") return str(file_path) def list_data() -> None: - """Display a table of available example data records.""" + """Display a table of available downloadable datasets.""" index = _fetch_data_index() if not index: - console.print('❌ No example data available.') + console.print('❌ No datasets available.') return - console.paragraph('Example data available for download:') + console.paragraph('Datasets available for download:') - columns_headers = ['id', 'file', 'kind', 'description'] - columns_alignment = ['right', 'left', 'left', 'left'] + # The table renderer adds its own leading row number. The category + # prefix carries the kind, so only the name, format, and description + # are shown; the name is the canonical download handle. + columns_headers = ['name', 'format', 'description'] + columns_alignment = ['left', 'left', 'left'] columns_data = [] - for data_id in sorted(index, key=lambda value: int(value) if value.isdigit() else value): - record = index[data_id] + for resource_id in _ordered_keys(index): + record = index[resource_id] + suffix = pathlib.PurePosixPath(_record_path(record)).suffix.lstrip('.') columns_data.append([ - data_id, - pathlib.PurePosixPath(_record_path(record)).name, - record.get('kind', ''), + resource_id, + suffix, record.get('description', ''), ]) @@ -517,6 +789,81 @@ def _get_version_for_url(package_name: str = 'easydiffraction') -> str: return stripped_package_version(package_name) or 'dev' +def parameter_docs_url( + data_name: str, + *, + page: str | None = None, + anchor: str | None = None, + package_name: str = 'easydiffraction', +) -> str: + """ + Return a versioned parameter documentation URL. + + Parameters + ---------- + data_name : str + Edi data name, such as ``'_cell.length_a'``. + page : str | None, default=None + Parameter reference page override. + anchor : str | None, default=None + Parameter anchor override. + package_name : str, default='easydiffraction' + Package used to resolve the documentation version. + + Returns + ------- + str + Absolute URL for the parameter reference entry. + """ + resolved_page, resolved_anchor = _parameter_docs_route( + data_name, + page=page, + anchor=anchor, + ) + version = _get_version_for_url(package_name) + base_url = f'{_DOCS_BASE_URL}/{version}/user-guide/parameters/{resolved_page}/' + return f'{base_url}#{resolved_anchor}' + + +def _parameter_docs_route( + data_name: str, + *, + page: str | None, + anchor: str | None, +) -> tuple[str, str]: + """Resolve the parameter-reference page and anchor.""" + category, item = _split_parameter_data_name(data_name) + if page is None and anchor is None: + override = _PARAMETER_DOCS_ITEM_ROUTES.get((category, item)) + if override is not None: + return override + + route_category = _PARAMETER_DOCS_CATEGORY_PAGES.get(category, category) + resolved_page = page or _parameter_docs_page(route_category) + resolved_anchor = anchor or _parameter_docs_anchor(route_category, item) + return resolved_page.strip('/'), resolved_anchor + + +def _parameter_docs_page(category: str) -> str: + """Return the grouped parameter-reference page for a category.""" + for block, categories in _PARAMETER_DOCS_BLOCKS.items(): + if category in categories: + return f'{block}/{category}' + return category + + +def _split_parameter_data_name(data_name: str) -> tuple[str, str]: + """Split a data name into category and item components.""" + category, _, item = data_name.strip().lstrip('_').partition('.') + return category, item + + +def _parameter_docs_anchor(category: str, item: str) -> str: + """Return the stable docs anchor for a category item.""" + parts = [part for part in (category, item) if part] + return '-'.join(parts).replace('_', '-').lower() + + def _safe_urlopen(request_or_url: object) -> object: # type: ignore[no-untyped-def] """ Open a URL with prior validation. @@ -558,109 +905,166 @@ def _resolve_tutorial_url(url_template: str) -> str: return url_template.replace('{version}', version) +class TutorialFormat(StrEnum): + """The file formats a tutorial can be downloaded in.""" + + IPYNB = 'ipynb' + PY = 'py' + + +def _resolve_tutorial_format(file_format: str) -> TutorialFormat: + """Return the validated tutorial format, or raise ValueError.""" + try: + return TutorialFormat(file_format) + except ValueError: + allowed = ', '.join(repr(member.value) for member in TutorialFormat) + msg = f"Unknown tutorial format '{file_format}'. Choose one of: {allowed}." + raise ValueError(msg) from None + + +def _tutorial_url_for_format(url: str, fmt: TutorialFormat) -> str: + """Return the published URL for the requested tutorial format.""" + if fmt is TutorialFormat.IPYNB: + return url + if not url.endswith('.ipynb'): + msg = f'Tutorial URL does not point to a notebook: {url}' + raise ValueError(msg) + # The notebook is published nested at + # ``tutorials/<name>/<name>.ipynb`` (mkdocs-jupyter include_source), + # whereas the .py source is a flat static copy at + # ``tutorials/<name>.py``. Map the former to the latter, falling + # back to a same-directory swap for a flat layout. + name = pathlib.PurePosixPath(urlparse(url).path).name[: -len('.ipynb')] + nested_suffix = f'{name}/{name}.ipynb' + if url.endswith(nested_suffix): + return f'{url[: -len(nested_suffix)]}{name}.py' + return f'{url[: -len(".ipynb")]}.py' + + +# Cap the listing table so it stays readable on very wide terminals +# while still shrinking to fit narrower ones. +_LIST_TABLE_MAX_WIDTH = 100 + + +def _list_table_width() -> int: + """Return the listing-table width, capped at the maximum.""" + return min(shutil.get_terminal_size().columns, _LIST_TABLE_MAX_WIDTH) + + def list_tutorials() -> None: """ Display a table of available tutorial notebooks. - In the terminal each row shows the tutorial ID, filename, and a - combined entry with the title on the first line and a dimmed - description on the second. In Jupyter the table shows the plain - title only, since the HTML backend cannot render the terminal - styling. + Each tutorial occupies one multi-line cell: the name on the first + line (default color), the title on the second, and a dimmed + description on the third. This keeps long descriptions readable. In + Jupyter the cell uses plain lines, since the HTML backend cannot + render the terminal styling. """ index = _fetch_tutorials_index() if not index: console.print('❌ No tutorials available.') return - version = _get_version_for_url() + version = package_version('easydiffraction') or _get_version_for_url() console.paragraph(f'Tutorials available for easydiffraction v{version}:') - columns_headers = ['id', 'file', 'tutorial'] - columns_alignment = ['right', 'left', 'left'] + # One column: each tutorial is a name/title/description stack. The + # renderer adds its own leading row number, so the name lives in the + # cell rather than a separate column. + columns_headers = ['tutorial'] + columns_alignment = ['left'] columns_data = [] use_markup = not in_jupyter() - for tutorial_id in index: + for tutorial_id in _ordered_keys(index): record = index[tutorial_id] - filename = f'ed-{tutorial_id}.ipynb' title = record.get('title', '') description = record.get('description', '') if not use_markup: - # Jupyter uses the HTML table backend, which would show Rich - # markup as literal text; keep the plain title there. - details = title + # Jupyter renders Rich markup as literal text; emit plain + # lines instead. + lines = [line for line in (tutorial_id, title, description) if line] else: - styled_title = f'[{CONSOLE_PARAGRAPH_STYLE}]{escape(title)}[/]' + lines = [escape(tutorial_id), f'[{CONSOLE_PARAGRAPH_STYLE}]{escape(title)}[/]'] if description: - details = f'{styled_title}\n[dim]{escape(description)}[/dim]' - else: - details = styled_title - columns_data.append([tutorial_id, filename, details]) + lines.append(f'[dim]{escape(description)}[/dim]') + columns_data.append(['\n'.join(lines)]) render_table( columns_headers=columns_headers, columns_data=columns_data, columns_alignment=columns_alignment, - width=shutil.get_terminal_size().columns, + width=_list_table_width(), ) +def _resolve_tutorial_id(name: int | str, index: dict) -> str: + """ + Resolve a tutorial name or interactive row number to an index key. + """ + if _is_positional(name): + return _resolve_positional(int(name), _ordered_keys(index), kind='tutorial') + name = str(name) + _validate_tutorial_id(name) + if name not in index: + msg = f"Unknown tutorial '{name}'. Run list_tutorials() to see available tutorials." + raise KeyError(msg) + return name + + def download_tutorial( - id: int | str, + name: int | str, destination: str = 'tutorials', *, + file_format: str = 'ipynb', overwrite: bool = False, ) -> str: """ - Download a tutorial notebook by numeric ID. + Download a tutorial by its name in one file format. - Example: path = download_tutorial(id=1, destination="tutorials") + Example: path = download_tutorial('refine-lbco-hrpt-from-cif') Parameters ---------- - id : int | str - Numeric tutorial id (e.g. 1). + name : int | str + Tutorial name (e.g. ``'refine-lbco-hrpt-from-cif'``). + Interactively, the row number shown by :func:`list_tutorials` is + also accepted; use the name in saved code. destination : str, default='tutorials' Directory to save the file into (created if missing). Relative destinations are resolved against the configured artifact root when ``EASYDIFFRACTION_ARTIFACT_ROOT`` is set. + file_format : str, default='ipynb' + File format to download: ``'ipynb'`` for the Jupyter notebook or + ``'py'`` for the plain-Python script. overwrite : bool, default=False Whether to overwrite the file if it already exists. Returns ------- str - Full path to the downloaded file as string. - - Raises - ------ - KeyError - If the id is not found in the index. + Full path to the downloaded file as string. A malformed name or + format raises ``ValueError`` and an unknown name raises + ``KeyError``. """ + fmt = _resolve_tutorial_format(file_format) index = _fetch_tutorials_index() - key = str(id) + resource_id = _resolve_tutorial_id(name, index) - if key not in index: - available = ', '.join( - sorted(index.keys(), key=lambda s: int(s) if s.isdigit() else s)[:20] - ) - msg = f'Unknown tutorial id={id}. Available ids: {available}' - raise KeyError(msg) - - record = index[key] + record = index[resource_id] url_template = record['url'] - url = _resolve_tutorial_url(url_template) + url = _tutorial_url_for_format(_resolve_tutorial_url(url_template), fmt) _validate_url(url) - fname = f'ed-{id}.ipynb' + fname = f'{resource_id}.{fmt.value}' dest_path = resolve_artifact_path(destination) dest_path.mkdir(parents=True, exist_ok=True) file_path = dest_path / fname title = record.get('title', '') - message = f'Tutorial #{id}' + message = f"Tutorial '{resource_id}'" if title: message += f': {title}' @@ -670,13 +1074,13 @@ def download_tutorial( if file_path.exists(): if not overwrite: console.print( - f"✅ Tutorial #{id} already present at '{display_path(file_path)}'. " - 'Keeping existing.' + f"✅ Tutorial '{resource_id}' already present at " + f"'{display_path(file_path)}'. Keeping existing." ) return str(file_path) log.debug( - f"Tutorial #{id} already present at '{display_path(file_path)}', " - 'but will be overwritten.' + f"Tutorial '{resource_id}' already present at " + f"'{display_path(file_path)}', but will be overwritten." ) file_path.unlink() @@ -684,7 +1088,7 @@ def download_tutorial( with _safe_urlopen(url) as resp: file_path.write_bytes(resp.read()) - console.print(f"✅ Tutorial #{id} downloaded to '{display_path(file_path)}'") + console.print(f"✅ Tutorial '{resource_id}' downloaded to '{display_path(file_path)}'") return str(file_path) @@ -717,20 +1121,20 @@ def download_all_tutorials( console.print('❌ No tutorials available to download.') return [] - version = _get_version_for_url() + version = package_version('easydiffraction') or _get_version_for_url() console.print(f'📥 Downloading all tutorials for easydiffraction v{version}...') downloaded_paths = [] - for tutorial_id in sorted(index.keys(), key=lambda x: int(x) if x.isdigit() else x): + for tutorial_id in _ordered_keys(index): try: path = download_tutorial( - id=tutorial_id, + tutorial_id, destination=destination, overwrite=overwrite, ) downloaded_paths.append(path) except (OSError, ValueError) as e: - log.warning(f'Failed to download tutorial #{tutorial_id}: {e}') + log.warning(f"Failed to download tutorial '{tutorial_id}': {e}") resolved_destination = resolve_artifact_path(destination) console.print( @@ -898,12 +1302,12 @@ def render_object_help(obj: object) -> None: def render_cif(cif_text: str) -> None: """ - Display CIF text as a formatted table in Jupyter or terminal. + Display Edi-format text as a formatted table in Jupyter or terminal. Parameters ---------- cif_text : str - The CIF text to display. + The Edi-format text to display. """ # Split into lines lines: list[str] = list(cif_text.splitlines()) @@ -911,9 +1315,9 @@ def render_cif(cif_text: str) -> None: # Convert each line into a single-column format for table rendering columns: list[list[str]] = [[line] for line in lines] - # Render the table using left alignment and no headers + # Render the table; the single column is the Edi-format text. render_table( - columns_headers=['CIF'], + columns_headers=['Edi'], columns_alignment=['left'], columns_data=columns, ) diff --git a/tests/benchmarks/test_calculate_pattern_benchmark.py b/tests/benchmarks/test_calculate_pattern_benchmark.py index 30c1eb52d..6408321a1 100644 --- a/tests/benchmarks/test_calculate_pattern_benchmark.py +++ b/tests/benchmarks/test_calculate_pattern_benchmark.py @@ -13,10 +13,10 @@ import pytest -# (id, structure download id, experiment download id, engine) +# (id, structure download slug, experiment download slug, engine) SCENARIOS = [ - ('neut-cwl-pd-cryspy', 1, 2, 'cryspy'), - ('neut-cwl-pd-crysfml', 1, 2, 'crysfml'), + ('neut-cwl-pd-cryspy', 'struct-lbco', 'expt-lbco-hrpt', 'cryspy'), + ('neut-cwl-pd-crysfml', 'struct-lbco', 'expt-lbco-hrpt', 'crysfml'), ] @@ -28,12 +28,12 @@ ) def test_calculate_pattern_benchmark(benchmark, label, structure_id, experiment_id, engine): """Benchmark a single pattern calculation for one experiment x engine.""" - import easydiffraction as ed + import easydiffraction as edi from easydiffraction.analysis.fit_helpers.metrics import get_reliability_inputs - project = ed.Project() - project.structures.add_from_cif_path(ed.download_data(id=structure_id, destination='data')) - project.experiments.add_from_cif_path(ed.download_data(id=experiment_id, destination='data')) + project = edi.Project() + project.structures.add_from_cif_path(edi.download_data(structure_id, destination='data')) + project.experiments.add_from_edi_path(edi.download_data(experiment_id, destination='data')) experiment = project.experiments['hrpt'] experiment.calculator.type = engine diff --git a/tests/functional/test_adp_switching.py b/tests/functional/test_adp_switching.py index e4536762f..82bcab0df 100644 --- a/tests/functional/test_adp_switching.py +++ b/tests/functional/test_adp_switching.py @@ -25,7 +25,7 @@ def _make_project_with_structure(): s.space_group.name_h_m = 'P m -3 m' s.cell.length_a = 3.89 s.atom_sites.create( - label='A', + id='A', type_symbol='La', fract_x=0, fract_y=0, @@ -34,7 +34,7 @@ def _make_project_with_structure(): adp_iso=0.5, ) s.atom_sites.create( - label='B', + id='B', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -217,26 +217,30 @@ class TestCifNamesByAdpType: def test_biso_uses_b_names(self): project = _make_project_with_structure() s = project.structures['cubic'] + # A freshly created site is type-neutral (``_atom_site.adp_iso``); + # committing the (default) Biso type resolves the strict IUCr name. + s.atom_sites['A'].adp_type = 'Biso' + s._update_categories() site = s.atom_sites['A'] - assert '_atom_site.B_iso_or_equiv' in site._adp_iso._cif_handler.names[0] + assert '_atom_site.B_iso_or_equiv' in site._adp_iso._tags.cif_names[0] def test_uiso_uses_u_names(self): project = _make_project_with_structure() s = project.structures['cubic'] s.atom_sites['A'].adp_type = 'Uiso' site = s.atom_sites['A'] - assert '_atom_site.U_iso_or_equiv' in site._adp_iso._cif_handler.names[0] + assert '_atom_site.U_iso_or_equiv' in site._adp_iso._tags.cif_names[0] def test_bani_uses_b_aniso_names(self): project = _make_project_with_structure() s = project.structures['cubic'] s.atom_sites['A'].adp_type = 'Bani' aniso = s.atom_site_aniso['A'] - assert aniso._adp_11._cif_handler.names[0] == '_atom_site_aniso.B_11' + assert aniso._adp_11._tags.cif_names[0] == '_atom_site_aniso.B_11' def test_uani_uses_u_aniso_names(self): project = _make_project_with_structure() s = project.structures['cubic'] s.atom_sites['A'].adp_type = 'Uani' aniso = s.atom_site_aniso['A'] - assert aniso._adp_11._cif_handler.names[0] == '_atom_site_aniso.U_11' + assert aniso._adp_11._tags.cif_names[0] == '_atom_site_aniso.U_11' diff --git a/tests/functional/test_project_lifecycle.py b/tests/functional/test_project_lifecycle.py index 850f040e9..73f89cf6a 100644 --- a/tests/functional/test_project_lifecycle.py +++ b/tests/functional/test_project_lifecycle.py @@ -60,7 +60,7 @@ def test_save_creates_directory_structure(self, tmp_path): Project._loading = False project.save_as(str(tmp_path / 'proj')) - assert (tmp_path / 'proj' / 'project.cif').is_file() + assert (tmp_path / 'proj' / 'project.edi').is_file() assert (tmp_path / 'proj' / 'structures').is_dir() assert (tmp_path / 'proj' / 'experiments').is_dir() assert (tmp_path / 'proj' / 'analysis').is_dir() diff --git a/tests/functional/test_structure_workflow.py b/tests/functional/test_structure_workflow.py index f2e4a9193..13cd8693a 100644 --- a/tests/functional/test_structure_workflow.py +++ b/tests/functional/test_structure_workflow.py @@ -71,7 +71,7 @@ def test_create_atom_site(self): project.structures.create(name='test') s = project.structures['test'] s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -81,12 +81,12 @@ def test_create_atom_site(self): ) assert len(s.atom_sites) == 1 - def test_access_atom_site_by_label(self): + def test_access_atom_site_by_id(self): project = _make_project() project.structures.create(name='test') s = project.structures['test'] s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -103,7 +103,7 @@ def test_atom_site_fract_is_fittable(self): project.structures.create(name='test') s = project.structures['test'] s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0.1, fract_y=0.2, @@ -119,7 +119,7 @@ def test_multiple_atom_sites(self): project.structures.create(name='test') s = project.structures['test'] s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -128,7 +128,7 @@ def test_multiple_atom_sites(self): adp_iso=0.5, ) s.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0.5, fract_y=0.5, @@ -148,7 +148,7 @@ def test_special_position_fract_cannot_be_freed(self, monkeypatch): s = project.structures['test'] s.space_group.name_h_m = 'P m -3 m' s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -196,7 +196,7 @@ def test_general_position_remains_refinable(self): s = project.structures['test'] # Default space group is P 1 -- general position 'a' s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0.1, fract_y=0.2, diff --git a/tests/functional/test_wyckoff_tutorial_corpus.py b/tests/functional/test_wyckoff_tutorial_corpus.py index 08306c269..e4c5c7523 100644 --- a/tests/functional/test_wyckoff_tutorial_corpus.py +++ b/tests/functional/test_wyckoff_tutorial_corpus.py @@ -99,7 +99,7 @@ def _string_assignment(tree, attr_name): def _wyckoff_declarations(tree): - """Yield ``(label, letter, (x, y, z))`` for create() calls with a letter.""" + """Yield ``(atom_id, letter, (x, y, z))`` for create() calls with a letter.""" for node in ast.walk(tree): if not (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)): continue @@ -117,11 +117,11 @@ def _wyckoff_declarations(tree): float(kwargs.get('fract_y', 0.0)), float(kwargs.get('fract_z', 0.0)), ) - yield kwargs.get('label', '?'), kwargs['wyckoff_letter'], coords + yield kwargs.get('id', '?'), kwargs['wyckoff_letter'], coords def _corpus(): - """Collect ``(tutorial, name_hm, code, label, letter, coords)`` rows.""" + """Collect ``(tutorial, name_hm, code, atom_id, letter, coords)`` rows.""" rows = [] for path in sorted(_TUTORIALS_DIR.glob('*.py')): tree = ast.parse(path.read_text(encoding='utf-8')) @@ -130,9 +130,9 @@ def _corpus(): # No structure, or several structures we cannot unambiguously # associate with create() calls: skip this tutorial. continue - code = _string_assignment(tree, 'it_coordinate_system_code') - for label, letter, coords in _wyckoff_declarations(tree): - rows.append((path.name, name_hm, code, label, letter, coords)) + code = _string_assignment(tree, 'coord_system_code') + for atom_id, letter, coords in _wyckoff_declarations(tree): + rows.append((path.name, name_hm, code, atom_id, letter, coords)) return rows @@ -143,11 +143,11 @@ def test_tutorial_declared_letters_are_reproduced_by_detection(): assert rows, 'no tutorial Wyckoff-letter declarations were found' mismatches = [] - for tutorial, name_hm, code, label, letter, coords in rows: + for tutorial, name_hm, code, atom_id, letter, coords in rows: detected = ecr.detect_wyckoff_position(name_hm, code, coords) if detected is None or detected.letter != letter: found = None if detected is None else detected.letter - mismatches.append((tutorial, label, name_hm, coords, letter, found)) + mismatches.append((tutorial, atom_id, name_hm, coords, letter, found)) assert not mismatches, mismatches diff --git a/tests/integration/fitting/conftest.py b/tests/integration/fitting/conftest.py index afc1026a7..d5a7dbaf5 100644 --- a/tests/integration/fitting/conftest.py +++ b/tests/integration/fitting/conftest.py @@ -22,7 +22,7 @@ def lbco_fitted_project(): model.space_group.name_h_m = 'P m -3 m' model.cell.length_a = 3.88 model.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -32,7 +32,7 @@ def lbco_fitted_project(): adp_iso=0.1, ) model.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -42,7 +42,7 @@ def lbco_fitted_project(): adp_iso=0.1, ) model.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -51,7 +51,7 @@ def lbco_fitted_project(): adp_iso=0.1, ) model.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -60,7 +60,7 @@ def lbco_fitted_project(): adp_iso=0.1, ) - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path(name='hrpt', data_path=data_path) expt.instrument.setup_wavelength = 1.494 expt.instrument.calib_twotheta_offset = 0 @@ -69,9 +69,9 @@ def lbco_fitted_project(): expt.peak.broad_gauss_w = 0.2 expt.peak.broad_lorentz_x = 0 expt.peak.broad_lorentz_y = 0 - expt.linked_phases.create(id='lbco', scale=5.0) - expt.background.create(id='1', x=10, y=170) - expt.background.create(id='2', x=165, y=170) + expt.linked_structures.create(structure_id='lbco', scale=5.0) + expt.background.create(id='1', position=10, intensity=170) + expt.background.create(id='2', position=165, intensity=170) project = Project() project.structures.add(model) @@ -79,10 +79,10 @@ def lbco_fitted_project(): project.analysis.minimizer.type = 'lmfit' model.cell.length_a.free = True - expt.linked_phases['lbco'].scale.free = True + expt.linked_structures['lbco'].scale.free = True expt.instrument.calib_twotheta_offset.free = True - expt.background['1'].y.free = True - expt.background['2'].y.free = True + expt.background['1'].intensity.free = True + expt.background['2'].intensity.free = True project.verbosity = 'silent' project.analysis.fit() diff --git a/tests/integration/fitting/test_analysis_display.py b/tests/integration/fitting/test_analysis_display.py index f749bd1a8..8828bd5f4 100644 --- a/tests/integration/fitting/test_analysis_display.py +++ b/tests/integration/fitting/test_analysis_display.py @@ -24,9 +24,19 @@ def test_display_how_to_access_parameters(lbco_fitted_project): project.display.parameters.access() -def test_display_parameter_cif_uids(lbco_fitted_project): +def test_display_parameter_uids(lbco_fitted_project): project = lbco_fitted_project - project.display.parameters.cif_uids() + project.display.parameters.uid() + + +def test_display_parameter_edi_tags(lbco_fitted_project): + project = lbco_fitted_project + project.display.parameters.edi() + + +def test_display_parameter_cif_tags(lbco_fitted_project): + project = lbco_fitted_project + project.display.parameters.cif() def test_display_constraints_empty(lbco_fitted_project): @@ -40,9 +50,9 @@ def test_display_fit_results(lbco_fitted_project): project.display.fit.results() -def test_display_as_cif(lbco_fitted_project): +def test_display_as_text(lbco_fitted_project): project = lbco_fitted_project - project.analysis.show_as_cif() + project.analysis.show_as_text() def test_analysis_as_cif(lbco_fitted_project): diff --git a/tests/integration/fitting/test_aniso_adp_fitting.py b/tests/integration/fitting/test_aniso_adp_fitting.py index fd3bb0542..bbd25cf7d 100644 --- a/tests/integration/fitting/test_aniso_adp_fitting.py +++ b/tests/integration/fitting/test_aniso_adp_fitting.py @@ -4,19 +4,19 @@ import tempfile -import easydiffraction as ed +import easydiffraction as edi TEMP_DIR = tempfile.gettempdir() def _setup_tbti_project(): """Create a Tb2Ti2O7 single-crystal project ready for fitting.""" - project = ed.Project() + project = edi.Project() - model_path = ed.download_data(id=20, destination=TEMP_DIR) + model_path = edi.download_data('struct-tbti', destination=TEMP_DIR) project.structures.add_from_cif_path(model_path) - data_path = ed.download_data(id=19, destination=TEMP_DIR) + data_path = edi.download_data('meas-tbti-heidi', destination=TEMP_DIR) project.experiments.add_from_data_path( name='heidi', data_path=data_path, @@ -25,8 +25,8 @@ def _setup_tbti_project(): radiation_probe='neutron', ) experiment = project.experiments['heidi'] - experiment.linked_crystal.id = 'tbti' - experiment.linked_crystal.scale = 1.0 + experiment.linked_structure.structure_id = 'tbti' + experiment.linked_structure.scale = 1.0 experiment.instrument.setup_wavelength = 0.793 experiment.extinction.mosaicity = 35000 experiment.extinction.radius = 10 @@ -52,7 +52,7 @@ def test_iso_then_aniso_fit() -> None: s.atom_sites['O2'].occupancy.free = True for name in ('Tb', 'Ti', 'O1', 'O2'): s.atom_sites[name].adp_iso.free = True - e.linked_crystal.scale.free = True + e.linked_structure.scale.free = True e.extinction.radius.free = True # Fit isotropic diff --git a/tests/integration/fitting/test_bayesian_dream.py b/tests/integration/fitting/test_bayesian_dream.py index 31200246b..7387cd1a9 100644 --- a/tests/integration/fitting/test_bayesian_dream.py +++ b/tests/integration/fitting/test_bayesian_dream.py @@ -21,7 +21,7 @@ def _create_lbco_project() -> Project: model.space_group.name_h_m = 'P m -3 m' model.cell.length_a = 3.88 model.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -31,7 +31,7 @@ def _create_lbco_project() -> Project: adp_iso=0.1, ) model.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -41,7 +41,7 @@ def _create_lbco_project() -> Project: adp_iso=0.1, ) model.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -50,7 +50,7 @@ def _create_lbco_project() -> Project: adp_iso=0.1, ) model.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -59,7 +59,7 @@ def _create_lbco_project() -> Project: adp_iso=0.1, ) - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) experiment = ExperimentFactory.from_data_path(name='hrpt', data_path=data_path) experiment.instrument.setup_wavelength = 1.494 experiment.instrument.calib_twotheta_offset = 0.0 @@ -68,9 +68,9 @@ def _create_lbco_project() -> Project: experiment.peak.broad_gauss_w = 0.2 experiment.peak.broad_lorentz_x = 0.0 experiment.peak.broad_lorentz_y = 0.0 - experiment.linked_phases.create(id='lbco', scale=5.0) - experiment.background.create(id='1', x=10, y=170) - experiment.background.create(id='2', x=165, y=170) + experiment.linked_structures.create(structure_id='lbco', scale=5.0) + experiment.background.create(id='1', position=10, intensity=170) + experiment.background.create(id='2', position=165, intensity=170) project = Project(name='lbco_bayesian') project.structures.add(model) @@ -83,7 +83,7 @@ def _dream_parameters(project: Project) -> tuple[object, object, object]: experiment = project.experiments['hrpt'] return ( structure.cell.length_a, - experiment.linked_phases['lbco'].scale, + experiment.linked_structures['lbco'].scale, experiment.instrument.calib_twotheta_offset, ) @@ -185,9 +185,9 @@ def test_bayesian_fit_results_reload_from_persisted_fit_state(tmp_path): proj_dir = tmp_path / 'dream_project' project.save_as(str(proj_dir)) - analysis_cif = proj_dir / 'analysis' / 'analysis.cif' + analysis_edi = proj_dir / 'analysis' / 'analysis.edi' results_sidecar = proj_dir / 'analysis' / 'results.h5' - assert analysis_cif.is_file() + assert analysis_edi.is_file() assert results_sidecar.is_file() loaded = Project.load(str(proj_dir)) diff --git a/tests/integration/fitting/test_calculate-without-measured-data.py b/tests/integration/fitting/test_calculate-without-measured-data.py index ebfa95154..2579f8cff 100644 --- a/tests/integration/fitting/test_calculate-without-measured-data.py +++ b/tests/integration/fitting/test_calculate-without-measured-data.py @@ -25,7 +25,7 @@ def _lbco_structure(): model.space_group.name_h_m = 'P m -3 m' model.cell.length_a = 3.88 model.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -35,7 +35,7 @@ def _lbco_structure(): adp_iso=0.1, ) model.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -45,7 +45,7 @@ def _lbco_structure(): adp_iso=0.1, ) model.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -54,7 +54,7 @@ def _lbco_structure(): adp_iso=0.1, ) model.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -69,10 +69,10 @@ def _si_structure(): """Build the silicon structure model (used for the TOF cases).""" model = StructureFactory.from_scratch(name='si') model.space_group.name_h_m = 'F d -3 m' - model.space_group.it_coordinate_system_code = '2' + model.space_group.coord_system_code = '2' model.cell.length_a = 5.4315 model.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.125, fract_y=0.125, @@ -99,12 +99,12 @@ def _cwl_calc_only_project(): experiment.peak.broad_gauss_u = 0.1 experiment.peak.broad_gauss_v = -0.1 experiment.peak.broad_gauss_w = 0.2 - experiment.background.create(id='1', x=10, y=20) - experiment.background.create(id='2', x=60, y=20) + experiment.background.create(id='1', position=10, intensity=20) + experiment.background.create(id='2', position=60, intensity=20) experiment.data_range.two_theta_min = 20.0 experiment.data_range.two_theta_max = 60.0 experiment.data_range.two_theta_inc = 0.1 - experiment.linked_phases.create(id='lbco', scale=10.0) + experiment.linked_structures.create(structure_id='lbco', scale=10.0) return project @@ -123,21 +123,21 @@ def _tof_calc_only_project(): experiment.instrument.setup_twotheta_bank = 144.845 experiment.instrument.calib_d_to_tof_offset = 0.0 experiment.instrument.calib_d_to_tof_linear = 7476.91 - experiment.instrument.calib_d_to_tof_quad = -1.54 + experiment.instrument.calib_d_to_tof_quadratic = -1.54 experiment.peak.broad_gauss_sigma_0 = 3.0 experiment.peak.broad_gauss_sigma_1 = 40.0 experiment.peak.broad_gauss_sigma_2 = 2.0 - experiment.peak.exp_decay_beta_0 = 0.04221 - experiment.peak.exp_decay_beta_1 = 0.00946 - experiment.peak.exp_rise_alpha_0 = 0.0 - experiment.peak.exp_rise_alpha_1 = 0.5971 + experiment.peak.decay_beta_0 = 0.04221 + experiment.peak.decay_beta_1 = 0.00946 + experiment.peak.rise_alpha_0 = 0.0 + experiment.peak.rise_alpha_1 = 0.5971 experiment.background.type = 'line-segment' - experiment.background.create(id='1', x=5000, y=20) - experiment.background.create(id='2', x=15000, y=20) + experiment.background.create(id='1', position=5000, intensity=20) + experiment.background.create(id='2', position=15000, intensity=20) experiment.data_range.time_of_flight_min = 5000.0 experiment.data_range.time_of_flight_max = 15000.0 experiment.data_range.time_of_flight_inc = 5.0 - experiment.linked_phases.create(id='si', scale=10.0) + experiment.linked_structures.create(structure_id='si', scale=10.0) return project diff --git a/tests/integration/fitting/test_cif_round_trip.py b/tests/integration/fitting/test_cif_round_trip.py index 17a5bff81..96e88d57c 100644 --- a/tests/integration/fitting/test_cif_round_trip.py +++ b/tests/integration/fitting/test_cif_round_trip.py @@ -28,7 +28,7 @@ def _build_fully_configured_experiment() -> ExperimentFactory: ExperimentBase A complete experiment ready for CIF round-trip testing. """ - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='hrpt', data_path=data_path, @@ -45,23 +45,23 @@ def _build_fully_configured_experiment() -> ExperimentFactory: expt.peak.broad_lorentz_y = 0.0797 # Background - expt.background.create(id='1', x=10, y=170) - expt.background.create(id='2', x=80, y=160) - expt.background.create(id='3', x=165, y=170) + expt.background.create(id='1', position=10, intensity=170) + expt.background.create(id='2', position=80, intensity=160) + expt.background.create(id='3', position=165, intensity=170) # Excluded regions expt.excluded_regions.create(id='1', start=0, end=5) expt.excluded_regions.create(id='2', start=165, end=180) - # Linked phases - expt.linked_phases.create(id='lbco', scale=9.0) + # Linked structures + expt.linked_structures.create(structure_id='lbco', scale=9.0) # Free parameters expt.instrument.calib_twotheta_offset.free = True - expt.linked_phases['lbco'].scale.free = True - expt.background['1'].y.free = True - expt.background['2'].y.free = True - expt.background['3'].y.free = True + expt.linked_structures['lbco'].scale.free = True + expt.background['1'].intensity.free = True + expt.background['2'].intensity.free = True + expt.background['3'].intensity.free = True return expt @@ -71,7 +71,7 @@ def _collect_param_values(expt: object) -> dict[str, object]: Collect all parameter values from an experiment. Returns a dict keyed by unique_name with the parameter value. - Skips raw data parameters (pd_data.*) since those are large arrays. + Skips raw data parameters (data.*) since those are large arrays. """ result = {} for p in expt.parameters: @@ -79,7 +79,7 @@ def _collect_param_values(expt: object) -> dict[str, object]: if uname is None: continue # Skip raw data arrays - if 'pd_data.' in uname: + if '.data.' in uname: continue result[uname] = p.value return result @@ -90,7 +90,7 @@ def _collect_free_flags(expt: object) -> dict[str, bool]: return { p.unique_name: p.free for p in expt.parameters - if isinstance(p, Parameter) and not p.unique_name.startswith('pd_data.') + if isinstance(p, Parameter) and '.data.' not in p.unique_name } @@ -189,11 +189,11 @@ def test_experiment_cif_round_trip_preserves_categories() -> None: f'got {len(loaded.excluded_regions)}' ) - # Linked phases - assert len(loaded.linked_phases) == len(original.linked_phases), ( - f'Linked phases count mismatch: ' - f'expected {len(original.linked_phases)}, ' - f'got {len(loaded.linked_phases)}' + # Linked structures + assert len(loaded.linked_structures) == len(original.linked_structures), ( + f'Linked structures count mismatch: ' + f'expected {len(original.linked_structures)}, ' + f'got {len(loaded.linked_structures)}' ) @@ -258,7 +258,7 @@ def test_structure_cif_round_trip_preserves_parameters() -> None: original.space_group.name_h_m = 'P m -3 m' original.cell.length_a = 3.8909 original.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -268,7 +268,7 @@ def test_structure_cif_round_trip_preserves_parameters() -> None: adp_iso=0.5, ) original.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -277,7 +277,7 @@ def test_structure_cif_round_trip_preserves_parameters() -> None: adp_iso=0.5, ) original.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, diff --git a/tests/integration/fitting/test_cli_entrypoints.py b/tests/integration/fitting/test_cli_entrypoints.py index 34cc85ad8..9074e5317 100644 --- a/tests/integration/fitting/test_cli_entrypoints.py +++ b/tests/integration/fitting/test_cli_entrypoints.py @@ -5,11 +5,12 @@ from typer.testing import CliRunner +import easydiffraction as edi + runner = CliRunner() def test_cli_version_invokes_show_version(monkeypatch): - import easydiffraction as ed import easydiffraction.__main__ as main_mod called = {'ok': False} @@ -18,7 +19,7 @@ def fake_show_version() -> None: print('VERSION_OK') called['ok'] = True - monkeypatch.setattr(ed, 'show_version', fake_show_version) + monkeypatch.setattr(edi, 'show_version', fake_show_version) result = runner.invoke(main_mod.app, ['--version']) @@ -37,20 +38,21 @@ def test_cli_help_shows_and_exits_zero(): def test_cli_subcommands_call_utils(monkeypatch): - import easydiffraction as ed import easydiffraction.__main__ as main_mod calls: list[str] = [] - monkeypatch.setattr(ed, 'list_tutorials', lambda: calls.append('LIST')) + monkeypatch.setattr(edi, 'list_tutorials', lambda: calls.append('LIST')) monkeypatch.setattr( - ed, + edi, 'download_all_tutorials', lambda destination='tutorials', overwrite=False: calls.append('DOWNLOAD_ALL'), ) monkeypatch.setattr( - ed, + edi, 'download_tutorial', - lambda id, destination='tutorials', overwrite=False: calls.append(f'DOWNLOAD_{id}'), + lambda id, destination='tutorials', file_format='ipynb', overwrite=False: calls.append( + f'DOWNLOAD_{id}' + ), ) list_result = runner.invoke(main_mod.app, ['list-tutorials']) @@ -69,14 +71,14 @@ def test_cli_fit_loads_and_fits(monkeypatch, tmp_path): calls: list[str] = [] - class FakeInfo: + class FakeMetadata: _path = '/some/path' class FakeExperiment: name = 'exp1' class FakeProject: - info = FakeInfo() + metadata = FakeMetadata() experiments = [FakeExperiment()] class _analysis: @@ -114,7 +116,7 @@ def pattern(expt_name: str, **kwargs) -> None: project_dir = tmp_path / 'proj' project_dir.mkdir() - (project_dir / 'project.cif').write_text('_project.id test\n') + (project_dir / 'project.edi').write_text('_metadata.name test\n') monkeypatch.setattr(Project, 'load', staticmethod(lambda path: fake_project)) @@ -128,14 +130,14 @@ def test_cli_fit_dry_clears_path(monkeypatch, tmp_path): import easydiffraction.__main__ as main_mod from easydiffraction.project.project import Project - class FakeInfo: + class FakeMetadata: _path = '/some/path' class FakeExperiment: name = 'exp1' class FakeProject: - info = FakeInfo() + metadata = FakeMetadata() experiments = [FakeExperiment()] class _analysis: @@ -172,11 +174,11 @@ def pattern(expt_name: str, **kwargs) -> None: project_dir = tmp_path / 'proj' project_dir.mkdir() - (project_dir / 'project.cif').write_text('_project.id test\n') + (project_dir / 'project.edi').write_text('_metadata.name test\n') monkeypatch.setattr(Project, 'load', staticmethod(lambda path: fake_project)) result = runner.invoke(main_mod.app, ['fit', '--dry', str(project_dir)]) assert result.exit_code == 0 - assert fake_project.info._path is None + assert fake_project.metadata._path is None diff --git a/tests/integration/fitting/test_exploration_help.py b/tests/integration/fitting/test_exploration_help.py index ff777b5cb..f95bdb0c0 100644 --- a/tests/integration/fitting/test_exploration_help.py +++ b/tests/integration/fitting/test_exploration_help.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause -"""Integration tests for help(), show_as_cif(), and switchable-category show methods.""" +"""Integration tests for help(), show_as_text(), and switchable-category show methods.""" def test_project_str(lbco_fitted_project): @@ -28,10 +28,10 @@ def test_structure_help(lbco_fitted_project): model.help() -def test_structure_show_as_cif(lbco_fitted_project): +def test_structure_show_as_text(lbco_fitted_project): project = lbco_fitted_project model = project.structures['lbco'] - model.show_as_cif() + model.show_as_text() def test_structure_as_cif(lbco_fitted_project): @@ -59,13 +59,13 @@ def test_experiment_help(lbco_fitted_project): expt.help() -def test_experiment_show_as_cif(lbco_fitted_project): +def test_experiment_show_as_text(lbco_fitted_project): project = lbco_fitted_project expt = project.experiments['hrpt'] - expt.show_as_cif() + expt.show_as_text() -def test_experiment_show_as_cif_omits_empty_category_gaps(lbco_fitted_project, monkeypatch): +def test_experiment_show_as_text_omits_empty_category_gaps(lbco_fitted_project, monkeypatch): import re import easydiffraction.datablocks.experiment.item.base as experiment_base @@ -79,10 +79,12 @@ def fake_render_cif(cif_text): project = lbco_fitted_project expt = project.experiments['hrpt'] - expt.show_as_cif() + expt.show_as_text() cif_text = captured['cif_text'] - assert re.search(r'_pd_phase_block\.scale\n[^\n]+\n\n_background\.type', cif_text) is not None + assert ( + re.search(r'_linked_structure\.scale\n[^\n]+\n\n_background\.type', cif_text) is not None + ) assert re.search(r'_background\.type [^\n]+\n\nloop_', cif_text) is not None assert '\n\n\n' not in cif_text @@ -106,8 +108,8 @@ def test_experiment_switchable_category_types(lbco_fitted_project): # Peak profile expt.peak.show_supported() assert isinstance(expt.peak.type, str) - # Linked phases - assert expt.linked_phases is not None + # Linked structures + assert expt.linked_structures is not None # Calculator expt.calculator.show_supported() assert isinstance(expt.calculator.type, str) @@ -139,7 +141,7 @@ def test_structure_atom_sites_iteration(lbco_fitted_project): model = project.structures['lbco'] count = 0 for site in model.atom_sites: - assert site.label.value is not None + assert site.id.value is not None assert site.type_symbol.value is not None count += 1 assert count == 4 diff --git a/tests/integration/fitting/test_multi.py b/tests/integration/fitting/test_multi.py index af6e56aa0..8aaa7c22f 100644 --- a/tests/integration/fitting/test_multi.py +++ b/tests/integration/fitting/test_multi.py @@ -5,7 +5,7 @@ from numpy.testing import assert_almost_equal -import easydiffraction as ed +import easydiffraction as edi from easydiffraction import ExperimentFactory from easydiffraction import Project from easydiffraction import StructureFactory @@ -18,10 +18,10 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: # Set structures model_1 = StructureFactory.from_scratch(name='lbco') model_1.space_group.name_h_m = 'P m -3 m' - model_1.space_group.it_coordinate_system_code = '1' + model_1.space_group.coord_system_code = '1' model_1.cell.length_a = 3.8909 model_1.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -31,7 +31,7 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: occupancy=0.5, ) model_1.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -41,7 +41,7 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: occupancy=0.5, ) model_1.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -50,7 +50,7 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: adp_iso=0.2567, ) model_1.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -61,10 +61,10 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: model_2 = StructureFactory.from_scratch(name='si') model_2.space_group.name_h_m = 'F d -3 m' - model_2.space_group.it_coordinate_system_code = '2' + model_2.space_group.coord_system_code = '2' model_2.cell.length_a = 5.43146 model_2.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.0, fract_y=0.0, @@ -74,7 +74,7 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: ) # Set experiment - data_path = download_data(id=8, destination=TEMP_DIR) + data_path = download_data('meas-lbco-si-mcstas', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='mcstas', data_path=data_path, @@ -83,19 +83,19 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: expt.instrument.setup_twotheta_bank = 94.90931761529106 expt.instrument.calib_d_to_tof_offset = 0.0 expt.instrument.calib_d_to_tof_linear = 58724.76869981215 - expt.instrument.calib_d_to_tof_quad = -0.00001 + expt.instrument.calib_d_to_tof_quadratic = -0.00001 expt.peak.type = 'jorgensen' expt.peak.broad_gauss_sigma_0 = 45137 expt.peak.broad_gauss_sigma_1 = -52394 expt.peak.broad_gauss_sigma_2 = 22998 - expt.peak.exp_decay_beta_0 = 0.0055 - expt.peak.exp_decay_beta_1 = 0.0041 - expt.peak.exp_rise_alpha_0 = 0.0 - expt.peak.exp_rise_alpha_1 = 0.0097 - expt.linked_phases.create(id='lbco', scale=4.0) - expt.linked_phases.create(id='si', scale=0.2) + expt.peak.decay_beta_0 = 0.0055 + expt.peak.decay_beta_1 = 0.0041 + expt.peak.rise_alpha_0 = 0.0 + expt.peak.rise_alpha_1 = 0.0097 + expt.linked_structures.create(structure_id='lbco', scale=4.0) + expt.linked_structures.create(structure_id='si', scale=0.2) for x in range(45000, 115000, 5000): - expt.background.create(id=str(x), x=x, y=0.2) + expt.background.create(id=str(x), position=x, intensity=0.2) # Create project project = Project() @@ -117,16 +117,16 @@ def test_single_fit_neutron_pd_tof_mcstas_lbco_si() -> None: model_1.atom_sites['O'].adp_iso.free = True model_2.cell.length_a.free = True model_2.atom_sites['Si'].adp_iso.free = True - expt.linked_phases['lbco'].scale.free = True - expt.linked_phases['si'].scale.free = True + expt.linked_structures['lbco'].scale.free = True + expt.linked_structures['si'].scale.free = True expt.peak.broad_gauss_sigma_0.free = True expt.peak.broad_gauss_sigma_1.free = True expt.peak.broad_gauss_sigma_2.free = True - expt.peak.exp_rise_alpha_1.free = True - expt.peak.exp_decay_beta_0.free = True - expt.peak.exp_decay_beta_1.free = True + expt.peak.rise_alpha_1.free = True + expt.peak.decay_beta_0.free = True + expt.peak.decay_beta_1.free = True for point in expt.background: - point.y.free = True + point.intensity.free = True # Perform fit project.analysis.fit() @@ -143,10 +143,10 @@ def _test_joint_fit_bragg_pdf_neutron_pd_tof_si() -> None: # Set structure (shared between Bragg and PDF experiments) model = StructureFactory.from_scratch(name='si') model.space_group.name_h_m = 'F d -3 m' - model.space_group.it_coordinate_system_code = '2' + model.space_group.coord_system_code = '2' model.cell.length_a = 5.431 model.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.125, fract_y=0.125, @@ -155,7 +155,7 @@ def _test_joint_fit_bragg_pdf_neutron_pd_tof_si() -> None: ) # Set Bragg experiment (SEPD, TOF) - bragg_data_path = download_data(id=7, destination=TEMP_DIR) + bragg_data_path = download_data('meas-si-sepd', destination=TEMP_DIR) bragg_expt = ExperimentFactory.from_data_path( name='sepd', data_path=bragg_data_path, @@ -164,21 +164,21 @@ def _test_joint_fit_bragg_pdf_neutron_pd_tof_si() -> None: bragg_expt.instrument.setup_twotheta_bank = 144.845 bragg_expt.instrument.calib_d_to_tof_offset = 0.0 bragg_expt.instrument.calib_d_to_tof_linear = 7476.91 - bragg_expt.instrument.calib_d_to_tof_quad = -1.54 + bragg_expt.instrument.calib_d_to_tof_quadratic = -1.54 bragg_expt.peak.type = 'jorgensen' bragg_expt.peak.broad_gauss_sigma_0 = 3.0 bragg_expt.peak.broad_gauss_sigma_1 = 40.0 bragg_expt.peak.broad_gauss_sigma_2 = 2.0 - bragg_expt.peak.exp_decay_beta_0 = 0.04221 - bragg_expt.peak.exp_decay_beta_1 = 0.00946 - bragg_expt.peak.exp_rise_alpha_0 = 0.0 - bragg_expt.peak.exp_rise_alpha_1 = 0.5971 - bragg_expt.linked_phases.create(id='si', scale=10.0) + bragg_expt.peak.decay_beta_0 = 0.04221 + bragg_expt.peak.decay_beta_1 = 0.00946 + bragg_expt.peak.rise_alpha_0 = 0.0 + bragg_expt.peak.rise_alpha_1 = 0.5971 + bragg_expt.linked_structures.create(structure_id='si', scale=10.0) for x in range(0, 35000, 5000): - bragg_expt.background.create(id=str(x), x=x, y=200) + bragg_expt.background.create(id=str(x), position=x, intensity=200) # Set PDF experiment (NOMAD, TOF) - pdf_data_path = ed.download_data(id=5, destination=TEMP_DIR) + pdf_data_path = edi.download_data('meas-si-pdf-nomad', destination=TEMP_DIR) pdf_expt = ExperimentFactory.from_data_path( name='nomad', data_path=pdf_data_path, @@ -191,7 +191,7 @@ def _test_joint_fit_bragg_pdf_neutron_pd_tof_si() -> None: pdf_expt.peak.sharp_delta_1 = 0.0 pdf_expt.peak.sharp_delta_2 = 4.0 pdf_expt.peak.damp_particle_diameter = 0 - pdf_expt.linked_phases.create(id='si', scale=1.0) + pdf_expt.linked_structures.create(structure_id='si', scale=1.0) # Create project project = Project() @@ -208,13 +208,13 @@ def _test_joint_fit_bragg_pdf_neutron_pd_tof_si() -> None: model.atom_sites['Si'].adp_iso.free = True # Select fitting parameters — Bragg experiment - bragg_expt.linked_phases['si'].scale.free = True + bragg_expt.linked_structures['si'].scale.free = True bragg_expt.instrument.calib_d_to_tof_offset.free = True for point in bragg_expt.background: - point.y.free = True + point.intensity.free = True # Select fitting parameters — PDF experiment - pdf_expt.linked_phases['si'].scale.free = True + pdf_expt.linked_structures['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 diff --git a/tests/integration/fitting/test_pair-distribution-function.py b/tests/integration/fitting/test_pair-distribution-function.py index d6551d3d5..6d80db3b4 100644 --- a/tests/integration/fitting/test_pair-distribution-function.py +++ b/tests/integration/fitting/test_pair-distribution-function.py @@ -5,22 +5,22 @@ from numpy.testing import assert_almost_equal -import easydiffraction as ed +import easydiffraction as edi TEMP_DIR = tempfile.gettempdir() def test_single_fit_pdf_xray_pd_cw_nacl() -> None: - project = ed.Project() + project = edi.Project() # Set structure project.structures.create(name='nacl') structure = project.structures['nacl'] structure.space_group.name_h_m = 'F m -3 m' - structure.space_group.it_coordinate_system_code = '1' + structure.space_group.coord_system_code = '1' structure.cell.length_a = 5.6018 structure.atom_sites.create( - label='Na', + id='Na', type_symbol='Na', fract_x=0, fract_y=0, @@ -29,7 +29,7 @@ def test_single_fit_pdf_xray_pd_cw_nacl() -> None: adp_iso=1.1053, ) structure.atom_sites.create( - label='Cl', + id='Cl', type_symbol='Cl', fract_x=0.5, fract_y=0.5, @@ -39,7 +39,7 @@ def test_single_fit_pdf_xray_pd_cw_nacl() -> None: ) # Set experiment - data_path = ed.download_data(id=4, destination=TEMP_DIR) + data_path = edi.download_data('meas-nacl-pdf', destination=TEMP_DIR) project.experiments.add_from_data_path( name='xray_pdf', data_path=data_path, @@ -56,13 +56,13 @@ def test_single_fit_pdf_xray_pd_cw_nacl() -> None: experiment.peak.sharp_delta_1 = 0 experiment.peak.sharp_delta_2 = 3.5041 experiment.peak.damp_particle_diameter = 0 - experiment.linked_phases.create(id='nacl', scale=0.4254) + experiment.linked_structures.create(structure_id='nacl', scale=0.4254) # Select fitting parameters structure.cell.length_a.free = True structure.atom_sites['Na'].adp_iso.free = True structure.atom_sites['Cl'].adp_iso.free = True - experiment.linked_phases['nacl'].scale.free = True + experiment.linked_structures['nacl'].scale.free = True experiment.peak.damp_q.free = True experiment.peak.sharp_delta_2.free = True @@ -75,16 +75,16 @@ def test_single_fit_pdf_xray_pd_cw_nacl() -> None: def test_single_fit_pdf_neutron_pd_cw_ni(): - project = ed.Project() + project = edi.Project() # Set structure project.structures.create(name='ni') structure = project.structures['ni'] structure.space_group.name_h_m.value = 'F m -3 m' - structure.space_group.it_coordinate_system_code = '1' + structure.space_group.coord_system_code = '1' structure.cell.length_a = 3.526 structure.atom_sites.create( - label='Ni', + id='Ni', type_symbol='Ni', fract_x=0, fract_y=0, @@ -94,7 +94,7 @@ def test_single_fit_pdf_neutron_pd_cw_ni(): ) # Set experiment - data_path = ed.download_data(id=6, destination=TEMP_DIR) + data_path = edi.download_data('meas-ni-pdf', destination=TEMP_DIR) project.experiments.add_from_data_path( name='pdf', data_path=data_path, @@ -110,12 +110,12 @@ def test_single_fit_pdf_neutron_pd_cw_ni(): experiment.peak.sharp_delta_1 = 0 experiment.peak.sharp_delta_2 = 2.5587 experiment.peak.damp_particle_diameter = 0 - experiment.linked_phases.create(id='ni', scale=0.9892) + experiment.linked_structures.create(structure_id='ni', scale=0.9892) # Select fitting parameters structure.cell.length_a.free = True structure.atom_sites['Ni'].adp_iso.free = True - experiment.linked_phases['ni'].scale.free = True + experiment.linked_structures['ni'].scale.free = True experiment.peak.broad_q.free = True experiment.peak.sharp_delta_2.free = True @@ -128,16 +128,16 @@ def test_single_fit_pdf_neutron_pd_cw_ni(): def test_single_fit_pdf_neutron_pd_tof_si(): - project = ed.Project() + project = edi.Project() # Set structure project.structures.create(name='si') structure = project.structures['si'] structure.space_group.name_h_m.value = 'F d -3 m' - structure.space_group.it_coordinate_system_code = '1' + structure.space_group.coord_system_code = '1' structure.cell.length_a = 5.4306 structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0, fract_y=0, @@ -147,7 +147,7 @@ def test_single_fit_pdf_neutron_pd_tof_si(): ) # Set experiment - data_path = ed.download_data(id=5, destination=TEMP_DIR) + data_path = edi.download_data('meas-si-pdf-nomad', destination=TEMP_DIR) project.experiments.add_from_data_path( name='nomad', data_path=data_path, @@ -163,12 +163,12 @@ def test_single_fit_pdf_neutron_pd_tof_si(): experiment.peak.sharp_delta_1 = 2.54 experiment.peak.sharp_delta_2 = -1.7525 experiment.peak.damp_particle_diameter = 0 - experiment.linked_phases.create(id='si', scale=1.2728) + experiment.linked_structures.create(structure_id='si', scale=1.2728) # Select fitting parameters project.structures['si'].cell.length_a.free = True project.structures['si'].atom_sites['Si'].adp_iso.free = True - experiment.linked_phases['si'].scale.free = True + experiment.linked_structures['si'].scale.free = True experiment.peak.damp_q.free = True experiment.peak.broad_q.free = True experiment.peak.sharp_delta_1.free = True diff --git a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py index a689c950a..ca80a49cd 100644 --- a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py +++ b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py @@ -19,7 +19,7 @@ def test_single_fit_neutron_pd_cwl_lbco() -> None: model.space_group.name_h_m = 'P m -3 m' model.cell.length_a = 3.88 model.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -29,7 +29,7 @@ def test_single_fit_neutron_pd_cwl_lbco() -> None: adp_iso=0.1, ) model.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -39,7 +39,7 @@ def test_single_fit_neutron_pd_cwl_lbco() -> None: adp_iso=0.1, ) model.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -48,7 +48,7 @@ def test_single_fit_neutron_pd_cwl_lbco() -> None: adp_iso=0.1, ) model.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -58,7 +58,7 @@ def test_single_fit_neutron_pd_cwl_lbco() -> None: ) # Set experiment - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='hrpt', @@ -74,10 +74,10 @@ def test_single_fit_neutron_pd_cwl_lbco() -> None: expt.peak.broad_lorentz_x = 0 expt.peak.broad_lorentz_y = 0 - expt.linked_phases.create(id='lbco', scale=5.0) + expt.linked_structures.create(structure_id='lbco', scale=5.0) - expt.background.create(id='1', x=10, y=170) - expt.background.create(id='2', x=165, y=170) + expt.background.create(id='1', position=10, intensity=170) + expt.background.create(id='2', position=165, intensity=170) # Create project project = Project() @@ -91,10 +91,10 @@ def test_single_fit_neutron_pd_cwl_lbco() -> None: # Select fitting parameters model.cell.length_a.free = True - expt.linked_phases['lbco'].scale.free = True + expt.linked_structures['lbco'].scale.free = True expt.instrument.calib_twotheta_offset.free = True - expt.background['1'].y.free = True - expt.background['2'].y.free = True + expt.background['1'].intensity.free = True + expt.background['2'].intensity.free = True # Perform fit project.analysis.fit() @@ -155,7 +155,7 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: atom_sites = model.atom_sites atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -165,7 +165,7 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: occupancy=0.5, ) atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -175,7 +175,7 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: occupancy=0.5, ) atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -184,7 +184,7 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: adp_iso=1.0, ) atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -194,7 +194,7 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: ) # Set experiment - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='hrpt', @@ -213,18 +213,18 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: peak.broad_lorentz_y = 0.0797 background = expt.background - background.create(id='10', x=10, y=174.3) - background.create(id='20', x=20, y=159.8) - background.create(id='30', x=30, y=167.9) - background.create(id='50', x=50, y=166.1) - background.create(id='70', x=70, y=172.3) - background.create(id='90', x=90, y=171.1) - background.create(id='110', x=110, y=172.4) - background.create(id='130', x=130, y=182.5) - background.create(id='150', x=150, y=173.0) - background.create(id='165', x=165, y=171.1) - - expt.linked_phases.create(id='lbco', scale=9.0976) + background.create(id='10', position=10, intensity=174.3) + background.create(id='20', position=20, intensity=159.8) + background.create(id='30', position=30, intensity=167.9) + background.create(id='50', position=50, intensity=166.1) + background.create(id='70', position=70, intensity=172.3) + background.create(id='90', position=90, intensity=171.1) + background.create(id='110', position=110, intensity=172.4) + background.create(id='130', position=130, intensity=182.5) + background.create(id='150', position=150, intensity=173.0) + background.create(id='165', position=165, intensity=171.1) + + expt.linked_structures.create(structure_id='lbco', scale=9.0976) # Create project project = Project() @@ -274,19 +274,19 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: # Set aliases for parameters project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=atom_sites['Ba'].adp_iso, ) project.analysis.aliases.create( - label='occ_La', + id='occ_La', param=atom_sites['La'].occupancy, ) project.analysis.aliases.create( - label='occ_Ba', + id='occ_Ba', param=atom_sites['Ba'].occupancy, ) @@ -317,11 +317,11 @@ def test_fit_neutron_pd_cwl_hs() -> None: # Set structure model = StructureFactory.from_scratch(name='hs') model.space_group.name_h_m = 'R -3 m' - model.space_group.it_coordinate_system_code = 'h' + model.space_group.coord_system_code = 'h' model.cell.length_a = 6.8615 model.cell.length_c = 14.136 model.atom_sites.create( - label='Zn', + id='Zn', type_symbol='Zn', fract_x=0, fract_y=0, @@ -330,7 +330,7 @@ def test_fit_neutron_pd_cwl_hs() -> None: adp_iso=0.1, ) model.atom_sites.create( - label='Cu', + id='Cu', type_symbol='Cu', fract_x=0.5, fract_y=0, @@ -339,7 +339,7 @@ def test_fit_neutron_pd_cwl_hs() -> None: adp_iso=1.2, ) model.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0.206, fract_y=-0.206, @@ -348,7 +348,7 @@ def test_fit_neutron_pd_cwl_hs() -> None: adp_iso=0.7, ) model.atom_sites.create( - label='Cl', + id='Cl', type_symbol='Cl', fract_x=0, fract_y=0, @@ -357,7 +357,7 @@ def test_fit_neutron_pd_cwl_hs() -> None: adp_iso=1.1, ) model.atom_sites.create( - label='H', + id='H', type_symbol='2H', fract_x=0.132, fract_y=-0.132, @@ -367,7 +367,7 @@ def test_fit_neutron_pd_cwl_hs() -> None: ) # Set experiment - data_path = download_data(id=11, destination=TEMP_DIR) + data_path = download_data('meas-hs-hrpt', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path(name='hrpt', data_path=data_path) @@ -380,17 +380,17 @@ def test_fit_neutron_pd_cwl_hs() -> None: expt.peak.broad_lorentz_x = 0.2927 expt.peak.broad_lorentz_y = 0 - expt.background.create(id='1', x=4.4196, y=648.413) - expt.background.create(id='2', x=6.6207, y=523.788) - expt.background.create(id='3', x=10.4918, y=454.938) - expt.background.create(id='4', x=15.4634, y=435.913) - expt.background.create(id='5', x=45.6041, y=472.972) - expt.background.create(id='6', x=74.6844, y=486.606) - expt.background.create(id='7', x=103.4187, y=472.409) - expt.background.create(id='8', x=121.6311, y=496.734) - expt.background.create(id='9', x=159.4116, y=473.146) + expt.background.create(id='1', position=4.4196, intensity=648.413) + expt.background.create(id='2', position=6.6207, intensity=523.788) + expt.background.create(id='3', position=10.4918, intensity=454.938) + expt.background.create(id='4', position=15.4634, intensity=435.913) + expt.background.create(id='5', position=45.6041, intensity=472.972) + expt.background.create(id='6', position=74.6844, intensity=486.606) + expt.background.create(id='7', position=103.4187, intensity=472.409) + expt.background.create(id='8', position=121.6311, intensity=496.734) + expt.background.create(id='9', position=159.4116, intensity=473.146) - expt.linked_phases.create(id='hs', scale=0.492) + expt.linked_structures.create(structure_id='hs', scale=0.492) # Create project project = Project() @@ -405,7 +405,7 @@ def test_fit_neutron_pd_cwl_hs() -> None: # Select fitting parameters model.cell.length_a.free = True model.cell.length_c.free = True - expt.linked_phases['hs'].scale.free = True + expt.linked_structures['hs'].scale.free = True expt.instrument.calib_twotheta_offset.free = True # Perform fit @@ -426,7 +426,7 @@ def test_fit_neutron_pd_cwl_hs() -> None: expt.peak.broad_gauss_w.free = True expt.peak.broad_lorentz_x.free = True for point in expt.background: - point.y.free = True + point.intensity.free = True # Perform fit project.analysis.fit() @@ -492,29 +492,29 @@ def test_fit_neutron_pd_cwl_hs() -> None: def test_single_fit_neutron_pd_cwl_lbco_with_constraints_from_project(tmp_path) -> None: - import easydiffraction as ed + import easydiffraction as edi # Create a project from CIF files - project = ed.Project() - project.structures.add_from_cif_path(ed.download_data(id=1, destination='data')) - project.experiments.add_from_cif_path(ed.download_data(id=2, destination='data')) + project = edi.Project() + project.structures.add_from_cif_path(edi.download_data('struct-lbco', destination='data')) + project.experiments.add_from_edi_path(edi.download_data('expt-lbco-hrpt', destination='data')) # Set constraints project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=project.structures['lbco'].atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=project.structures['lbco'].atom_sites['Ba'].adp_iso, ) project.analysis.aliases.create( - label='occ_La', + id='occ_La', param=project.structures['lbco'].atom_sites['La'].occupancy, ) project.analysis.aliases.create( - label='occ_Ba', + id='occ_Ba', param=project.structures['lbco'].atom_sites['Ba'].occupancy, ) @@ -529,7 +529,7 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints_from_project(tmp_path) project.save_as(proj_dir) # Load Project from Directory - project = ed.Project.load(proj_dir) + project = edi.Project.load(proj_dir) # Perform Analysis project.analysis.fit() diff --git a/tests/integration/fitting/test_powder-diffraction_joint-fit.py b/tests/integration/fitting/test_powder-diffraction_joint-fit.py index 9ce18c48e..2aab7b30b 100644 --- a/tests/integration/fitting/test_powder-diffraction_joint-fit.py +++ b/tests/integration/fitting/test_powder-diffraction_joint-fit.py @@ -21,7 +21,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: model.cell.length_b = 5.39 model.cell.length_c = 6.95 model.atom_sites.create( - label='Pb', + id='Pb', type_symbol='Pb', fract_x=0.1876, fract_y=0.25, @@ -30,7 +30,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: adp_iso=1.37, ) model.atom_sites.create( - label='S', + id='S', type_symbol='S', fract_x=0.0654, fract_y=0.25, @@ -39,7 +39,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: adp_iso=0.3777, ) model.atom_sites.create( - label='O1', + id='O1', type_symbol='O', fract_x=0.9082, fract_y=0.25, @@ -48,7 +48,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: adp_iso=1.9764, ) model.atom_sites.create( - label='O2', + id='O2', type_symbol='O', fract_x=0.1935, fract_y=0.25, @@ -57,7 +57,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: adp_iso=1.4456, ) model.atom_sites.create( - label='O3', + id='O3', type_symbol='O', fract_x=0.0811, fract_y=0.0272, @@ -67,7 +67,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: ) # Set experiments - data_path = download_data(id=14, destination=TEMP_DIR) + data_path = download_data('meas-pbso4-d1a-part1', destination=TEMP_DIR) expt1 = ExperimentFactory.from_data_path(name='npd1', data_path=data_path) expt1.instrument.setup_wavelength = 1.91 expt1.instrument.calib_twotheta_offset = -0.1406 @@ -76,7 +76,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: expt1.peak.broad_gauss_w = 0.386 expt1.peak.broad_lorentz_x = 0 expt1.peak.broad_lorentz_y = 0.0878 - expt1.linked_phases.create(id='pbso4', scale=1.46) + expt1.linked_structures.create(structure_id='pbso4', scale=1.46) expt1.background.type = 'line-segment' for id, x, y in [ ('1', 11.0, 206.1624), @@ -88,9 +88,9 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: ('7', 120.0, 244.4525), ('8', 153.0, 226.0595), ]: - expt1.background.create(id=id, x=x, y=y) + expt1.background.create(id=id, position=x, intensity=y) - data_path = download_data(id=15, destination=TEMP_DIR) + data_path = download_data('meas-pbso4-d1a-part2', destination=TEMP_DIR) expt2 = ExperimentFactory.from_data_path(name='npd2', data_path=data_path) expt2.instrument.setup_wavelength = 1.91 expt2.instrument.calib_twotheta_offset = -0.1406 @@ -99,7 +99,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: expt2.peak.broad_gauss_w = 0.386 expt2.peak.broad_lorentz_x = 0 expt2.peak.broad_lorentz_y = 0.0878 - expt2.linked_phases.create(id='pbso4', scale=1.46) + expt2.linked_structures.create(structure_id='pbso4', scale=1.46) expt2.background.type = 'line-segment' for id, x, y in [ ('1', 11.0, 206.1624), @@ -111,7 +111,7 @@ def test_joint_fit_split_dataset_neutron_pd_cwl_pbso4() -> None: ('7', 120.0, 244.4525), ('8', 153.0, 226.0595), ]: - expt2.background.create(id=id, x=x, y=y) + expt2.background.create(id=id, position=x, intensity=y) # Create project project = Project() @@ -147,7 +147,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: model.cell.length_b = 5.39 model.cell.length_c = 6.95 model.atom_sites.create( - label='Pb', + id='Pb', type_symbol='Pb', fract_x=0.1876, fract_y=0.25, @@ -156,7 +156,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: adp_iso=1.37, ) model.atom_sites.create( - label='S', + id='S', type_symbol='S', fract_x=0.0654, fract_y=0.25, @@ -165,7 +165,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: adp_iso=0.3777, ) model.atom_sites.create( - label='O1', + id='O1', type_symbol='O', fract_x=0.9082, fract_y=0.25, @@ -174,7 +174,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: adp_iso=1.9764, ) model.atom_sites.create( - label='O2', + id='O2', type_symbol='O', fract_x=0.1935, fract_y=0.25, @@ -183,7 +183,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: adp_iso=1.4456, ) model.atom_sites.create( - label='O3', + id='O3', type_symbol='O', fract_x=0.0811, fract_y=0.0272, @@ -193,7 +193,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: ) # Set experiments - data_path = download_data(id=13, destination=TEMP_DIR) + data_path = download_data('meas-pbso4-d1a', destination=TEMP_DIR) expt1 = ExperimentFactory.from_data_path( name='npd', data_path=data_path, @@ -206,7 +206,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: expt1.peak.broad_gauss_w = 0.386 expt1.peak.broad_lorentz_x = 0 expt1.peak.broad_lorentz_y = 0.088 - expt1.linked_phases.create(id='pbso4', scale=1.5) + expt1.linked_structures.create(structure_id='pbso4', scale=1.5) for id, x, y in [ ('1', 11.0, 206.1624), ('2', 15.0, 194.75), @@ -217,9 +217,9 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: ('7', 120.0, 244.4525), ('8', 153.0, 226.0595), ]: - expt1.background.create(id=id, x=x, y=y) + expt1.background.create(id=id, position=x, intensity=y) - data_path = download_data(id=16, destination=TEMP_DIR) + data_path = download_data('meas-pbso4-xray', destination=TEMP_DIR) expt2 = ExperimentFactory.from_data_path( name='xrd', data_path=data_path, @@ -232,7 +232,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: expt2.peak.broad_gauss_w = 0.021272 expt2.peak.broad_lorentz_x = 0 expt2.peak.broad_lorentz_y = 0.057691 - expt2.linked_phases.create(id='pbso4', scale=0.001) + expt2.linked_structures.create(structure_id='pbso4', scale=0.001) for id, x, y in [ ('1', 11.0, 141.8516), ('2', 13.0, 102.8838), @@ -243,7 +243,7 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: ('7', 90.0, 113.7473), ('8', 110.0, 132.4643), ]: - expt2.background.create(id=id, x=x, y=y) + expt2.background.create(id=id, position=x, intensity=y) # Create project project = Project() @@ -258,8 +258,8 @@ def test_joint_fit_neutron_xray_pd_cwl_pbso4() -> None: model.cell.length_a.free = True model.cell.length_b.free = True model.cell.length_c.free = True - expt1.linked_phases['pbso4'].scale.free = True - expt2.linked_phases['pbso4'].scale.free = True + expt1.linked_structures['pbso4'].scale.free = True + expt2.linked_structures['pbso4'].scale.free = True # ------------ 1st fitting ------------ diff --git a/tests/integration/fitting/test_powder-diffraction_time-of-flight.py b/tests/integration/fitting/test_powder-diffraction_time-of-flight.py index fffc54400..df71bd751 100644 --- a/tests/integration/fitting/test_powder-diffraction_time-of-flight.py +++ b/tests/integration/fitting/test_powder-diffraction_time-of-flight.py @@ -17,10 +17,10 @@ def test_single_fit_neutron_pd_tof_si() -> None: # Set structure model = StructureFactory.from_scratch(name='si') model.space_group.name_h_m = 'F d -3 m' - model.space_group.it_coordinate_system_code = '2' + model.space_group.coord_system_code = '2' model.cell.length_a = 5.4315 model.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', fract_x=0.125, fract_y=0.125, @@ -30,7 +30,7 @@ def test_single_fit_neutron_pd_tof_si() -> None: ) # Set experiment - data_path = download_data(id=7, destination=TEMP_DIR) + data_path = download_data('meas-si-sepd', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='sepd', data_path=data_path, @@ -39,18 +39,18 @@ def test_single_fit_neutron_pd_tof_si() -> None: expt.instrument.setup_twotheta_bank = 144.845 expt.instrument.calib_d_to_tof_offset = -9.29 expt.instrument.calib_d_to_tof_linear = 7476.91 - expt.instrument.calib_d_to_tof_quad = -1.54 + expt.instrument.calib_d_to_tof_quadratic = -1.54 expt.peak.type = 'jorgensen' expt.peak.broad_gauss_sigma_0 = 4.2 expt.peak.broad_gauss_sigma_1 = 45.8 expt.peak.broad_gauss_sigma_2 = 1.1 - expt.peak.exp_decay_beta_0 = 0.04221 - expt.peak.exp_decay_beta_1 = 0.00946 - expt.peak.exp_rise_alpha_0 = 0.0 - expt.peak.exp_rise_alpha_1 = 0.5971 - expt.linked_phases.create(id='si', scale=14.92) + expt.peak.decay_beta_0 = 0.04221 + expt.peak.decay_beta_1 = 0.00946 + expt.peak.rise_alpha_0 = 0.0 + expt.peak.rise_alpha_1 = 0.5971 + expt.linked_structures.create(structure_id='si', scale=14.92) for x in range(0, 35000, 5000): - expt.background.create(id=str(x), x=x, y=200) + expt.background.create(id=str(x), position=x, intensity=200) # Create project project = Project() @@ -63,10 +63,10 @@ def test_single_fit_neutron_pd_tof_si() -> None: # Select fitting parameters model.cell.length_a.free = True model.atom_sites['Si'].adp_iso.free = True - expt.linked_phases['si'].scale.free = True + expt.linked_structures['si'].scale.free = True expt.instrument.calib_d_to_tof_offset.free = True for point in expt.background: - point.y.free = True + point.intensity.free = True # Perform fit project.analysis.fit() @@ -83,10 +83,10 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: # Set structure model = StructureFactory.from_scratch(name='ncaf') model.space_group.name_h_m = 'I 21 3' - model.space_group.it_coordinate_system_code = '1' + model.space_group.coord_system_code = '1' model.cell.length_a = 10.250256 model.atom_sites.create( - label='Ca', + id='Ca', type_symbol='Ca', fract_x=0.4661, fract_y=0.0, @@ -95,7 +95,7 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: adp_iso=0.9, ) model.atom_sites.create( - label='Al', + id='Al', type_symbol='Al', fract_x=0.25171, fract_y=0.25171, @@ -104,7 +104,7 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: adp_iso=0.66, ) model.atom_sites.create( - label='Na', + id='Na', type_symbol='Na', fract_x=0.08481, fract_y=0.08481, @@ -113,7 +113,7 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: adp_iso=1.9, ) model.atom_sites.create( - label='F1', + id='F1', type_symbol='F', fract_x=0.1375, fract_y=0.3053, @@ -122,7 +122,7 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: adp_iso=0.9, ) model.atom_sites.create( - label='F2', + id='F2', type_symbol='F', fract_x=0.3626, fract_y=0.3634, @@ -131,7 +131,7 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: adp_iso=1.28, ) model.atom_sites.create( - label='F3', + id='F3', type_symbol='F', fract_x=0.4612, fract_y=0.4612, @@ -141,7 +141,7 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: ) # Set experiment - data_path = download_data(id=9, destination=TEMP_DIR) + data_path = download_data('meas-ncaf-wish-b56', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='wish', data_path=data_path, @@ -152,16 +152,16 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: expt.instrument.setup_twotheta_bank = 152.827 expt.instrument.calib_d_to_tof_offset = -13.7123 expt.instrument.calib_d_to_tof_linear = 20773.1 - expt.instrument.calib_d_to_tof_quad = -1.08308 + expt.instrument.calib_d_to_tof_quadratic = -1.08308 expt.peak.type = 'jorgensen' expt.peak.broad_gauss_sigma_0 = 0.0 expt.peak.broad_gauss_sigma_1 = 0.0 expt.peak.broad_gauss_sigma_2 = 15.7 - expt.peak.exp_decay_beta_0 = 0.00670 - expt.peak.exp_decay_beta_1 = 0.0099 - expt.peak.exp_rise_alpha_0 = -0.009 - expt.peak.exp_rise_alpha_1 = 0.1085 - expt.linked_phases.create(id='ncaf', scale=1.0928) + expt.peak.decay_beta_0 = 0.00670 + expt.peak.decay_beta_1 = 0.0099 + expt.peak.rise_alpha_0 = -0.009 + expt.peak.rise_alpha_1 = 0.1085 + expt.linked_structures.create(structure_id='ncaf', scale=1.0928) for x, y in [ (9162, 465), (11136, 593), @@ -192,7 +192,7 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: (91958, 268), (102712, 262), ]: - expt.background.create(id=str(x), x=x, y=y) + expt.background.create(id=str(x), position=x, intensity=y) # Create project project = Project() @@ -203,11 +203,11 @@ def test_single_fit_neutron_pd_tof_ncaf() -> None: project.analysis.minimizer.type = 'lmfit' # Select fitting parameters - expt.linked_phases['ncaf'].scale.free = True + expt.linked_structures['ncaf'].scale.free = True expt.instrument.calib_d_to_tof_offset.free = True expt.peak.broad_gauss_sigma_2.free = True - expt.peak.exp_decay_beta_1.free = True - expt.peak.exp_rise_alpha_1.free = True + expt.peak.decay_beta_1.free = True + expt.peak.rise_alpha_1.free = True # Perform fit project.analysis.fit() diff --git a/tests/integration/fitting/test_project_load.py b/tests/integration/fitting/test_project_load.py index 368a35b2b..b90b52f7a 100644 --- a/tests/integration/fitting/test_project_load.py +++ b/tests/integration/fitting/test_project_load.py @@ -34,7 +34,7 @@ def _create_lbco_project() -> Project: model.space_group.name_h_m = 'P m -3 m' model.cell.length_a = 3.8909 model.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -44,7 +44,7 @@ def _create_lbco_project() -> Project: adp_iso=0.5, ) model.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -54,7 +54,7 @@ def _create_lbco_project() -> Project: adp_iso=0.5, ) model.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -63,7 +63,7 @@ def _create_lbco_project() -> Project: adp_iso=0.5, ) model.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -73,7 +73,7 @@ def _create_lbco_project() -> Project: ) # Experiment - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='hrpt', data_path=data_path, @@ -85,9 +85,9 @@ def _create_lbco_project() -> Project: expt.peak.broad_gauss_w = 0.123 expt.peak.broad_lorentz_x = 0 expt.peak.broad_lorentz_y = 0.0797 - expt.background.create(id='1', x=10, y=170) - expt.background.create(id='2', x=165, y=170) - expt.linked_phases.create(id='lbco', scale=9.0) + expt.background.create(id='1', position=10, intensity=170) + expt.background.create(id='2', position=165, intensity=170) + expt.linked_structures.create(structure_id='lbco', scale=9.0) # Project assembly project = Project(name='lbco_project') @@ -96,18 +96,18 @@ def _create_lbco_project() -> Project: # Free parameters model.cell.length_a.free = True - expt.linked_phases['lbco'].scale.free = True + expt.linked_structures['lbco'].scale.free = True expt.instrument.calib_twotheta_offset.free = True - expt.background['1'].y.free = True - expt.background['2'].y.free = True + expt.background['1'].intensity.free = True + expt.background['2'].intensity.free = True # Aliases and constraints project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=model.atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=model.atom_sites['Ba'].adp_iso, ) project.analysis.constraints.create(expression='biso_Ba = biso_La') @@ -117,11 +117,7 @@ def _create_lbco_project() -> Project: def _collect_param_snapshot(project: Project) -> dict[str, float]: """Return ``{unique_name: value}`` for model parameters (excluding raw data).""" - return { - p.unique_name: p.value - for p in project.parameters - if not p.unique_name.startswith('pd_data.') - } + return {p.unique_name: p.value for p in project.parameters if '.data.' not in p.unique_name} def _collect_free_flags(project: Project) -> dict[str, bool]: @@ -157,9 +153,9 @@ def test_save_load_round_trip_preserves_parameters(tmp_path) -> None: # Load loaded = Project.load(proj_dir) - # Compare project info + # Compare project metadata assert loaded.name == original.name - assert loaded.info.title == original.info.title + assert loaded.metadata.title == original.metadata.title # Compare structures assert loaded.structures.names == original.structures.names @@ -197,10 +193,10 @@ def test_save_load_round_trip_preserves_parameters(tmp_path) -> None: # Compare aliases assert len(loaded.analysis.aliases) == len(original.analysis.aliases) for orig_alias in original.analysis.aliases: - label = orig_alias.label.value - loaded_alias = loaded.analysis.aliases[label] - assert loaded_alias.param_unique_name.value == orig_alias.param_unique_name.value - assert loaded_alias.param is not None, f"Alias '{label}' param reference not resolved" + alias_id = orig_alias.id.value + loaded_alias = loaded.analysis.aliases[alias_id] + assert loaded_alias.parameter_unique_name.value == orig_alias.parameter_unique_name.value + assert loaded_alias.param is not None, f"Alias '{alias_id}' param reference not resolved" # Compare constraints assert len(loaded.analysis.constraints) == len(original.analysis.constraints) diff --git a/tests/integration/fitting/test_sequential.py b/tests/integration/fitting/test_sequential.py index 37d5bd26a..95cce8696 100644 --- a/tests/integration/fitting/test_sequential.py +++ b/tests/integration/fitting/test_sequential.py @@ -34,7 +34,7 @@ def _create_sequential_project( model.space_group.name_h_m = 'P m -3 m' model.cell.length_a = 3.8909 model.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -44,7 +44,7 @@ def _create_sequential_project( adp_iso=0.5, ) model.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -54,7 +54,7 @@ def _create_sequential_project( adp_iso=0.5, ) model.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -63,7 +63,7 @@ def _create_sequential_project( adp_iso=0.5, ) model.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -73,7 +73,7 @@ def _create_sequential_project( ) # Experiment (template) - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) expt = ExperimentFactory.from_data_path( name='template', data_path=data_path, @@ -85,9 +85,9 @@ def _create_sequential_project( expt.peak.broad_gauss_w = 0.123 expt.peak.broad_lorentz_x = 0 expt.peak.broad_lorentz_y = 0.0797 - expt.background.create(id='1', x=10, y=170) - expt.background.create(id='2', x=165, y=170) - expt.linked_phases.create(id='lbco', scale=9.0) + expt.background.create(id='1', position=10, intensity=170) + expt.background.create(id='2', position=165, intensity=170) + expt.linked_structures.create(structure_id='lbco', scale=9.0) # Project assembly project = Project(name='seq_test') @@ -96,10 +96,10 @@ def _create_sequential_project( # Free parameters model.cell.length_a.free = True - expt.linked_phases['lbco'].scale.free = True + expt.linked_structures['lbco'].scale.free = True expt.instrument.calib_twotheta_offset.free = True - expt.background['1'].y.free = True - expt.background['2'].y.free = True + expt.background['1'].intensity.free = True + expt.background['2'].intensity.free = True # Initial fit on the template project.verbosity = 'silent' @@ -156,7 +156,7 @@ def test_fit_sequential_produces_csv(tmp_path) -> None: _run_sequential_fit(project, data_dir) - csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path = project.metadata.path / 'analysis' / 'results.csv' assert csv_path.is_file(), 'results.csv was not created' with csv_path.open() as f: @@ -195,7 +195,7 @@ def test_fit_sequential_crash_recovery(tmp_path) -> None: # First run: fit all 3 files _run_sequential_fit(project, data_dir) - csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path = project.metadata.path / 'analysis' / 'results.csv' with csv_path.open() as f: rows_first = list(csv.DictReader(f)) assert len(rows_first) == 3 @@ -220,7 +220,7 @@ def test_fit_sequential_parameter_propagation(tmp_path) -> None: _run_sequential_fit(project, data_dir) - csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path = project.metadata.path / 'analysis' / 'results.csv' with csv_path.open() as f: rows = list(csv.DictReader(f)) @@ -249,7 +249,7 @@ def test_fit_sequential_with_diffrn_extract_rules(tmp_path) -> None: _run_sequential_fit(project, data_dir) - csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path = project.metadata.path / 'analysis' / 'results.csv' with csv_path.open() as f: rows = list(csv.DictReader(f)) @@ -268,14 +268,14 @@ def test_fit_sequential_with_diffrn_extract_rules(tmp_path) -> None: def test_fit_sequential_requires_saved_project(tmp_path) -> None: """fit_sequential raises if project hasn't been saved.""" - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) model = StructureFactory.from_scratch(name='s') expt = ExperimentFactory.from_data_path( name='e', data_path=data_path, ) - expt.linked_phases.create(id='s', scale=1.0) - expt.linked_phases['s'].scale.free = True + expt.linked_structures.create(structure_id='s', scale=1.0) + expt.linked_structures['s'].scale.free = True project = Project(name='unsaved') project.structures.add(model) project.experiments.add(expt) @@ -315,7 +315,7 @@ def test_fit_sequential_parallel(tmp_path) -> None: _run_sequential_fit(project, data_dir, max_workers=2) - csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path = project.metadata.path / 'analysis' / 'results.csv' assert csv_path.is_file(), 'results.csv was not created' with csv_path.open() as f: @@ -355,7 +355,7 @@ def test_apply_params_from_csv_loads_data_and_params(tmp_path) -> None: _run_sequential_fit(project, data_dir) - csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path = project.metadata.path / 'analysis' / 'results.csv' with csv_path.open() as f: rows = list(csv.DictReader(f)) diff --git a/tests/integration/fitting/test_single-crystal-diffraction.py b/tests/integration/fitting/test_single-crystal-diffraction.py index 6879f2e9f..5ee87335f 100644 --- a/tests/integration/fitting/test_single-crystal-diffraction.py +++ b/tests/integration/fitting/test_single-crystal-diffraction.py @@ -5,20 +5,20 @@ import pytest -import easydiffraction as ed +import easydiffraction as edi TEMP_DIR = tempfile.gettempdir() def test_single_fit_neut_sc_cwl_tbti() -> None: - project = ed.Project() + project = edi.Project() # Set structure - model_path = ed.download_data(id=20, destination=TEMP_DIR) + model_path = edi.download_data('struct-tbti', destination=TEMP_DIR) project.structures.add_from_cif_path(model_path) # Set experiment - data_path = ed.download_data(id=19, destination=TEMP_DIR) + data_path = edi.download_data('meas-tbti-heidi', destination=TEMP_DIR) project.experiments.add_from_data_path( name='heidi', data_path=data_path, @@ -28,15 +28,15 @@ def test_single_fit_neut_sc_cwl_tbti() -> None: scattering_type='bragg', ) experiment = project.experiments['heidi'] - experiment.linked_crystal.id = 'tbti' - experiment.linked_crystal.scale = 3 + experiment.linked_structure.structure_id = 'tbti' + experiment.linked_structure.scale = 3 experiment.instrument.setup_wavelength = 0.793 experiment.extinction.mosaicity = 29820 experiment.extinction.radius = 27 # Select fitting parameters (experiment only) # Structure parameters are selected in the loaded CIF file - experiment.linked_crystal.scale.free = True + experiment.linked_structure.scale.free = True experiment.extinction.radius.free = True # Perform fit @@ -48,14 +48,14 @@ def test_single_fit_neut_sc_cwl_tbti() -> None: def test_single_fit_neut_sc_tof_taurine() -> None: - project = ed.Project() + project = edi.Project() # Set structure - model_path = ed.download_data(id=21, destination=TEMP_DIR) + model_path = edi.download_data('struct-taurine', destination=TEMP_DIR) project.structures.add_from_cif_path(model_path) # Set experiment - data_path = ed.download_data(id=22, destination=TEMP_DIR) + data_path = edi.download_data('meas-taurine-senju', destination=TEMP_DIR) project.experiments.add_from_data_path( name='senju', data_path=data_path, @@ -65,14 +65,14 @@ def test_single_fit_neut_sc_tof_taurine() -> None: scattering_type='bragg', ) experiment = project.experiments['senju'] - experiment.linked_crystal.id = 'taurine' - experiment.linked_crystal.scale = 1.4 + experiment.linked_structure.structure_id = 'taurine' + experiment.linked_structure.scale = 1.4 experiment.extinction.mosaicity = 1000.0 experiment.extinction.radius = 2.0 # Select fitting parameters (experiment only) # Structure parameters are selected in the loaded CIF file - experiment.linked_crystal.scale.free = True + experiment.linked_structure.scale.free = True experiment.extinction.radius.free = True # Perform fit diff --git a/tests/integration/fitting/test_switch-calculator.py b/tests/integration/fitting/test_switch-calculator.py index 31c9d91ed..154a9c5d9 100644 --- a/tests/integration/fitting/test_switch-calculator.py +++ b/tests/integration/fitting/test_switch-calculator.py @@ -9,7 +9,7 @@ def test_neutron_pd_cwl_lbco_crysfml(tmp_path) -> None: - import easydiffraction as ed + import easydiffraction as edi from easydiffraction.analysis.calculators.crysfml import CrysfmlCalculator # Fail clearly if the crysfml backend is not importable, rather than @@ -17,26 +17,26 @@ def test_neutron_pd_cwl_lbco_crysfml(tmp_path) -> None: assert CrysfmlCalculator.engine_imported is True # Create a project from CIF files - project = ed.Project() - project.structures.add_from_cif_path(ed.download_data(id=1, destination='data')) - project.experiments.add_from_cif_path(ed.download_data(id=2, destination='data')) + project = edi.Project() + project.structures.add_from_cif_path(edi.download_data('struct-lbco', destination='data')) + project.experiments.add_from_edi_path(edi.download_data('expt-lbco-hrpt', destination='data')) # Set constraints project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=project.structures['lbco'].atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=project.structures['lbco'].atom_sites['Ba'].adp_iso, ) project.analysis.aliases.create( - label='occ_La', + id='occ_La', param=project.structures['lbco'].atom_sites['La'].occupancy, ) project.analysis.aliases.create( - label='occ_Ba', + id='occ_Ba', param=project.structures['lbco'].atom_sites['Ba'].occupancy, ) @@ -51,7 +51,7 @@ def test_neutron_pd_cwl_lbco_crysfml(tmp_path) -> None: project.save_as(proj_dir) # Load Project from Directory - project = ed.Project.load(proj_dir) + project = edi.Project.load(proj_dir) # Change calculator project.experiments['hrpt'].calculator.type = 'crysfml' diff --git a/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py b/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py index 27d5a3e90..66f80ed90 100644 --- a/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py +++ b/tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py @@ -17,7 +17,7 @@ import pytest -import easydiffraction as ed +import easydiffraction as edi # CIF experiment type tags required by easydiffraction to identify # the experiment configuration (powder TOF neutron diffraction) @@ -55,23 +55,23 @@ def prepared_cif_path( @pytest.fixture(scope='module') def project_with_data( prepared_cif_path: str, -) -> ed.Project: +) -> edi.Project: """Create project with structure, experiment data, and configuration.""" # Step 1: Define Project - project = ed.Project() + project = edi.Project() # Step 2: Define Structure manually project.structures.create(name='diamond') structure = project.structures['diamond'] structure.space_group.name_h_m = 'F d -3 m' - structure.space_group.it_coordinate_system_code = '1' + structure.space_group.coord_system_code = '1' structure.cell.length_a = 3.567 structure.atom_sites.create( - label='C', + id='C', type_symbol='C', fract_x=0.125, fract_y=0.125, @@ -85,8 +85,8 @@ def project_with_data( experiment = project.experiments['reduced_tof'] # Step 4: Configure experiment - # Link phase - experiment.linked_phases.create(id='diamond', scale=0.8) + # Link structure + experiment.linked_structures.create(structure_id='diamond', scale=0.8) # Instrument setup experiment.instrument.setup_twotheta_bank = 90.0 @@ -96,10 +96,10 @@ def project_with_data( experiment.peak.broad_gauss_sigma_0 = 48500.0 experiment.peak.broad_gauss_sigma_1 = 3000.0 experiment.peak.broad_gauss_sigma_2 = 0.0 - experiment.peak.exp_decay_beta_0 = 0.05 - experiment.peak.exp_decay_beta_1 = 0.0 - experiment.peak.exp_rise_alpha_0 = 0.0 - experiment.peak.exp_rise_alpha_1 = 0.26 + experiment.peak.decay_beta_0 = 0.05 + experiment.peak.decay_beta_1 = 0.0 + experiment.peak.rise_alpha_0 = 0.0 + experiment.peak.rise_alpha_1 = 0.26 # Excluded regions experiment.excluded_regions.create(id='1', start=0, end=10000) @@ -116,16 +116,16 @@ def project_with_data( ('8', 61000, 0.7), ('9', 70000, 0.6), ] - for id_, x, y in background_points: - experiment.background.create(id=id_, x=x, y=y) + for id_, position, intensity in background_points: + experiment.background.create(id=id_, position=position, intensity=intensity) return project @pytest.fixture(scope='module') def fitted_project( - project_with_data: ed.Project, -) -> ed.Project: + project_with_data: edi.Project, +) -> edi.Project: """Perform fit and return project with results.""" project = project_with_data structure = project.structures['diamond'] @@ -136,15 +136,15 @@ def fitted_project( structure.atom_sites['C'].adp_iso.free = True # Set free parameters for experiment - experiment.linked_phases['diamond'].scale.free = True + experiment.linked_structures['diamond'].scale.free = True experiment.instrument.calib_d_to_tof_linear.free = True experiment.peak.broad_gauss_sigma_0.free = True experiment.peak.broad_gauss_sigma_1.free = True - experiment.peak.exp_decay_beta_0.free = True + experiment.peak.decay_beta_0.free = True for point in experiment.background: - point.y.free = True + point.intensity.free = True # Step 6: Do fitting project.analysis.fit() @@ -156,14 +156,14 @@ def fitted_project( def test_analyze_reduced_data__load_cif( - project_with_data: ed.Project, + project_with_data: edi.Project, ) -> None: """Verify CIF data loads into project correctly.""" assert 'reduced_tof' in project_with_data.experiments.names def test_analyze_reduced_data__data_size( - project_with_data: ed.Project, + project_with_data: edi.Project, ) -> None: """Verify loaded data has expected size.""" experiment = project_with_data.experiments['reduced_tof'] @@ -175,15 +175,15 @@ def test_analyze_reduced_data__data_size( def test_analyze_reduced_data__phase_linked( - project_with_data: ed.Project, + project_with_data: edi.Project, ) -> None: """Verify phase is correctly linked to experiment.""" experiment = project_with_data.experiments['reduced_tof'] - assert 'diamond' in experiment.linked_phases.names + assert 'diamond' in experiment.linked_structures.names def test_analyze_reduced_data__background_set( - project_with_data: ed.Project, + project_with_data: edi.Project, ) -> None: """Verify background points are configured.""" experiment = project_with_data.experiments['reduced_tof'] @@ -194,7 +194,7 @@ def test_analyze_reduced_data__background_set( def test_analyze_reduced_data__fit_quality( - fitted_project: ed.Project, + fitted_project: edi.Project, ) -> None: """Verify fit quality is reasonable (chi-square value).""" chi_square = fitted_project.analysis.fit_results.reduced_chi_square diff --git a/tests/integration/workflows/test_background_auto_estimate_corpus.py b/tests/integration/workflows/test_background_auto_estimate_corpus.py index eca5e3d6b..323fb2e9d 100644 --- a/tests/integration/workflows/test_background_auto_estimate_corpus.py +++ b/tests/integration/workflows/test_background_auto_estimate_corpus.py @@ -21,7 +21,7 @@ import numpy as np -import easydiffraction as ed +import easydiffraction as edi # The estimated background may differ from the coarse hand-placed # reference by at most these fractions of the measured signal scale @@ -33,8 +33,8 @@ def _assert_tracks_reference(tmp_path, name, data_id, beam_mode, probe, excluded, ref_points): - project = ed.Project() - data_path = ed.download_data(id=data_id, destination=str(tmp_path)) + project = edi.Project() + data_path = edi.download_data(data_id, destination=str(tmp_path)) project.experiments.add_from_data_path( name=name, data_path=data_path, @@ -53,16 +53,16 @@ def _assert_tracks_reference(tmp_path, name, data_id, beam_mode, probe, excluded # The tutorial's hand-placed background is the reference curve. for px, py in ref_points: - experiment.background.create(x=px, y=py) - ref_x = np.array([p.x.value for p in experiment.background]) - ref_y = np.array([p.y.value for p in experiment.background]) + experiment.background.create(position=px, intensity=py) + ref_x = np.array([p.position.value for p in experiment.background]) + ref_y = np.array([p.intensity.value for p in experiment.background]) reference = np.interp(x, ref_x, ref_y) # Strip the reference and estimate the background automatically. experiment.background.auto_estimate() points = list(experiment.background) - est_x = np.array([p.x.value for p in points]) - est_y = np.array([p.y.value for p in points]) + est_x = np.array([p.position.value for p in points]) + est_y = np.array([p.intensity.value for p in points]) estimate = np.interp(x, est_x, est_y) span = x.max() - x.min() @@ -82,7 +82,7 @@ def test_auto_estimate_tracks_cwl_tutorial_background(tmp_path): _assert_tracks_reference( tmp_path, 'hrpt', - 3, + 'meas-lbco-hrpt', 'constant wavelength', 'neutron', [(0, 5), (165, 180)], @@ -95,7 +95,7 @@ def test_auto_estimate_tracks_tof_tutorial_background(tmp_path): _assert_tracks_reference( tmp_path, 'sim_si', - 17, + 'meas-si-mcstas-dmsc2025', 'time-of-flight', 'neutron', [(0, 55000), (105500, 200000)], diff --git a/tests/integration/workflows/test_experiment_workflow.py b/tests/integration/workflows/test_experiment_workflow.py index 9fd1ef617..a79867278 100644 --- a/tests/integration/workflows/test_experiment_workflow.py +++ b/tests/integration/workflows/test_experiment_workflow.py @@ -27,7 +27,7 @@ def _make_project_with_experiment(): s.space_group.name_h_m = 'P m -3 m' s.cell.length_a = 3.89 s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -38,7 +38,7 @@ def _make_project_with_experiment(): ) # Add experiment from data file - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) project.experiments.add_from_data_path( name='hrpt', data_path=data_path, @@ -94,31 +94,31 @@ class TestBackground: def test_create_background_points(self): project = _make_project_with_experiment() expt = project.experiments['hrpt'] - expt.background.create(id='1', x=10, y=170) - expt.background.create(id='2', x=165, y=170) + expt.background.create(id='1', position=10, intensity=170) + expt.background.create(id='2', position=165, intensity=170) assert len(expt.background) == 2 def test_background_y_is_fittable(self): project = _make_project_with_experiment() expt = project.experiments['hrpt'] - expt.background.create(id='1', x=10, y=170) - expt.background['1'].y.free = True - assert expt.background['1'].y.free is True + expt.background.create(id='1', position=10, intensity=170) + expt.background['1'].intensity.free = True + assert expt.background['1'].intensity.free is True class TestLinkedPhases: def test_create_linked_phase(self): project = _make_project_with_experiment() expt = project.experiments['hrpt'] - expt.linked_phases.create(id='lbco', scale=9.0) - assert len(expt.linked_phases) == 1 + expt.linked_structures.create(structure_id='lbco', scale=9.0) + assert len(expt.linked_structures) == 1 def test_linked_phase_scale_is_fittable(self): project = _make_project_with_experiment() expt = project.experiments['hrpt'] - expt.linked_phases.create(id='lbco', scale=9.0) - expt.linked_phases['lbco'].scale.free = True - assert expt.linked_phases['lbco'].scale.free is True + expt.linked_structures.create(structure_id='lbco', scale=9.0) + expt.linked_structures['lbco'].scale.free = True + assert expt.linked_structures['lbco'].scale.free is True class TestExcludedRegions: diff --git a/tests/integration/workflows/test_fitting_workflow.py b/tests/integration/workflows/test_fitting_workflow.py index 0901269c6..89afe8bd8 100644 --- a/tests/integration/workflows/test_fitting_workflow.py +++ b/tests/integration/workflows/test_fitting_workflow.py @@ -28,7 +28,7 @@ def _make_fit_ready_project(): s.space_group.name_h_m = 'P m -3 m' s.cell.length_a = 3.89 s.atom_sites.create( - label='La', + id='La', type_symbol='La', fract_x=0, fract_y=0, @@ -38,7 +38,7 @@ def _make_fit_ready_project(): adp_iso=0.5, ) s.atom_sites.create( - label='Ba', + id='Ba', type_symbol='Ba', fract_x=0, fract_y=0, @@ -48,7 +48,7 @@ def _make_fit_ready_project(): adp_iso=0.5, ) s.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.5, fract_y=0.5, @@ -57,7 +57,7 @@ def _make_fit_ready_project(): adp_iso=0.5, ) s.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0, fract_y=0.5, @@ -67,7 +67,7 @@ def _make_fit_ready_project(): ) # Experiment - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) project.experiments.add_from_data_path( name='hrpt', data_path=data_path, @@ -80,16 +80,16 @@ def _make_fit_ready_project(): expt.peak.broad_gauss_w = 0.123 expt.peak.broad_lorentz_x = 0 expt.peak.broad_lorentz_y = 0.0797 - expt.background.create(id='1', x=10, y=170) - expt.background.create(id='2', x=165, y=170) - expt.linked_phases.create(id='lbco', scale=9.0) + expt.background.create(id='1', position=10, intensity=170) + expt.background.create(id='2', position=165, intensity=170) + expt.linked_structures.create(structure_id='lbco', scale=9.0) # Free parameters s.cell.length_a.free = True - expt.linked_phases['lbco'].scale.free = True + expt.linked_structures['lbco'].scale.free = True expt.instrument.calib_twotheta_offset.free = True - expt.background['1'].y.free = True - expt.background['2'].y.free = True + expt.background['1'].intensity.free = True + expt.background['2'].intensity.free = True return project @@ -99,7 +99,7 @@ def test_create_alias(self): project = _make_fit_ready_project() s = project.structures['lbco'] project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=s.atom_sites['La'].adp_iso, ) assert len(project.analysis.aliases) == 1 @@ -108,11 +108,11 @@ def test_create_multiple_aliases(self): project = _make_fit_ready_project() s = project.structures['lbco'] project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=s.atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=s.atom_sites['Ba'].adp_iso, ) assert len(project.analysis.aliases) == 2 @@ -123,11 +123,11 @@ def test_create_constraint(self): project = _make_fit_ready_project() s = project.structures['lbco'] project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=s.atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=s.atom_sites['Ba'].adp_iso, ) project.analysis.constraints.create( @@ -169,11 +169,11 @@ def test_fit_with_constraints(self): s.atom_sites['Ba'].adp_iso.free = True project.analysis.aliases.create( - label='biso_La', + id='biso_La', param=s.atom_sites['La'].adp_iso, ) project.analysis.aliases.create( - label='biso_Ba', + id='biso_Ba', param=s.atom_sites['Ba'].adp_iso, ) project.analysis.constraints.create( diff --git a/tests/integration/workflows/test_switchable_categories.py b/tests/integration/workflows/test_switchable_categories.py index 71db77df0..3674c99ff 100644 --- a/tests/integration/workflows/test_switchable_categories.py +++ b/tests/integration/workflows/test_switchable_categories.py @@ -20,7 +20,7 @@ def _make_project_with_experiment(): Project._loading = False project.structures.create(name='s') - data_path = download_data(id=3, destination=TEMP_DIR) + data_path = download_data('meas-lbco-hrpt', destination=TEMP_DIR) project.experiments.add_from_data_path(name='e', data_path=data_path) return project diff --git a/tests/tutorials/analysis_cif_reader.py b/tests/tutorials/analysis_edi_reader.py similarity index 92% rename from tests/tutorials/analysis_cif_reader.py rename to tests/tutorials/analysis_edi_reader.py index b011b6fa5..9d584db4c 100644 --- a/tests/tutorials/analysis_cif_reader.py +++ b/tests/tutorials/analysis_edi_reader.py @@ -8,12 +8,12 @@ Two sources are parsed: -* ``analysis/analysis.cif`` for the scalar ``_fit_result.*`` metrics and +* ``analysis/analysis.edi`` for the scalar ``_fit_result.*`` metrics and the ``_fit_parameter`` loop. The loop's ``start_value`` column is a *pre-fit* snapshot, so it is **not** used for deterministic parameter values; it only tells us which parameters were refined (and, for Bayesian fits, carries the post-fit ``posterior_median``). -* ``structures/*.cif`` and ``experiments/*.cif`` for the *refined* +* ``structures/*.edi`` and ``experiments/*.edi`` for the *refined* parameter values of deterministic fits, which the library persists as the live ``param.value`` (e.g. ``si 1.4525(73)``). """ @@ -29,7 +29,7 @@ from pathlib import Path _FIT_RESULT_PREFIX = '_fit_result.' -_FIT_PARAMETER_NAME_TAG = '_fit_parameter.param_unique_name' +_FIT_PARAMETER_NAME_TAG = '_fit_parameter.parameter_unique_name' _BAYESIAN_VALUE_COLUMN = 'posterior_median' # Parameter attribute names that differ from their CIF data-name tail. @@ -66,7 +66,7 @@ class _CifBlock: @dataclass(frozen=True) -class AnalysisCif: +class AnalysisEdi: """Parsed fit results for one saved tutorial project.""" fit_result: dict[str, str] @@ -92,8 +92,8 @@ def parameter_value(self, name: str) -> float: """Return the refined value of a named parameter. Bayesian fits use the post-fit ``posterior_median`` recorded in - ``analysis.cif``. Deterministic fits read the refined value from - the model/experiment CIFs, because ``analysis.cif`` only keeps a + ``analysis.edi``. Deterministic fits read the refined value from + the model/experiment Edi files, because ``analysis.edi`` only keeps a pre-fit snapshot of each parameter. """ columns = self.fit_parameters.get(name, {}) @@ -209,7 +209,7 @@ def _load_model_blocks(project_dir: Path) -> dict[str, _CifBlock]: directory = project_dir / subdir if not directory.is_dir(): continue - for cif_path in sorted(directory.glob('*.cif')): + for cif_path in sorted(directory.glob('*.edi')): text = cif_path.read_text(encoding='utf-8') name = next( ( @@ -224,7 +224,7 @@ def _load_model_blocks(project_dir: Path) -> dict[str, _CifBlock]: return blocks -def parse_analysis_cif(text: str, model_blocks: dict[str, _CifBlock] | None = None) -> AnalysisCif: +def parse_analysis_edi(text: str, model_blocks: dict[str, _CifBlock] | None = None) -> AnalysisEdi: """Parse ``_fit_result`` scalars and the ``_fit_parameter`` loop.""" fit_result: dict[str, str] = {} fit_parameters: dict[str, dict[str, str]] = {} @@ -247,19 +247,19 @@ def parse_analysis_cif(text: str, model_blocks: dict[str, _CifBlock] | None = No fit_result[key.removeprefix(_FIT_RESULT_PREFIX)] = _unquote(value.strip()) index += 1 - return AnalysisCif( + return AnalysisEdi( fit_result=fit_result, fit_parameters=fit_parameters, model_blocks=model_blocks or {}, ) -def read_analysis_cif(path: Path) -> AnalysisCif: - """Parse ``analysis.cif`` at *path* plus its sibling model CIFs. +def read_analysis_edi(path: Path) -> AnalysisEdi: + """Parse ``analysis.edi`` at *path* plus its sibling model Edi files. Deterministic refined parameter values are read from the ``structures/`` and ``experiments/`` CIFs of the same project - directory (``<project>/analysis/analysis.cif`` → ``<project>``). + directory (``<project>/analysis/analysis.edi`` → ``<project>``). """ model_blocks = _load_model_blocks(path.parents[1]) - return parse_analysis_cif(path.read_text(encoding='utf-8'), model_blocks) + return parse_analysis_edi(path.read_text(encoding='utf-8'), model_blocks) diff --git a/tests/tutorials/baseline.json b/tests/tutorials/baseline.json index 1398c225b..94be4987d 100644 --- a/tests/tutorials/baseline.json +++ b/tests/tutorials/baseline.json @@ -1,266 +1,253 @@ { - "ed_10_ni_pdf": { - "result_kind": "deterministic", - "rtol": 0.02, - "reduced_chi_square": 207.102333, - "R_factor_all": 0.098321, - "wR_factor_all": 0.09479, + "bayesian-dream-display-lbco-hrpt": { + "result_kind": "bayesian", + "rtol": 0.1, + "reduced_chi_square": 1.289863, "parameters": { - "ni.cell.length_a": 3.526009, - "pdf.linked_phases.ni.scale": 0.98919 + "lbco.cell.length_a": 3.891319, + "hrpt.linked_structure.lbco.scale": 9.126921 } }, - "ed_11_si_nomad_pdf": { - "result_kind": "deterministic", - "rtol": 0.02, - "reduced_chi_square": 170.543161, - "R_factor_all": 0.084011, - "wR_factor_all": 0.082977, + "bayesian-dream-lbco-hrpt": { + "result_kind": "bayesian", + "rtol": 0.1, + "reduced_chi_square": 1.289863, "parameters": { - "si.cell.length_a": 5.430592, - "nomad.linked_phases.si.scale": 1.2728 + "lbco.cell.length_a": 3.891319, + "hrpt.linked_structure.lbco.scale": 9.126921 + } + }, + "bayesian-emcee-lbco-hrpt": { + "result_kind": "bayesian", + "rtol": 0.1, + "reduced_chi_square": 1.289863, + "parameters": { + "lbco.cell.length_a": 3.891316, + "hrpt.linked_structure.lbco.scale": 9.130046 + } + }, + "bayesian-emcee-resume-lbco-hrpt": { + "result_kind": "bayesian", + "rtol": 0.1, + "reduced_chi_square": 1.289889, + "parameters": { + "lbco.cell.length_a": 3.891321, + "hrpt.linked_structure.lbco.scale": 9.128088 } }, - "ed_12_nacl_xray_pdf": { + "bayesian-emcee-tbti-heidi": { + "result_kind": "bayesian", + "rtol": 0.1, + "reduced_chi_square": 12.716655, + "parameters": { + "tbti.atom_site.Tb.adp_iso": 0.532284, + "heidi.linked_structure.scale": 2.923592 + } + }, + "calibrate-beer-ess": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 1.476045, - "R_factor_all": 0.110217, - "wR_factor_all": 0.113817, + "reduced_chi_square": 12.756675, + "r_factor_all": 0.049714, + "wr_factor_all": 0.068833, "parameters": { - "nacl.cell.length_a": 5.6018, - "xray_pdf.linked_phases.nacl.scale": 0.42537 + "expt_s2.linked_structure.ferrite.scale": 50.33, + "expt_s2.linked_structure.austenite.scale": 12.005 } }, - "ed_13_main": { + "fitting-exercise-si-lbco-main": { "result_kind": "deterministic", "rtol": 0.02, "reduced_chi_square": 1.537656, - "R_factor_all": 0.046203, - "wR_factor_all": 0.05534, + "r_factor_all": 0.046203, + "wr_factor_all": 0.05534, "parameters": { "lbco.cell.length_a": 3.89125, - "sim_lbco.linked_phases.lbco.scale": 4.855, - "sim_lbco.linked_phases.si.scale": 0.0373 + "sim_lbco.linked_structure.lbco.scale": 4.855, + "sim_lbco.linked_structure.si.scale": 0.0373 } }, - "ed_13_reference": { + "fitting-exercise-si-lbco-reference": { "result_kind": "deterministic", "rtol": 0.02, "reduced_chi_square": 2.30651, - "R_factor_all": 0.068706, - "wR_factor_all": 0.091774, + "r_factor_all": 0.068706, + "wr_factor_all": 0.091774, "parameters": { - "sim_si.linked_phases.si.scale": 1.454, + "sim_si.linked_structure.si.scale": 1.454, "sim_si.peak.rise_alpha_0": -0.00662 } }, - "ed_14_tbti_heidi": { + "joint-si-bragg-pdf": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 2.944513, - "R_factor_all": 0.042052, - "wR_factor_all": 0.042544, + "reduced_chi_square": 51.874938, + "r_factor_all": 0.104889, + "wr_factor_all": 0.082877, "parameters": { - "tbti.atom_site.Ti.occupancy": 0.9661, - "heidi.linked_crystal.scale": 2.874 + "si.cell.length_a": 5.430637, + "sepd.linked_structure.si.scale": 16.05, + "nomad.linked_structure.si.scale": 1.2712 } }, - "ed_15_taurine_senju": { + "load-and-fit-lbco-hrpt": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 12.192189, - "R_factor_all": 0.133438, - "wR_factor_all": 0.084877, + "reduced_chi_square": 1.265064, + "r_factor_all": 0.055846, + "wr_factor_all": 0.0714, "parameters": { - "taurine.atom_site.S1.fract_x": 0.203646, - "senju.linked_crystal.scale": 1.358181 + "lbco.cell.length_a": 3.890786, + "hrpt.linked_structure.lbco.scale": 9.128 } }, - "ed_16_si_bragg_pdf": { + "pdf-nacl-xrd": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 51.874887, - "R_factor_all": 0.104937, - "wR_factor_all": 0.082874, + "reduced_chi_square": 1.476045, + "r_factor_all": 0.110217, + "wr_factor_all": 0.113817, "parameters": { - "si.cell.length_a": 5.430637, - "sepd.linked_phases.si.scale": 16.06, - "nomad.linked_phases.si.scale": 1.2713 + "nacl.cell.length_a": 5.6018, + "xray_pdf.linked_structure.nacl.scale": 0.42537 } }, - "ed_18_lbco_hrpt": { + "pdf-ni-npd": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 1.289805, - "R_factor_all": 0.056345, - "wR_factor_all": 0.072095, + "reduced_chi_square": 207.102333, + "r_factor_all": 0.098321, + "wr_factor_all": 0.09479, "parameters": { - "lbco.cell.length_a": 3.890868, - "hrpt.linked_phases.lbco.scale": 9.135 + "ni.cell.length_a": 3.526009, + "pdf.linked_structure.ni.scale": 0.98919 } }, - "ed_1_lbco_hrpt": { + "pdf-si-nomad": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 1.290457, - "R_factor_all": 0.056249, - "wR_factor_all": 0.072055, + "reduced_chi_square": 170.543161, + "r_factor_all": 0.084011, + "wr_factor_all": 0.082977, "parameters": { - "lbco.cell.length_a": 3.890868, - "hrpt.linked_phases.lbco.scale": 9.135 + "si.cell.length_a": 5.430592, + "nomad.linked_structure.si.scale": 1.2728 } }, - "ed_20_beer_mcstas": { + "refine-cosio-d20": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 12.756675, - "R_factor_all": 0.049714, - "wR_factor_all": 0.068833, - "parameters": { - "expt_s2.linked_phases.ferrite.scale": 50.33, - "expt_s2.linked_phases.austenite.scale": 12.005 - } - }, - "ed_21_lbco_hrpt_bumps_dream": { - "result_kind": "bayesian", - "rtol": 0.1, - "reduced_chi_square": 1.289863, - "parameters": { - "lbco.cell.length_a": 3.891316, - "hrpt.linked_phases.lbco.scale": 9.134207 - } - }, - "ed_22_tbti_heidi_emcee": { - "result_kind": "bayesian", - "rtol": 0.1, - "reduced_chi_square": 12.717513, + "reduced_chi_square": 4.380305, + "r_factor_all": 0.029898, + "wr_factor_all": 0.03934, "parameters": { - "tbti.atom_site.Tb.adp_iso": 0.532057, - "heidi.linked_crystal.scale": 2.923727 + "cosio.cell.length_a": 10.30837, + "cosio.cell.length_b": 6.00362, + "cosio.cell.length_c": 4.78644, + "d20.linked_structure.cosio.scale": 1.331 } }, - "ed_23_cosio_d20_scan": { + "refine-hs-hrpt": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 53.079845, - "R_factor_all": 0.078802, - "wR_factor_all": 0.137043, - "parameters": { - "cosio.cell.length_a": 10.30635, - "cosio.cell.length_b": 6.00149, - "cosio.cell.length_c": 4.787, - "d20.linked_phases.cosio.scale": 1.357 - } - }, - "ed_24_lbco_hrpt_bumps_dream": { - "result_kind": "bayesian", - "rtol": 0.1, - "reduced_chi_square": 1.289855, - "parameters": { - "lbco.cell.length_a": 3.891321, - "hrpt.linked_phases.lbco.scale": 9.132917 - } - }, - "ed_25_lbco_hrpt_emcee": { - "result_kind": "bayesian", - "rtol": 0.1, - "reduced_chi_square": 1.289868, + "reduced_chi_square": 1.929924, + "r_factor_all": 0.03972, + "wr_factor_all": 0.050183, "parameters": { - "lbco.cell.length_a": 3.891321, - "hrpt.linked_phases.lbco.scale": 9.133284 + "hs.cell.length_a": 6.864, + "hs.cell.length_c": 14.1418, + "hrpt.linked_structure.hs.scale": 0.5132 } }, - "ed_26_lbco_hrpt_emcee": { - "result_kind": "bayesian", - "rtol": 0.1, - "reduced_chi_square": 1.289881, + "refine-lbco-hrpt-from-cif": { + "result_kind": "deterministic", + "rtol": 0.02, + "reduced_chi_square": 1.290457, + "r_factor_all": 0.056249, + "wr_factor_all": 0.072055, "parameters": { - "lbco.cell.length_a": 3.891322, - "hrpt.linked_phases.lbco.scale": 9.131956 + "lbco.cell.length_a": 3.890868, + "hrpt.linked_structure.lbco.scale": 9.135 } }, - "ed_2_lbco_hrpt": { + "refine-lbco-hrpt-from-data": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 1.265395, - "R_factor_all": 0.055898, - "wR_factor_all": 0.071409, + "reduced_chi_square": 1.265618, + "r_factor_all": 0.055879, + "wr_factor_all": 0.071416, "parameters": { - "lbco.cell.length_a": 3.890758, - "hrpt.linked_phases.lbco.scale": 9.145 + "lbco.cell.length_a": 3.890753, + "hrpt.linked_structure.lbco.scale": 9.12 } }, - "ed_3_lbco_hrpt": { + "refine-lbco-hrpt-report": { "result_kind": "deterministic", "rtol": 0.02, "reduced_chi_square": 1.28275, - "R_factor_all": 0.056132, - "wR_factor_all": 0.071886, + "r_factor_all": 0.056132, + "wr_factor_all": 0.071886, "parameters": { "lbco.cell.length_a": 3.890869, - "hrpt.linked_phases.lbco.scale": 8.906 + "hrpt.linked_structure.lbco.scale": 8.906 } }, - "ed_5_cosio_d20": { + "refine-lbco-si-mcstas": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 4.380305, - "R_factor_all": 0.029898, - "wR_factor_all": 0.03934, + "reduced_chi_square": 9.532167, + "r_factor_all": 0.058084, + "wr_factor_all": 0.076338, "parameters": { - "cosio.cell.length_a": 10.30837, - "cosio.cell.length_b": 6.00362, - "cosio.cell.length_c": 4.78644, - "d20.linked_phases.cosio.scale": 1.331 + "lbco.cell.length_a": 3.89046, + "si.cell.length_a": 5.4358, + "mcstas.linked_structure.lbco.scale": 4.491, + "mcstas.linked_structure.si.scale": 0.00473 } }, - "ed_6_hs_hrpt": { + "refine-ncaf-wish": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 1.929924, - "R_factor_all": 0.03972, - "wR_factor_all": 0.050183, + "reduced_chi_square": 15.486737, + "r_factor_all": 0.069775, + "wr_factor_all": 0.081356, "parameters": { - "hs.cell.length_a": 6.864, - "hs.cell.length_c": 14.1418, - "hrpt.linked_phases.hs.scale": 0.5132 + "wish_5_6.linked_structure.ncaf.scale": 1.1029, + "wish_4_7.linked_structure.ncaf.scale": 2.5216 } }, - "ed_7_si_sepd": { + "refine-si-sepd": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 2.970886, + "reduced_chi_square": 2.666035, "platform_sensitive": true, - "R_factor_all": 0.086528, - "wR_factor_all": 0.063032, + "r_factor_all": 0.079746, + "wr_factor_all": 0.05971, "parameters": { - "si.cell.length_a": 5.432487, - "sepd.linked_phases.si.scale": 1113.7 + "si.cell.length_a": 5.430972, + "sepd.linked_structure.si.scale": 1134.057081 } }, - "ed_8_ncaf_wish": { + "refine-taurine-senju": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 15.486737, - "R_factor_all": 0.069775, - "wR_factor_all": 0.081356, + "reduced_chi_square": 12.192189, + "r_factor_all": 0.133438, + "wr_factor_all": 0.084877, "parameters": { - "wish_5_6.linked_phases.ncaf.scale": 1.1029, - "wish_4_7.linked_phases.ncaf.scale": 2.5216 + "taurine.atom_site.S1.fract_x": 0.203646, + "senju.linked_structure.scale": 1.358181 } }, - "ed_9_lbco_si_mcstas": { + "refine-tbti-heidi": { "result_kind": "deterministic", "rtol": 0.02, - "reduced_chi_square": 9.532167, - "R_factor_all": 0.058084, - "wR_factor_all": 0.076338, + "reduced_chi_square": 2.944513, + "r_factor_all": 0.042052, + "wr_factor_all": 0.042544, "parameters": { - "lbco.cell.length_a": 3.89046, - "si.cell.length_a": 5.4358, - "mcstas.linked_phases.lbco.scale": 4.491, - "mcstas.linked_phases.si.scale": 0.00473 + "tbti.atom_site.Ti.occupancy": 0.9661, + "heidi.linked_structure.scale": 2.874 } } } diff --git a/tests/tutorials/conftest.py b/tests/tutorials/conftest.py index 011ed51ec..f6e3d7790 100644 --- a/tests/tutorials/conftest.py +++ b/tests/tutorials/conftest.py @@ -4,7 +4,7 @@ Pytest runs with ``--import-mode=importlib``, which does not add the test directory to ``sys.path``. Insert it here so the test module can -import the sibling ``analysis_cif_reader`` helper. +import the sibling ``analysis_edi_reader`` helper. """ from __future__ import annotations diff --git a/tests/tutorials/generate_baseline.py b/tests/tutorials/generate_baseline.py index fc1b09659..f658d9055 100644 --- a/tests/tutorials/generate_baseline.py +++ b/tests/tutorials/generate_baseline.py @@ -5,7 +5,7 @@ Run the tutorials first (``pixi run script-tests`` or ``pixi run notebook-tests``) so each saved project exists under ``<artifact-root>/projects/``. Then run this script to (re)write -``baseline.json`` from the freshly produced ``analysis.cif`` files:: +``baseline.json`` from the freshly produced ``analysis.edi`` files:: pixi run python tests/tutorials/generate_baseline.py @@ -19,8 +19,8 @@ import os from pathlib import Path -from analysis_cif_reader import AnalysisCif -from analysis_cif_reader import read_analysis_cif +from analysis_edi_reader import AnalysisEdi +from analysis_edi_reader import read_analysis_edi # Relative tolerances used when comparing against the baseline. Bayesian # (MCMC) fits are seeded but still vary slightly more than deterministic @@ -29,7 +29,7 @@ BAYESIAN_RTOL = 0.10 # Optional deterministic fit-quality scalars to track when present. -OPTIONAL_SCALARS = ('R_factor_all', 'wR_factor_all') +OPTIONAL_SCALARS = ('r_factor_all', 'wr_factor_all') # Tutorials whose fit metrics are not reproducible across platforms, # so they are exempted from the numeric baseline comparison. ed-7 @@ -37,7 +37,15 @@ # differs between arm64 macOS and x86-64 Linux/Windows. They still # run in script-/notebook-tests and are checked for result_kind; # only their numeric metrics are skipped. Add a name here to exempt. -PLATFORM_SENSITIVE = frozenset({'ed_7_si_sepd'}) +PLATFORM_SENSITIVE = frozenset({'refine-si-sepd'}) + +# Sequential-fitting tutorials run one fit per measured point and write +# ``_fitting_mode.type sequential`` with no single ``_fit_result`` block, +# so there is no scalar reduced_chi_square / r-factor to baseline. They +# are excluded from the numeric baseline here and covered separately by +# ``test_sequential_tutorial_saved`` (which asserts each one saved a +# sequential analysis.edi). Add a name here to exclude another. +SEQUENTIAL_TUTORIALS = frozenset({'refine-cosio-d20-tscan', 'refine-cosio-d20-tscan-resumed'}) # Number of refined parameters to track per tutorial (cell lengths and # phase scales preferred, topped up from the front of the loop). @@ -61,7 +69,7 @@ def _is_key_parameter(name: str) -> bool: return '.cell.length_' in name or name.endswith('.scale') -def select_key_parameters(cif: AnalysisCif) -> dict[str, float]: +def select_key_parameters(cif: AnalysisEdi) -> dict[str, float]: """Return the tracked refined parameter values for one project.""" names = list(cif.fit_parameters) selected = [name for name in names if _is_key_parameter(name)] @@ -74,7 +82,7 @@ def select_key_parameters(cif: AnalysisCif) -> dict[str, float]: return {name: round(cif.parameter_value(name), ROUND_DIGITS) for name in ordered} -def build_entry(name: str, cif: AnalysisCif) -> dict | None: +def build_entry(name: str, cif: AnalysisEdi) -> dict | None: """Build a baseline entry, or ``None`` if the project has no fit.""" reduced_chi_square = cif.scalar('reduced_chi_square') if reduced_chi_square is None or reduced_chi_square <= 0: @@ -100,11 +108,17 @@ def collect_baseline(root: Path) -> dict[str, dict]: """Build baseline entries for every saved project under *root*.""" projects_dir = root / 'projects' baseline: dict[str, dict] = {} - for cif_path in sorted(projects_dir.glob('*/analysis/analysis.cif')): + for cif_path in sorted(projects_dir.glob('*/analysis/analysis.edi')): name = cif_path.parents[1].name - if not name.startswith('ed_'): + # Skip downloaded project archives (the ``proj-`` data category); + # only tutorial-saved projects are baselined. + if name.startswith('proj-'): + continue + # Sequential fits have no scalar fit result to compare; they are + # covered by ``test_sequential_tutorial_saved`` instead. + if name in SEQUENTIAL_TUTORIALS: continue - entry = build_entry(name, read_analysis_cif(cif_path)) + entry = build_entry(name, read_analysis_edi(cif_path)) if entry is not None: baseline[name] = entry return baseline diff --git a/tests/tutorials/test_tutorial_outputs.py b/tests/tutorials/test_tutorial_outputs.py index 45c40bece..c94346d94 100644 --- a/tests/tutorials/test_tutorial_outputs.py +++ b/tests/tutorials/test_tutorial_outputs.py @@ -5,8 +5,8 @@ This module runs *after* the tutorials have been executed (as scripts via ``pixi run script-tests`` or as notebooks via ``pixi run notebook-tests``). Each tutorial saves its project under -``<artifact-root>/projects/ed_<n>_<name>/``; here we parse every -``analysis/analysis.cif`` and compare its fit-quality metrics and a few +``<artifact-root>/projects/<tutorial-name>/``; here we parse every +``analysis/analysis.edi`` and compare its fit-quality metrics and a few refined parameter values against the committed ``baseline.json``. If no tutorial artifacts are present the whole module is skipped, so the @@ -21,8 +21,9 @@ from pathlib import Path import pytest -from analysis_cif_reader import read_analysis_cif +from analysis_edi_reader import read_analysis_edi from generate_baseline import PLATFORM_SENSITIVE +from generate_baseline import SEQUENTIAL_TUTORIALS from generate_baseline import artifact_root BASELINE = json.loads((Path(__file__).parent / 'baseline.json').read_text(encoding='utf-8')) @@ -32,14 +33,20 @@ def _analysis_cif_path(name: str) -> Path: - """Return the ``analysis.cif`` path for a saved tutorial project.""" - return artifact_root() / 'projects' / name / 'analysis' / 'analysis.cif' + """Return the ``analysis.edi`` path for a saved tutorial project.""" + return artifact_root() / 'projects' / name / 'analysis' / 'analysis.edi' def _artifacts_present() -> bool: """Return whether any tutorial project has been saved.""" projects_dir = artifact_root() / 'projects' - return projects_dir.is_dir() and any(projects_dir.glob('ed_*/analysis/analysis.cif')) + if not projects_dir.is_dir(): + return False + # Tutorial-saved projects only; downloaded ``proj-`` archives don't count. + return any( + not path.parents[1].name.startswith('proj-') + for path in projects_dir.glob('*/analysis/analysis.edi') + ) pytestmark = pytest.mark.skipif( @@ -50,7 +57,7 @@ def _artifacts_present() -> bool: def _assert_close(actual: float | None, expected: float, rtol: float, label: str) -> None: """Assert *actual* matches *expected* within a relative tolerance.""" - assert actual is not None, f'{label}: value missing from analysis.cif' + assert actual is not None, f'{label}: value missing from analysis.edi' assert math.isclose(actual, expected, rel_tol=rtol, abs_tol=ABS_TOL), ( f'{label}: {actual} != {expected} (rel_tol={rtol})' ) @@ -58,12 +65,12 @@ def _assert_close(actual: float | None, expected: float, rtol: float, label: str @pytest.mark.parametrize('name', sorted(BASELINE)) def test_tutorial_output(name: str) -> None: - """Check one tutorial's saved analysis.cif against the baseline.""" + """Check one tutorial's saved analysis.edi against the baseline.""" expected = BASELINE[name] cif_path = _analysis_cif_path(name) assert cif_path.is_file(), f"Missing {cif_path}; tutorial '{name}' did not save its project." - cif = read_analysis_cif(cif_path) + cif = read_analysis_edi(cif_path) # result_kind reflects the minimizer type; it is reproducible # across platforms, so it is always checked. @@ -85,7 +92,7 @@ def test_tutorial_output(name: str) -> None: f'{name}: reduced_chi_square', ) - for scalar_name in ('R_factor_all', 'wR_factor_all'): + for scalar_name in ('r_factor_all', 'wr_factor_all'): if scalar_name in expected: _assert_close( cif.scalar(scalar_name), @@ -96,7 +103,7 @@ def test_tutorial_output(name: str) -> None: for param_name, exp_value in expected['parameters'].items(): assert param_name in cif.fit_parameters, ( - f"{name}: parameter '{param_name}' missing from analysis.cif" + f"{name}: parameter '{param_name}' missing from analysis.edi" ) _assert_close( cif.parameter_value(param_name), @@ -104,3 +111,20 @@ def test_tutorial_output(name: str) -> None: rtol, f'{name}: {param_name}', ) + + +@pytest.mark.parametrize('name', sorted(SEQUENTIAL_TUTORIALS)) +def test_sequential_tutorial_saved(name: str) -> None: + """Check a sequential-fit tutorial saved a sequential analysis.edi. + + Sequential fits run one refinement per measured point and write no + single ``_fit_result`` block, so they cannot go through the scalar + baseline in ``test_tutorial_output``. This guards the persistence of + that workflow instead: the project must save, and its analysis must + record ``_fitting_mode.type sequential``. + """ + cif_path = _analysis_cif_path(name) + assert cif_path.is_file(), f"Missing {cif_path}; tutorial '{name}' did not save its project." + + text = cif_path.read_text(encoding='utf-8') + assert '_fitting_mode.type sequential' in text, f'{name}: analysis.edi is not a sequential fit' diff --git a/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py b/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py index 53658be69..0ae584840 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_cryspy.py @@ -14,7 +14,7 @@ def _cwl_experiment_stub(): return SimpleNamespace( name='exp', - type=SimpleNamespace( + experiment_type=SimpleNamespace( sample_form=SimpleNamespace(value=SampleFormEnum.POWDER), beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH), ), @@ -157,7 +157,7 @@ def test_update_structure_zeroes_biso_for_anisotropic_atoms(): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure.atom_sites['Si'].adp_type = 'Bani' cryspy_model_dict = { @@ -180,9 +180,9 @@ def test_update_structure_restores_wyckoff_multiplicity_after_coordinate_wrappin structure = Structure(name='hs') structure.space_group.name_h_m = 'R -3 m' - structure.space_group.it_coordinate_system_code = 'h' + structure.space_group.coord_system_code = 'h' structure.atom_sites.create( - label='O', + id='O', type_symbol='O', fract_x=0.20587714, fract_y=-0.20587714, @@ -226,13 +226,15 @@ def test_last_powder_refln_records_converts_cwl_two_theta_to_degrees(): structure = SimpleNamespace(name='phase') experiment = SimpleNamespace( name='exp', - type=SimpleNamespace(beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH)), + experiment_type=SimpleNamespace( + beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH) + ), ) - records = calculator.last_powder_refln_records(structure, experiment, phase_id='phase-a') + records = calculator.last_powder_refln_records(structure, experiment, structure_id='phase-a') assert len(records) == 1 - assert records[0].phase_id == 'phase-a' + assert records[0].structure_id == 'phase-a' assert records[0].two_theta == pytest.approx(90.0) assert records[0].d_spacing == pytest.approx(2.0) assert records[0].f_calc == pytest.approx(5.0) @@ -256,13 +258,15 @@ def test_last_powder_refln_records_reads_tof_time_and_d_spacing(): structure = SimpleNamespace(name='phase') experiment = SimpleNamespace( name='exp', - type=SimpleNamespace(beam_mode=SimpleNamespace(value=BeamModeEnum.TIME_OF_FLIGHT)), + experiment_type=SimpleNamespace( + beam_mode=SimpleNamespace(value=BeamModeEnum.TIME_OF_FLIGHT) + ), ) - records = calculator.last_powder_refln_records(structure, experiment, phase_id='phase-b') + records = calculator.last_powder_refln_records(structure, experiment, structure_id='phase-b') assert len(records) == 1 - assert records[0].phase_id == 'phase-b' + assert records[0].structure_id == 'phase-b' assert records[0].time_of_flight == pytest.approx(1234.0) assert records[0].d_spacing == pytest.approx(3.21) assert records[0].f_calc == pytest.approx(6.0) @@ -285,13 +289,15 @@ def test_last_powder_refln_records_reads_xray_charge_structure_factor(): structure = SimpleNamespace(name='phase') experiment = SimpleNamespace( name='exp', - type=SimpleNamespace(beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH)), + experiment_type=SimpleNamespace( + beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH) + ), ) - records = calculator.last_powder_refln_records(structure, experiment, phase_id='phase-x') + records = calculator.last_powder_refln_records(structure, experiment, structure_id='phase-x') assert len(records) == 1 - assert records[0].phase_id == 'phase-x' + assert records[0].structure_id == 'phase-x' assert records[0].two_theta == pytest.approx(60.0) assert records[0].d_spacing == pytest.approx(2.5) assert records[0].f_calc == pytest.approx(10.0) @@ -306,7 +312,7 @@ def _make_beta_structure(): structure.cell.length_a = 10.0 structure.cell.length_b = 10.0 structure.cell.length_c = 10.0 - structure.atom_sites.create(label='Fe', type_symbol='Fe', adp_iso=0.0) + structure.atom_sites.create(id='Fe', type_symbol='Fe', adp_iso=0.0) structure.atom_sites['Fe'].adp_type = 'beta' structure._sync_atom_site_aniso() aniso = structure.atom_site_aniso['Fe'] @@ -375,14 +381,14 @@ def test_temporarily_convert_to_u_notation_stashes_and_restores_beta(): aniso = structure.atom_site_aniso['Fe'] assert atom.adp_type.value == AdpTypeEnum.UANI.value assert aniso.adp_11.value == pytest.approx(expected_u11) - assert '_atom_site_aniso.U_11' in aniso.adp_11._cif_handler.names + assert '_atom_site_aniso.U_11' in aniso.adp_11._tags.edi_names CryspyCalculator._restore_from_u_notation(structure, saved) assert atom.adp_type.value == AdpTypeEnum.BETA.value assert aniso.adp_11.value == pytest.approx(0.001) assert aniso.adp_23.value == pytest.approx(-0.0002) - assert '_atom_site_aniso.beta_11' in aniso.adp_11._cif_handler.names + assert '_atom_site_aniso.beta_11' in aniso.adp_11._tags.cif_names def _bragg_powder_experiment(beam_mode): @@ -397,7 +403,7 @@ def _bragg_powder_experiment(beam_mode): scattering_type='bragg', ) experiment.preferred_orientation.create( - phase_id='lbco', march_r=0.5, index_h=0, index_k=0, index_l=1 + structure_id='lbco', march_r=0.5, index_h=0, index_k=0, index_l=1 ) return experiment @@ -408,7 +414,7 @@ def test_cif_pref_orient_section_emits_for_constant_wavelength(): experiment = _bragg_powder_experiment('constant wavelength') structure = SimpleNamespace(name='lbco') cif_lines: list[str] = [] - MUT._cif_pref_orient_section(cif_lines, experiment.type, experiment, structure) + MUT._cif_pref_orient_section(cif_lines, experiment.experiment_type, experiment, structure) text = '\n'.join(cif_lines) assert '_texture_g_1' in text @@ -426,7 +432,7 @@ def test_cif_pref_orient_section_skips_time_of_flight(): experiment = _bragg_powder_experiment('time-of-flight') structure = SimpleNamespace(name='lbco') cif_lines: list[str] = [] - MUT._cif_pref_orient_section(cif_lines, experiment.type, experiment, structure) + MUT._cif_pref_orient_section(cif_lines, experiment.experiment_type, experiment, structure) assert not any('_texture_g_1' in line for line in cif_lines) @@ -488,6 +494,31 @@ def test_invalidate_stale_cache_drops_dict_on_pref_orient_axis_change(): assert combined_name not in calc._cryspy_dicts +def test_relabel_cif_tags_for_cryspy_maps_edi_tags_to_legacy(): + """Edi tags map to the legacy IUCr spellings cryspy needs.""" + from easydiffraction.analysis.calculators.cryspy import CryspyCalculator + + cif = ( + '_space_group.name_h_m "P m -3 m"\n' + '_space_group.coord_system_code 1\n' + 'loop_\n_atom_site.id\n_atom_site.adp_iso\nSi 0.4\n' + 'loop_\n_atom_site_aniso.id\n_atom_site_aniso.adp_11\nSi 0.01\n' + ) + + out = CryspyCalculator._relabel_cif_tags_for_cryspy(cif) + + assert '_space_group.name_H-M_alt' in out + assert '_space_group.IT_coordinate_system_code' in out + assert '_atom_site.label' in out + assert '_atom_site.U_iso_or_equiv' in out + assert '_atom_site_aniso.label' in out + assert '_atom_site_aniso.U_11' in out + # The Edi spellings are fully removed. + assert '_atom_site.id\n' not in out + assert '_atom_site.adp_iso' not in out + assert '_space_group.name_h_m' not in out + + def _absorption_cwl_experiment_stub(x, mu_r): """Minimal CWL experiment stub carrying a cylindrical absorption.""" from easydiffraction.datablocks.experiment.categories.absorption.cylinder_hewat import ( @@ -499,7 +530,9 @@ def _absorption_cwl_experiment_stub(x, mu_r): absorption.mu_r = mu_r return SimpleNamespace( name='exp', - type=SimpleNamespace(beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH)), + experiment_type=SimpleNamespace( + beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH) + ), absorption=absorption, data=SimpleNamespace(x=np.asarray(x, dtype=float)), ) diff --git a/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py b/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py index cf3d0b3f0..780b6ee10 100644 --- a/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py +++ b/tests/unit/easydiffraction/analysis/calculators/test_pdffit.py @@ -60,12 +60,15 @@ def __init__(self): self.name = 'E' self.peak = _DummyPeak() self.data = type('D', (), {'x': np.linspace(0.0, 1.0, 5)})() - self.type = type('T', (), {'radiation_probe': type('P', (), {'value': 'neutron'})()})() - self.linked_phases = _DummyLinkedPhases() + self.experiment_type = type( + 'T', (), {'radiation_probe': type('P', (), {'value': 'neutron'})()} + )() + self.linked_structures = _DummyLinkedPhases() class _DummyStructure: name = 'PhaseA' + atom_sites = () @property def as_cif(self): @@ -115,3 +118,74 @@ def test_pdffit_cif_v2_to_v1_regex_behavior(monkeypatch): ) assert isinstance(pattern, np.ndarray) assert pattern.shape[0] == 5 + + +def test_structure_cif_for_pdffit_uses_legacy_iucr_tags(): + """Edi structure tags map to the legacy spellings diffpy reads.""" + from easydiffraction.analysis.calculators.pdffit import _structure_cif_for_pdffit + from easydiffraction.datablocks.structure.item.base import Structure + + structure = Structure(name='ni') + structure.space_group.name_h_m = 'F m -3 m' + structure.cell.length_a = 3.52 + structure.atom_sites.create( + id='Ni', + type_symbol='Ni', + fract_x=0, + fract_y=0, + fract_z=0, + occupancy=1.0, + adp_iso=0.42, + ) + + cif = _structure_cif_for_pdffit(structure) + + assert '_atom_site.label' in cif + assert '_atom_site.id' not in cif + assert '_space_group.name_H-M_alt' in cif + assert '_space_group.name_h_m' not in cif + # ADPs are normalized to the U convention for diffpy. + assert '_atom_site.U_iso_or_equiv' in cif + assert '_atom_site.B_iso_or_equiv' not in cif + assert '_atom_site.adp_iso' not in cif + + +def test_structure_cif_for_pdffit_normalizes_mixed_b_u_iso_adp(): + import math + + from easydiffraction.analysis.calculators.pdffit import _structure_cif_for_pdffit + from easydiffraction.datablocks.structure.item.base import Structure + + structure = Structure(name='mixed') + structure.space_group.name_h_m = 'P 1' + structure.cell.length_a = 5.0 + structure.atom_sites.create( + id='B1', + type_symbol='Si', + fract_x=0, + fract_y=0, + fract_z=0, + adp_type='Biso', + adp_iso=0.8, + ) + structure.atom_sites.create( + id='U1', + type_symbol='O', + fract_x=0.5, + fract_y=0.5, + fract_z=0.5, + adp_type='Uiso', + adp_iso=0.01, + ) + + cif = _structure_cif_for_pdffit(structure) + + # Both rows use the U tag; the Biso value is converted (B / 8π² ≈ + # 0.0101) while the native Uiso value is left as-is. + assert '_atom_site.U_iso_or_equiv' in cif + assert '_atom_site.B_iso_or_equiv' not in cif + assert math.isclose(0.8 / (8.0 * math.pi**2), 0.010132, abs_tol=1e-5) + assert '0.0101' in cif + assert '0.01' in cif + # The live structure is restored to its original B value afterwards. + assert structure.atom_sites['B1'].adp_iso.value == 0.8 diff --git a/tests/unit/easydiffraction/analysis/categories/fit_result/test_lsq.py b/tests/unit/easydiffraction/analysis/categories/fit_result/test_lsq.py index b4ddbf7fc..278f454d3 100644 --- a/tests/unit/easydiffraction/analysis/categories/fit_result/test_lsq.py +++ b/tests/unit/easydiffraction/analysis/categories/fit_result/test_lsq.py @@ -110,8 +110,8 @@ def test_least_squares_fit_result_serializes_only_active_families(): cif_text = fit_result.as_cif - assert '_fit_result.prof_R_factor' not in cif_text - assert '_fit_result.R_factor_all' not in cif_text + assert '_fit_result.prof_r_factor' not in cif_text + assert '_fit_result.r_factor_all' not in cif_text assert '_fit_result.number_restraints' not in cif_text assert '_fit_result.number_constraints' not in cif_text assert '_fit_result.shift_over_su_max' not in cif_text @@ -121,14 +121,14 @@ def test_least_squares_fit_result_serializes_only_active_families(): fit_result._set_number_constraints(1) cif_text = fit_result.as_cif - assert '_fit_result.R_factor_all 0.12' in cif_text + assert '_fit_result.r_factor_all 0.12' in cif_text assert '_fit_result.number_constraints 1' in cif_text - assert '_fit_result.prof_R_factor' not in cif_text + assert '_fit_result.prof_r_factor' not in cif_text fit_result._set_prof_r_factor(0.21) cif_text = fit_result.as_cif - assert '_fit_result.prof_R_factor 0.21' in cif_text + assert '_fit_result.prof_r_factor 0.21' in cif_text def test_least_squares_fit_result_omits_duplicate_exit_reason(): diff --git a/tests/unit/easydiffraction/analysis/categories/minimizer/test_emcee.py b/tests/unit/easydiffraction/analysis/categories/minimizer/test_emcee.py index a3b1bfe59..a1dfb42b5 100644 --- a/tests/unit/easydiffraction/analysis/categories/minimizer/test_emcee.py +++ b/tests/unit/easydiffraction/analysis/categories/minimizer/test_emcee.py @@ -111,7 +111,7 @@ def _update_categories(self) -> None: analysis = SimpleNamespace( fit_parameters=[ SimpleNamespace( - param_unique_name=SimpleNamespace(value='saved.param'), + parameter_unique_name=SimpleNamespace(value='saved.param'), ), ], ) diff --git a/tests/unit/easydiffraction/analysis/categories/software/test_base.py b/tests/unit/easydiffraction/analysis/categories/software/test_base.py index 202b5d938..50e5ac20e 100644 --- a/tests/unit/easydiffraction/analysis/categories/software/test_base.py +++ b/tests/unit/easydiffraction/analysis/categories/software/test_base.py @@ -6,17 +6,21 @@ def test_software_role_serializes_name_version_and_url(): from easydiffraction.analysis.categories.software.base import SoftwareRole + from easydiffraction.analysis.enums import SoftwareRoleEnum - role = SoftwareRole(role_name='calculator', description='Calculator') + role = SoftwareRole(SoftwareRoleEnum.CALCULATOR) role.name = 'cryspy' role.version = '1.0' role.url = 'https://example.invalid/cryspy' assert [parameter.name for parameter in role.parameters] == [ - 'calculator_name', - 'calculator_version', - 'calculator_url', + 'id', + 'name', + 'version', + 'url', ] - assert '_software.calculator_name cryspy' in role.as_cif - assert '_software.calculator_version 1.0' in role.as_cif - assert '_software.calculator_url https://example.invalid/cryspy' in role.as_cif + assert role.id.value == 'calculator' + assert '_software.id calculator' in role.as_cif + assert '_software.name cryspy' in role.as_cif + assert '_software.version 1.0' in role.as_cif + assert '_software.url https://example.invalid/cryspy' in role.as_cif diff --git a/tests/unit/easydiffraction/analysis/categories/software/test_default.py b/tests/unit/easydiffraction/analysis/categories/software/test_default.py index ebef86b30..125641b62 100644 --- a/tests/unit/easydiffraction/analysis/categories/software/test_default.py +++ b/tests/unit/easydiffraction/analysis/categories/software/test_default.py @@ -7,18 +7,21 @@ def test_software_category_exposes_roles_and_timestamp(): from easydiffraction.analysis.categories.software.base import SoftwareRole from easydiffraction.analysis.categories.software.default import Software + from easydiffraction.analysis.enums import SoftwareRoleEnum software = Software() - software.framework.name = 'EasyDiffraction' - software.calculator.name = 'cryspy' - software.minimizer.name = 'lmfit' - software.timestamp = '2026-05-29T12:00:00+00:00' + software[SoftwareRoleEnum.FRAMEWORK.value].name = 'EasyDiffraction' + software[SoftwareRoleEnum.CALCULATOR.value].name = 'cryspy' + software[SoftwareRoleEnum.MINIMIZER.value].name = 'lmfit' - assert isinstance(software.framework, SoftwareRole) - assert isinstance(software.calculator, SoftwareRole) - assert isinstance(software.minimizer, SoftwareRole) - assert len(software.parameters) == 10 - assert '_software.framework_name EasyDiffraction' in software.as_cif - assert '_software.calculator_name cryspy' in software.as_cif - assert '_software.minimizer_name lmfit' in software.as_cif - assert '_software.timestamp 2026-05-29T12:00:00+00:00' in software.as_cif + assert isinstance(software[SoftwareRoleEnum.FRAMEWORK.value], SoftwareRole) + assert isinstance(software[SoftwareRoleEnum.CALCULATOR.value], SoftwareRole) + assert isinstance(software[SoftwareRoleEnum.MINIMIZER.value], SoftwareRole) + assert software.names == ['framework', 'calculator', 'minimizer'] + assert len(software.parameters) == 12 + assert software.has_provenance() + cif_text = software.as_cif + assert '_software.id' in cif_text + assert 'framework EasyDiffraction' in cif_text + assert 'calculator cryspy' in cif_text + assert 'minimizer lmfit' in cif_text diff --git a/tests/unit/easydiffraction/analysis/categories/test_aliases.py b/tests/unit/easydiffraction/analysis/categories/test_aliases.py index 81a936e53..e3eeb74a7 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_aliases.py +++ b/tests/unit/easydiffraction/analysis/categories/test_aliases.py @@ -5,23 +5,23 @@ from easydiffraction.analysis.categories.aliases import Aliases from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec def test_alias_creation_and_collection(): p1 = Parameter( name='adp_iso', value_spec=AttributeSpec(default=0.5), - cif_handler=CifHandler(names=['_atom_site.adp_iso']), + tags=TagSpec(edi_names=['_atom_site.adp_iso']), ) a = Alias() - a.label = 'x' + a.id = 'x' a._set_param(p1) - assert a.label.value == 'x' + assert a.id.value == 'x' assert a.param is p1 coll = Aliases() - coll.create(label='x', param=p1) + coll.create(id='x', param=p1) # Collections index by entry name; check via names or direct indexing assert 'x' in coll.names assert coll['x'].param is p1 - assert coll['x'].param_unique_name.value == p1.unique_name + assert coll['x'].parameter_unique_name.value == p1.unique_name diff --git a/tests/unit/easydiffraction/analysis/categories/test_fit_parameters.py b/tests/unit/easydiffraction/analysis/categories/test_fit_parameters.py index 63b4cfa85..820881998 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_fit_parameters.py +++ b/tests/unit/easydiffraction/analysis/categories/test_fit_parameters.py @@ -20,7 +20,7 @@ def _fit_parameters_with_parent_result_kind(result_kind: str): collection = FitParameters() collection.create( - param_unique_name='cosio.cell.length_a', + parameter_unique_name='cosio.cell.length_a', fit_min=-1.0, fit_max=1.0, start_value=10.3, @@ -42,7 +42,7 @@ def test_fit_parameters_cif_omits_posterior_columns_for_deterministic_result(): cif_text = collection.as_cif assert '_fit_parameter.start_value' in cif_text - assert '_fit_parameter.fit_bounds_uncertainty_multiplier' not in cif_text + assert '_fit_parameter.bounds_uncertainty_multiplier' not in cif_text assert '_fit_parameter.posterior_median' not in cif_text assert '_fit_parameter.posterior_effective_sample_size_bulk' not in cif_text @@ -51,11 +51,11 @@ def test_fit_parameters_cif_keeps_uncertainty_multiplier_when_populated(): from easydiffraction.analysis.enums import FitResultKindEnum collection = _fit_parameters_with_parent_result_kind(FitResultKindEnum.DETERMINISTIC.value) - collection['cosio.cell.length_a']._set_fit_bounds_uncertainty_multiplier(4.0) + collection['cosio.cell.length_a']._set_bounds_uncertainty_multiplier(4.0) cif_text = collection.as_cif - assert '_fit_parameter.fit_bounds_uncertainty_multiplier' in cif_text + assert '_fit_parameter.bounds_uncertainty_multiplier' in cif_text assert '4.' in cif_text diff --git a/tests/unit/easydiffraction/analysis/categories/test_fit_state.py b/tests/unit/easydiffraction/analysis/categories/test_fit_state.py index 085870671..1258ab2ee 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_fit_state.py +++ b/tests/unit/easydiffraction/analysis/categories/test_fit_state.py @@ -20,18 +20,18 @@ def test_fit_parameter_collection_serializes_expected_tags_and_values(): collection = FitParameters() collection.create( - param_unique_name='lbco.cell.length_a', + parameter_unique_name='lbco.cell.length_a', fit_min=3.88, fit_max=3.90, - fit_bounds_uncertainty_multiplier=4.0, + bounds_uncertainty_multiplier=4.0, start_value=3.89, start_uncertainty=0.01, ) cif_text = collection.as_cif - assert '_fit_parameter.param_unique_name' in cif_text - assert '_fit_parameter.fit_bounds_uncertainty_multiplier' in cif_text + assert '_fit_parameter.parameter_unique_name' in cif_text + assert '_fit_parameter.bounds_uncertainty_multiplier' in cif_text assert 'lbco.cell.length_a' in cif_text @@ -61,22 +61,22 @@ def test_fit_parameter_correlations_normalize_pair_order_and_replace_duplicate_i correlations = FitParameterCorrelations() correlations.create( source_kind='posterior', - param_unique_name_i='z.param', - param_unique_name_j='a.param', + parameter_unique_name_i='z.param', + parameter_unique_name_j='a.param', correlation=0.87, id='1', ) correlations.create( source_kind='posterior', - param_unique_name_i='b.param', - param_unique_name_j='c.param', + parameter_unique_name_i='b.param', + parameter_unique_name_j='c.param', correlation=0.55, id='1', ) assert len(correlations) == 1 - assert correlations['1'].param_unique_name_i.value == 'b.param' - assert correlations['1'].param_unique_name_j.value == 'c.param' + assert correlations['1'].parameter_unique_name_i.value == 'b.param' + assert correlations['1'].parameter_unique_name_j.value == 'c.param' def test_fit_parameter_correlations_rebuild_index_from_cif(): @@ -88,8 +88,8 @@ def test_fit_parameter_correlations_rebuild_index_from_cif(): loop_ _fit_parameter_correlation.id _fit_parameter_correlation.source_kind -_fit_parameter_correlation.param_unique_name_i -_fit_parameter_correlation.param_unique_name_j +_fit_parameter_correlation.parameter_unique_name_i +_fit_parameter_correlation.parameter_unique_name_j _fit_parameter_correlation.correlation 2 posterior hrpt.scale lbco.cell.length_a 0.42 """ @@ -108,7 +108,7 @@ def test_fit_parameter_posterior_summary_serializes_expected_tags(): collection = FitParameters() collection.create( - param_unique_name='lbco.cell.length_a', + parameter_unique_name='lbco.cell.length_a', fit_min=3.88, fit_max=3.90, ) @@ -165,7 +165,7 @@ def test_fit_parameter_posteriors_preserve_row_order_from_cif(): cif_text = """data_fit_state loop_ -_fit_parameter.param_unique_name +_fit_parameter.parameter_unique_name _fit_parameter.posterior_best_sample_value _fit_parameter.posterior_median _fit_parameter.posterior_uncertainty @@ -183,7 +183,7 @@ def test_fit_parameter_posteriors_preserve_row_order_from_cif(): posteriors = FitParameters() posteriors.from_cif(document.sole_block()) - assert [row.param_unique_name.value for row in posteriors] == [ + assert [row.parameter_unique_name.value for row in posteriors] == [ 'second.param', 'first.param', ] diff --git a/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py b/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py index bba7ee45b..9aef5c008 100644 --- a/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py +++ b/tests/unit/easydiffraction/analysis/fit_helpers/test_reporting.py @@ -85,6 +85,7 @@ def __init__(self): self.uncertainty = 0.05 self.name = 'a' self.units = 'angstrom_squared' + self.url = 'https://example.test/docs/a' def resolve_display_units(self, context): assert context == 'gui' @@ -138,3 +139,8 @@ def fake_render_table(*, columns_headers, columns_alignment, columns_data): '20.00 % ↑', ] ] + from easydiffraction.display.links import TableLink + + parameter_cell = captured['columns_data'][0][3] + assert isinstance(parameter_cell, TableLink) + assert parameter_cell.url == 'https://example.test/docs/a' diff --git a/tests/unit/easydiffraction/analysis/test_analysis.py b/tests/unit/easydiffraction/analysis/test_analysis.py index 59fd75075..39b3ee65f 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis.py +++ b/tests/unit/easydiffraction/analysis/test_analysis.py @@ -24,7 +24,7 @@ def names(self): class P: experiments = ExpCol(names) structures = object() - info = SimpleNamespace(path=None) + metadata = SimpleNamespace(path=None) _varname = 'proj' return P() @@ -33,12 +33,12 @@ class P: def _make_parameter(name, value): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec return Parameter( name=name, value_spec=AttributeSpec(default=value), - cif_handler=CifHandler(names=[f'_{name}.value']), + tags=TagSpec(edi_names=[f'_{name}.value']), ) @@ -56,7 +56,7 @@ def values(self): return SimpleNamespace( structures=ParamContainer(parameters), experiments=Experiments([]), - info=SimpleNamespace(path=None), + metadata=SimpleNamespace(path=None), _varname='proj', ) @@ -92,14 +92,12 @@ def test_analysis_extension_descriptors_keep_save_tags_and_iucr_names(): analysis = Analysis(project=_make_project_with_names([])) calculator = Calculator(type='cryspy') - assert analysis.minimizer._type._cif_handler.names == ['_minimizer.type'] - assert analysis.minimizer._type._cif_handler.iucr_name == '_easydiffraction_minimizer.type' - assert analysis.fitting_mode._type._cif_handler.names == ['_fitting_mode.type'] - assert ( - analysis.fitting_mode._type._cif_handler.iucr_name == '_easydiffraction_fitting_mode.type' - ) - assert calculator._type._cif_handler.names == ['_calculator.type'] - assert calculator._type._cif_handler.iucr_name == '_easydiffraction_calculator.type' + assert analysis.minimizer._type._tags.edi_names == ['_minimizer.type'] + assert analysis.minimizer._type._tags.cif_name == '_easydiffraction_minimizer.type' + assert analysis.fitting_mode._type._tags.edi_names == ['_fitting_mode.type'] + assert analysis.fitting_mode._type._tags.cif_name == '_easydiffraction_fitting_mode.type' + assert calculator._type._tags.edi_names == ['_calculator.type'] + assert calculator._type._tags.cif_name == '_easydiffraction_calculator.type' def test_fit_mode_category_and_joint_fit(monkeypatch, capsys): @@ -200,7 +198,7 @@ def test_undo_fit_restores_scalars_and_clears_fit_outputs(): ): parameter.fit_min = 3.5 parameter.fit_max = 4.5 - parameter._set_fit_bounds_uncertainty_multiplier(4.0) + parameter._set_bounds_uncertainty_multiplier(4.0) summary = PosteriorParameterSummary( unique_name=parameter.unique_name, display_name=parameter.name, @@ -214,10 +212,10 @@ def test_undo_fit_restores_scalars_and_clears_fit_outputs(): ) parameter._set_posterior(summary) analysis.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=parameter.fit_min, fit_max=parameter.fit_max, - fit_bounds_uncertainty_multiplier=4.0, + bounds_uncertainty_multiplier=4.0, start_value=start_value, start_uncertainty=start_uncertainty, ) @@ -227,8 +225,8 @@ def test_undo_fit_restores_scalars_and_clears_fit_outputs(): analysis.fit_result._set_success(value=True) analysis.fit_parameter_correlations.create( source_kind='deterministic', - param_unique_name_i=length_a.unique_name, - param_unique_name_j=length_b.unique_name, + parameter_unique_name_i=length_a.unique_name, + parameter_unique_name_j=length_b.unique_name, correlation=0.25, ) analysis._persisted_fit_state_sidecar = {'posterior': {'draws': object()}} @@ -266,7 +264,7 @@ def test_undo_fit_second_call_is_noop(monkeypatch): analysis = Analysis(project=project) parameter.value = 1.5 analysis.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=0.0, fit_max=2.0, start_value=1.0, @@ -310,7 +308,7 @@ def test_undo_fit_loaded_no_movement_fit_is_not_noop(): project = _make_project_with_parameters([parameter]) analysis = Analysis(project=project) analysis.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=0.0, fit_max=2.0, start_value=1.0, @@ -507,7 +505,7 @@ def test_fit_resume_defaults_extra_steps_to_sampling_steps(monkeypatch, tmp_path analysis = Analysis(project=_make_project_with_names(['e1'])) analysis.project.verbosity = SimpleNamespace(fit=SimpleNamespace(value='silent')) - analysis.project.info = SimpleNamespace(path=tmp_path) + analysis.project.metadata = SimpleNamespace(path=tmp_path) analysis.minimizer.type = 'emcee' analysis.minimizer.sampling_steps = 123 captured: dict[str, object] = {} @@ -529,7 +527,7 @@ def test_fit_resume_preserves_explicit_extra_steps(monkeypatch, tmp_path): analysis = Analysis(project=_make_project_with_names(['e1'])) analysis.project.verbosity = SimpleNamespace(fit=SimpleNamespace(value='silent')) - analysis.project.info = SimpleNamespace(path=tmp_path) + analysis.project.metadata = SimpleNamespace(path=tmp_path) analysis.minimizer.type = 'emcee' analysis.minimizer.sampling_steps = 123 captured: dict[str, object] = {} @@ -555,7 +553,7 @@ def test_fit_resume_missing_sidecar_warns_and_starts_fresh( analysis = Analysis(project=_make_project_with_names(['e1'])) analysis.project.verbosity = SimpleNamespace(fit=SimpleNamespace(value='silent')) - analysis.project.info = SimpleNamespace(path=tmp_path) + analysis.project.metadata = SimpleNamespace(path=tmp_path) analysis.minimizer.type = 'emcee' captured: dict[str, object] = {} warnings: list[str] = [] @@ -786,7 +784,7 @@ def test_run_sequential_sets_mode_and_saves_project(monkeypatch, tmp_path): from easydiffraction.analysis.analysis import Analysis project = SimpleNamespace( - info=SimpleNamespace(path=tmp_path), + metadata=SimpleNamespace(path=tmp_path), experiments=SimpleNamespace(values=list), save_calls=0, _varname='proj', @@ -872,7 +870,7 @@ def _stub(label): class _Project: structures = [_stub('structure')] experiments = [_stub('experiment')] - info = SimpleNamespace(path=None) + metadata = SimpleNamespace(path=None) _varname = 'proj' Analysis.calculate(SimpleNamespace(project=_Project())) diff --git a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py index 94bb70c0d..3e0da86b4 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis_access_params.py +++ b/tests/unit/easydiffraction/analysis/test_analysis_access_params.py @@ -17,7 +17,7 @@ def _make_param( from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec display_handler = ( DisplayHandler(display_units=display_units) if display_units is not None else None @@ -26,7 +26,7 @@ def _make_param( name=name, units=units, value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=[f'_{cat}.{name}']), + tags=TagSpec(edi_names=[f'_{cat}.{name}']), display_handler=display_handler, ) param.value = val @@ -48,12 +48,12 @@ def _make_int_descriptor(db, cat, entry, name, val): from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import IntegerDescriptor - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec descriptor = IntegerDescriptor( name=name, value_spec=AttributeSpec(default=None, allow_none=True), - cif_handler=CifHandler(names=[f'_{cat}.{name}']), + tags=TagSpec(edi_names=[f'_{cat}.{name}']), display_handler=DisplayHandler(), ) descriptor.value = val @@ -66,6 +66,7 @@ def _make_int_descriptor(db, cat, entry, name, val): def test_how_to_access_parameters_prints_paths_and_uids(capsys, monkeypatch): import easydiffraction.analysis.analysis as analysis_mod from easydiffraction.analysis.analysis import Analysis + from easydiffraction.display.links import TableLink p1 = _make_param('db1', 'catA', '', 'alpha', 1.0) p2 = _make_param('db2', 'catB', 'row1', 'beta', 2.0) @@ -100,6 +101,9 @@ def fake_render_table(**kwargs): data = captured.get('columns_data') or [] assert 'How to Access in Python Code' in headers + assert isinstance(data[0][3], TableLink) + assert data[0][3] == 'alpha' + assert data[0][3].url.endswith('/user-guide/parameters/catA/#cata-alpha') # Flatten rows to strings for simple membership checks flat_rows = [' '.join(map(str, row)) for row in data] @@ -108,29 +112,56 @@ def fake_render_table(**kwargs): assert any("proj.structures['db1'].catA.alpha" in r for r in flat_rows) assert any("proj.experiments['db2'].catB['row1'].beta" in r for r in flat_rows) - # Now check CIF unique identifiers via the new API + # Now check constraint unique identifiers via the new API captured2 = {} def fake_render_table2(**kwargs): captured2.update(kwargs) monkeypatch.setattr(analysis_mod, 'render_table', fake_render_table2) - a.display.parameter_cif_uids() + a.display.parameter_uids() headers2 = captured2.get('columns_headers') or [] data2 = captured2.get('columns_data') or [] - assert 'Unique Identifier for CIF Constraints' in headers2 + assert 'Unique Identifier for Constraints' in headers2 + assert isinstance(data2[0][3], TableLink) + assert data2[0][3] == 'alpha' + assert data2[0][3].url.endswith('/user-guide/parameters/catA/#cata-alpha') flat_rows2 = [' '.join(map(str, row)) for row in data2] # Unique names are datablock.category[.entry].parameter assert any('db1 catA alpha' in r.replace('.', ' ') for r in flat_rows2) assert any('db2 catB row1 beta' in r.replace('.', ' ') for r in flat_rows2) + # Edi persistence tags + captured3 = {} + + def fake_render_table3(**kwargs): + captured3.update(kwargs) + + monkeypatch.setattr(analysis_mod, 'render_table', fake_render_table3) + a.display.parameter_edi_tags() + assert 'Edi Tag' in (captured3.get('columns_headers') or []) + edi_rows = [' '.join(map(str, row)) for row in captured3.get('columns_data') or []] + assert any('_catA.alpha' in r for r in edi_rows) + + # Report CIF tags + captured4 = {} + + def fake_render_table4(**kwargs): + captured4.update(kwargs) + + monkeypatch.setattr(analysis_mod, 'render_table', fake_render_table4) + a.display.parameter_cif_tags() + assert 'CIF Tag' in (captured4.get('columns_headers') or []) + cif_rows = [' '.join(map(str, row)) for row in captured4.get('columns_data') or []] + assert any('_catA.alpha' in r for r in cif_rows) + def test_how_to_access_parameters_skips_large_loop_categories(capsys, monkeypatch): import easydiffraction.analysis.analysis as analysis_mod from easydiffraction.analysis.analysis import Analysis visible = _make_param('db1', 'catA', '', 'alpha', 1.0) - data_param = _make_param('db2', 'pd_data', '1', 'intensity_meas', 2.0) + data_param = _make_param('db2', 'data', '1', 'intensity_meas', 2.0) refln_param = _make_param('db2', 'refln', '1', 'f_calc', 3.0) class Coll: @@ -157,16 +188,16 @@ def fake_render_table(**kwargs): flat_rows = [' '.join(map(str, row)) for row in captured.get('columns_data') or []] assert any("proj.structures['db1'].catA.alpha" in row for row in flat_rows) - assert not any('pd_data' in row for row in flat_rows) + assert not any('data' in row for row in flat_rows) assert not any('refln' in row for row in flat_rows) -def test_parameter_cif_uids_skips_large_loop_categories(monkeypatch): +def test_parameter_uids_skips_large_loop_categories(monkeypatch): import easydiffraction.analysis.analysis as analysis_mod from easydiffraction.analysis.analysis import Analysis visible = _make_param('db1', 'catA', '', 'alpha', 1.0) - data_param = _make_param('db2', 'pd_data', '1', 'intensity_meas', 2.0) + data_param = _make_param('db2', 'data', '1', 'intensity_meas', 2.0) refln_param = _make_param('db2', 'refln', '1', 'f_calc', 3.0) class Coll: @@ -186,11 +217,11 @@ def fake_render_table(**kwargs): captured.update(kwargs) monkeypatch.setattr(analysis_mod, 'render_table', fake_render_table) - Analysis(Project()).display.parameter_cif_uids() + Analysis(Project()).display.parameter_uids() flat_rows = [' '.join(map(str, row)) for row in captured.get('columns_data') or []] assert any('db1 catA alpha' in row.replace('.', ' ') for row in flat_rows) - assert not any('pd_data' in row for row in flat_rows) + assert not any('data' in row for row in flat_rows) assert not any('refln' in row for row in flat_rows) @@ -200,7 +231,7 @@ def test_all_params_skips_large_loop_categories(monkeypatch): structure_param = _make_param('s1', 'cell', '', 'length_a', 4.0) visible_experiment_param = _make_param('e1', 'instrument', '', 'wavelength', 1.5) - data_param = _make_param('e1', 'pd_data', '1', 'intensity_meas', 10.0) + data_param = _make_param('e1', 'data', '1', 'intensity_meas', 10.0) refln_param = _make_param('e1', 'refln', '1', 'f_calc', 12.0) class Coll: @@ -272,6 +303,12 @@ def render(self, df): Analysis(Project()).display.all_params() structure_df = rendered[0] + from easydiffraction.display.links import TableLink + + parameter_cell = structure_df['parameter', 'left'].iloc[0] + assert isinstance(parameter_cell, TableLink) + assert str(parameter_cell) == 'length_a' + assert parameter_cell.url.endswith('/user-guide/parameters/structure/cell/#cell-length-a') assert structure_df['parameter', 'left'].tolist() == ['length_a', 'length_b', 'length_c'] assert structure_df['fittable', 'left'].tolist() == [True, False, False] @@ -424,7 +461,7 @@ def render(self, df): assert int(structure_df.isna().sum().sum()) == 0 -def test_how_to_access_and_cif_uids_include_integer_descriptors(monkeypatch): +def test_how_to_access_and_uids_include_integer_descriptors(monkeypatch): import easydiffraction.analysis.analysis as analysis_mod from easydiffraction.analysis.analysis import Analysis @@ -456,7 +493,7 @@ def fake_render_table(**kwargs): assert any("proj.structures['lbco'].atom_site['O'].multiplicity" in row for row in access_rows) captured.clear() - a.display.parameter_cif_uids() + a.display.parameter_uids() uid_rows = [' '.join(map(str, row)) for row in captured.get('columns_data') or []] assert any('multiplicity' in row for row in uid_rows) diff --git a/tests/unit/easydiffraction/analysis/test_analysis_coverage.py b/tests/unit/easydiffraction/analysis/test_analysis_coverage.py index a0abd00e5..99a133605 100644 --- a/tests/unit/easydiffraction/analysis/test_analysis_coverage.py +++ b/tests/unit/easydiffraction/analysis/test_analysis_coverage.py @@ -10,12 +10,12 @@ def _make_parameter(name, value): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec return Parameter( name=name, value_spec=AttributeSpec(default=value), - cif_handler=CifHandler(names=[f'_{name}.value']), + tags=TagSpec(edi_names=[f'_{name}.value']), ) @@ -37,7 +37,7 @@ def values(self): return SimpleNamespace( structures=StructureColl(structure_params), experiments=ExperimentColl(experiment_params), - info=SimpleNamespace(path=None), + metadata=SimpleNamespace(path=None), _varname='proj', ) @@ -296,7 +296,7 @@ class Experiments: def __getitem__(self, name): del name - return SimpleNamespace(type='powder') + return SimpleNamespace(experiment_type='powder') project = SimpleNamespace( experiments=Experiments(), @@ -522,17 +522,17 @@ def test_set_software_role_assigns_fields(self): from easydiffraction.analysis.analysis import Analysis a = Analysis(project=_make_project()) - Analysis._set_software_role(a.software.framework, ('EasyDiffraction', '9.9', 'url')) - assert a.software.framework.name.value == 'EasyDiffraction' - assert a.software.framework.version.value == '9.9' - assert a.software.framework.url.value == 'url' + Analysis._set_software_role(a.software['framework'], ('EasyDiffraction', '9.9', 'url')) + assert a.software['framework'].name.value == 'EasyDiffraction' + assert a.software['framework'].version.value == '9.9' + assert a.software['framework'].url.value == 'url' def test_has_software_provenance_tracks_stamping(self): from easydiffraction.analysis.analysis import Analysis a = Analysis(project=_make_project()) assert a._has_software_provenance() is False - a.software.framework.name = 'EasyDiffraction' + a.software['framework'].name = 'EasyDiffraction' assert a._has_software_provenance() is True @@ -642,10 +642,12 @@ def test_is_powder_fit_detects_powder(self): from easydiffraction.datablocks.experiment.item.enums import SampleFormEnum powder = SimpleNamespace( - type=SimpleNamespace(sample_form=SimpleNamespace(value=SampleFormEnum.POWDER.value)) + experiment_type=SimpleNamespace( + sample_form=SimpleNamespace(value=SampleFormEnum.POWDER.value) + ) ) single = SimpleNamespace( - type=SimpleNamespace( + experiment_type=SimpleNamespace( sample_form=SimpleNamespace(value=SampleFormEnum.SINGLE_CRYSTAL.value) ) ) @@ -1074,7 +1076,7 @@ def test_absolute_data_dir_returned_directly(self, tmp_path): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=None), + metadata=SimpleNamespace(path=None), _varname='proj', ) a = Analysis(project=project) @@ -1089,7 +1091,7 @@ def test_relative_data_dir_requires_saved_project(self): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=None), + metadata=SimpleNamespace(path=None), _varname='proj', ) a = Analysis(project=project) @@ -1103,7 +1105,7 @@ def test_relative_data_dir_joined_to_project_path(self, tmp_path): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=tmp_path), + metadata=SimpleNamespace(path=tmp_path), _varname='proj', ) a = Analysis(project=project) @@ -1123,7 +1125,7 @@ def test_no_project_path_returns_false(self): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=None), + metadata=SimpleNamespace(path=None), _varname='proj', ) a = Analysis(project=project) @@ -1135,7 +1137,7 @@ def test_missing_sidecar_file_returns_false(self, tmp_path): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=tmp_path), + metadata=SimpleNamespace(path=tmp_path), _varname='proj', ) a = Analysis(project=project) @@ -1157,7 +1159,7 @@ def test_sidecar_with_positive_iteration_returns_true(self, tmp_path): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=tmp_path), + metadata=SimpleNamespace(path=tmp_path), _varname='proj', ) a = Analysis(project=project) @@ -1179,7 +1181,7 @@ def test_sidecar_with_zero_iteration_returns_false(self, tmp_path): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=tmp_path), + metadata=SimpleNamespace(path=tmp_path), _varname='proj', ) a = Analysis(project=project) @@ -1199,7 +1201,7 @@ def test_sidecar_without_chain_group_returns_false(self, tmp_path): project = SimpleNamespace( experiments=SimpleNamespace(values=list), structures=object(), - info=SimpleNamespace(path=tmp_path), + metadata=SimpleNamespace(path=tmp_path), _varname='proj', ) a = Analysis(project=project) @@ -1227,7 +1229,7 @@ def names(self): return SimpleNamespace( experiments=Experiments(names), structures=object(), - info=SimpleNamespace(path=None), + metadata=SimpleNamespace(path=None), _varname='proj', ) @@ -1589,7 +1591,7 @@ def _analysis_with_posterior_row(self): a = Analysis(project=_make_project()) a.fit_parameters.create( - param_unique_name='alpha', + parameter_unique_name='alpha', fit_min=0.0, fit_max=2.0, start_value=1.0, @@ -1671,7 +1673,9 @@ def _powder_experiment(self, *, meas, calc, su): return SimpleNamespace( data=data, - type=SimpleNamespace(sample_form=SimpleNamespace(value=SampleFormEnum.POWDER.value)), + experiment_type=SimpleNamespace( + sample_form=SimpleNamespace(value=SampleFormEnum.POWDER.value) + ), peak=SimpleNamespace(type=SimpleNamespace(value='gaussian')), background=SimpleNamespace(type=SimpleNamespace(value='chebyshev')), parameters=[], @@ -1765,7 +1769,7 @@ def _identity_param(*, datablock, category, entry, name): category_code=category, category_entry_name=entry, ), - _cif_handler=SimpleNamespace(uid=f'{category}_{name}_uid'), + _tags=SimpleNamespace(uid=f'{category}_{name}_uid'), ) @@ -1802,15 +1806,15 @@ def test_how_to_access_parameters_builds_code_paths(self, capsys, monkeypatch): # Category-entry name is rendered for looped (collection) categories. assert "proj.experiments['hrpt'].atom_site['Ba'].fract_x" in access_codes - def test_parameter_cif_uids_warns_when_empty(self, capsys): + def test_parameter_uids_warns_when_empty(self, capsys): from easydiffraction.analysis.analysis import Analysis a = Analysis(project=_make_project()) - a.display.parameter_cif_uids() + a.display.parameter_uids() out = capsys.readouterr().out assert 'No parameters found' in out - def test_parameter_cif_uids_lists_handler_uids(self, capsys, monkeypatch): + def test_parameter_uids_lists_handler_uids(self, capsys, monkeypatch): import easydiffraction.analysis.analysis as mod from easydiffraction.analysis.analysis import Analysis @@ -1822,10 +1826,10 @@ def test_parameter_cif_uids_lists_handler_uids(self, capsys, monkeypatch): captured = {} monkeypatch.setattr(mod, 'render_table', lambda **kwargs: captured.update(kwargs)) - a.display.parameter_cif_uids() + a.display.parameter_uids() out = capsys.readouterr().out - assert 'CIF unique identifiers' in out + assert 'unique identifiers for constraints' in out uids = [row[-1] for row in captured['columns_data']] assert 'cell_length_a_uid' in uids @@ -1843,10 +1847,10 @@ def _analysis_with_persisted_param(self, parameter): project = _make_project_with_parameters([parameter]) a = Analysis(project=project) a.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=3.5, fit_max=4.5, - fit_bounds_uncertainty_multiplier=4.0, + bounds_uncertainty_multiplier=4.0, start_value=3.90, start_uncertainty=0.02, ) @@ -1885,7 +1889,7 @@ def test_restore_bounds_warns_for_unknown_parameter(self, monkeypatch): a = Analysis(project=_make_project_with_parameters([])) a.fit_parameters.create( - param_unique_name='ghost', + parameter_unique_name='ghost', fit_min=0.0, fit_max=1.0, start_value=0.5, @@ -1910,7 +1914,7 @@ def test_ordered_restored_parameter_names_follows_row_order(self): a = Analysis(project=_make_project_with_parameters([])) for name in ('beta', 'alpha'): a.fit_parameters.create( - param_unique_name=name, + parameter_unique_name=name, fit_min=0.0, fit_max=1.0, start_value=0.5, @@ -1922,7 +1926,7 @@ def test_restored_fit_parameters_filters_to_live_parameters(self): a = self._analysis_with_persisted_param(parameter) # Add a persisted row with no matching live parameter. a.fit_parameters.create( - param_unique_name='ghost', + parameter_unique_name='ghost', fit_min=0.0, fit_max=1.0, start_value=0.5, @@ -1951,7 +1955,7 @@ def test_restore_rebuilds_deterministic_results(self): parameter = _make_parameter('length_a', 3.90) a = Analysis(project=_make_project_with_parameters([parameter])) a.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=3.5, fit_max=4.5, start_value=3.90, @@ -1998,7 +2002,7 @@ def test_fit_results_property_restores_on_first_access(self): parameter = _make_parameter('length_a', 3.90) a = Analysis(project=_make_project_with_parameters([parameter])) a.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=3.5, fit_max=4.5, start_value=3.90, @@ -2027,7 +2031,7 @@ def test_restore_rebuilds_bayesian_results(self): a.minimizer.type = 'bumps (dream)' a.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=0.0, fit_max=2.0, start_value=1.0, @@ -2261,8 +2265,8 @@ def test_pair_cache_orders_names_and_stores_contours(self): assert payload pair = payload['1'] # Names are ordered alphabetically for the pair (alpha before beta). - assert pair['param_unique_name_x'] == 'alpha' - assert pair['param_unique_name_y'] == 'beta' + assert pair['parameter_unique_name_x'] == 'alpha' + assert pair['parameter_unique_name_y'] == 'beta' assert pair['contour_levels'].size > 0 def test_pair_cache_single_parameter_is_empty(self): @@ -2422,7 +2426,7 @@ def test_updates_live_parameter_and_stores_correlations(self): for name in (alpha.unique_name, beta.unique_name): a.fit_parameters.create( - param_unique_name=name, + parameter_unique_name=name, fit_min=0.0, fit_max=3.0, start_value=1.0, @@ -2482,7 +2486,7 @@ def _fit_project(*, structures, experiments, path=None, verbosity_value='silent' return SimpleNamespace( structures=structures, experiments=experiments, - info=SimpleNamespace(path=path), + metadata=SimpleNamespace(path=path), verbosity=SimpleNamespace(fit=SimpleNamespace(value=verbosity_value)), _varname='proj', ) diff --git a/tests/unit/easydiffraction/analysis/test_fitting_coverage.py b/tests/unit/easydiffraction/analysis/test_fitting_coverage.py index e83589766..926da27f7 100644 --- a/tests/unit/easydiffraction/analysis/test_fitting_coverage.py +++ b/tests/unit/easydiffraction/analysis/test_fitting_coverage.py @@ -184,12 +184,12 @@ def _make_fitter_with_dummy_minimizer(): def _make_param(name): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec return Parameter( name=name, value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=[f'_param.{name}']), + tags=TagSpec(edi_names=[f'_param.{name}']), ) @@ -301,7 +301,7 @@ def fake_validate(*, params, analysis): # resume path runs the resume validation instead of capturing state. monkeypatch.setattr(fitter, '_validate_resume_parameter_set', fake_validate) - persisted = [SimpleNamespace(param_unique_name=SimpleNamespace(value='a'))] + persisted = [SimpleNamespace(parameter_unique_name=SimpleNamespace(value='a'))] analysis = SimpleNamespace( fit_parameters=persisted, fit_result=SimpleNamespace(), @@ -333,7 +333,7 @@ def test_validate_resume_parameter_set_no_persisted_names_is_noop(): def test_validate_resume_parameter_set_matching_names_ok(): from easydiffraction.analysis.fitting import Fitter - persisted = [SimpleNamespace(param_unique_name=SimpleNamespace(value='a'))] + persisted = [SimpleNamespace(parameter_unique_name=SimpleNamespace(value='a'))] analysis = SimpleNamespace(fit_parameters=persisted) param = SimpleNamespace(unique_name='a') @@ -343,7 +343,7 @@ def test_validate_resume_parameter_set_matching_names_ok(): def test_validate_resume_parameter_set_mismatch_raises(): from easydiffraction.analysis.fitting import Fitter - persisted = [SimpleNamespace(param_unique_name=SimpleNamespace(value='a'))] + persisted = [SimpleNamespace(parameter_unique_name=SimpleNamespace(value='a'))] analysis = SimpleNamespace(fit_parameters=persisted) param = SimpleNamespace(unique_name='b') @@ -363,7 +363,7 @@ def test_set_minimizer_sidecar_path_noop_when_analysis_none(): def test_set_minimizer_sidecar_path_noop_when_no_attribute(): fitter = _make_fitter_with_dummy_minimizer() # Minimizer without _sidecar_path attribute -> early return, no crash. - analysis = SimpleNamespace(project=SimpleNamespace(info=SimpleNamespace(path=None))) + analysis = SimpleNamespace(project=SimpleNamespace(metadata=SimpleNamespace(path=None))) fitter._set_minimizer_sidecar_path(analysis) @@ -373,7 +373,7 @@ def test_set_minimizer_sidecar_path_noop_when_no_attribute(): def test_set_minimizer_sidecar_path_none_when_no_project_path(): fitter = _make_fitter_with_dummy_minimizer() fitter.minimizer._sidecar_path = 'unset' - analysis = SimpleNamespace(project=SimpleNamespace(info=SimpleNamespace(path=None))) + analysis = SimpleNamespace(project=SimpleNamespace(metadata=SimpleNamespace(path=None))) fitter._set_minimizer_sidecar_path(analysis) @@ -384,7 +384,7 @@ def test_set_minimizer_sidecar_path_builds_results_path(tmp_path): fitter = _make_fitter_with_dummy_minimizer() fitter.minimizer._sidecar_path = None analysis = SimpleNamespace( - project=SimpleNamespace(info=SimpleNamespace(path=tmp_path)), + project=SimpleNamespace(metadata=SimpleNamespace(path=tmp_path)), ) fitter._set_minimizer_sidecar_path(analysis) diff --git a/tests/unit/easydiffraction/analysis/test_sequential.py b/tests/unit/easydiffraction/analysis/test_sequential.py index 7274e0ad1..7f45a04f4 100644 --- a/tests/unit/easydiffraction/analysis/test_sequential.py +++ b/tests/unit/easydiffraction/analysis/test_sequential.py @@ -42,7 +42,7 @@ def _minimal_template( structure_cif='', experiment_cif='', initial_params={}, - free_param_unique_names=free_names, + free_parameter_unique_names=free_names, alias_defs=[], constraint_defs=[], constraints_enabled=False, @@ -489,7 +489,7 @@ def test_fields_accessible(self): free_names=['cell.a'], diffrn_fields=['temp'], ) - assert template.free_param_unique_names == ['cell.a'] + assert template.free_parameter_unique_names == ['cell.a'] assert template.diffrn_field_names == ['temp'] assert template.minimizer_tag == 'lmfit' assert template.calculator_tag == 'cryspy' diff --git a/tests/unit/easydiffraction/analysis/test_sequential_coverage.py b/tests/unit/easydiffraction/analysis/test_sequential_coverage.py index bdd15640a..3c8b40870 100644 --- a/tests/unit/easydiffraction/analysis/test_sequential_coverage.py +++ b/tests/unit/easydiffraction/analysis/test_sequential_coverage.py @@ -91,7 +91,7 @@ def _minimal_template(**overrides): 'structure_cif': 'struct', 'experiment_cif': 'expt', 'initial_params': {}, - 'free_param_unique_names': ['cell.a'], + 'free_parameter_unique_names': ['cell.a'], 'alias_defs': [], 'constraint_defs': [], 'constraints_enabled': False, @@ -188,7 +188,7 @@ def test_creates_aliases_and_constraints(self): constraint_calls = [] analysis = SimpleNamespace( aliases=SimpleNamespace( - create=lambda *, label, param: alias_calls.append((label, param)), + create=lambda *, id, param: alias_calls.append((id, param)), ), constraints=SimpleNamespace( create=lambda *, expression: constraint_calls.append(expression), @@ -202,7 +202,7 @@ def test_creates_aliases_and_constraints(self): _apply_constraints( project, - [{'label': 'A', 'param_unique_name': 'cell.a'}], + [{'id': 'A', 'parameter_unique_name': 'cell.a'}], ['A = 2 * B'], ) @@ -213,7 +213,7 @@ def test_skips_alias_for_missing_param(self): alias_calls = [] analysis = SimpleNamespace( aliases=SimpleNamespace( - create=lambda *, label, param: alias_calls.append((label, param)), + create=lambda *, id, param: alias_calls.append((id, param)), ), constraints=SimpleNamespace(create=lambda *, expression: None), ) @@ -225,7 +225,7 @@ def test_skips_alias_for_missing_param(self): _apply_constraints( project, - [{'label': 'A', 'param_unique_name': 'does.not.exist'}], + [{'id': 'A', 'parameter_unique_name': 'does.not.exist'}], [], ) @@ -370,7 +370,7 @@ def test_collects_metrics_and_free_param_values(self, monkeypatch): iterations=17, ) project = self._project([free, fixed], fit_results) - template = _minimal_template(free_param_unique_names=['cell.a']) + template = _minimal_template(free_parameter_unique_names=['cell.a']) result = _collect_results(project, template) @@ -387,7 +387,7 @@ def test_falls_back_to_best_iteration_when_iterations_zero(self, monkeypatch): _patch_variable_types(monkeypatch) fit_results = SimpleNamespace(success=True, reduced_chi_square=2.0, iterations=0) project = self._project([], fit_results, best_iteration=42) - template = _minimal_template(free_param_unique_names=[]) + template = _minimal_template(free_parameter_unique_names=[]) result = _collect_results(project, template) @@ -396,7 +396,7 @@ def test_falls_back_to_best_iteration_when_iterations_zero(self, monkeypatch): def test_handles_missing_fit_results(self, monkeypatch): _patch_variable_types(monkeypatch) project = self._project([], None, best_iteration=7) - template = _minimal_template(free_param_unique_names=[]) + template = _minimal_template(free_parameter_unique_names=[]) result = _collect_results(project, template) @@ -414,9 +414,9 @@ def test_handles_missing_fit_results(self, monkeypatch): class TestFitWorker: def test_success_path_builds_project_and_collects(self, monkeypatch): template = _minimal_template( - free_param_unique_names=['cell.a'], + free_parameter_unique_names=['cell.a'], constraints_enabled=True, - alias_defs=[{'label': 'A', 'param_unique_name': 'cell.a'}], + alias_defs=[{'id': 'A', 'parameter_unique_name': 'cell.a'}], constraint_defs=['A = 1'], ) events = [] @@ -539,7 +539,7 @@ def boom(template_arg, data_path): def test_skips_constraints_when_disabled(self, monkeypatch): template = _minimal_template( constraints_enabled=False, - alias_defs=[{'label': 'A', 'param_unique_name': 'cell.a'}], + alias_defs=[{'id': 'A', 'parameter_unique_name': 'cell.a'}], ) events = [] expt = SimpleNamespace( @@ -621,8 +621,8 @@ def test_builds_snapshot_from_project(self, monkeypatch): temp_desc = _FakeNumericDescriptor(300.0) alias = SimpleNamespace( - label=SimpleNamespace(value='A'), - param_unique_name=SimpleNamespace(value='cell.a'), + id=SimpleNamespace(value='A'), + parameter_unique_name=SimpleNamespace(value='cell.a'), ) constraint = SimpleNamespace(expression=SimpleNamespace(value='A = 1')) extract_rule = SimpleNamespace( @@ -645,9 +645,9 @@ def test_builds_snapshot_from_project(self, monkeypatch): assert template.structure_cif == 'STRUCT_CIF' assert template.experiment_cif == 'EXPT_CIF' # Only the free, non-user-constrained parameter is collected. - assert template.free_param_unique_names == ['cell.a'] + assert template.free_parameter_unique_names == ['cell.a'] assert template.initial_params == {'cell.a': pytest.approx(5.0)} - assert template.alias_defs == [{'label': 'A', 'param_unique_name': 'cell.a'}] + assert template.alias_defs == [{'id': 'A', 'parameter_unique_name': 'cell.a'}] assert template.constraint_defs == ['A = 1'] assert template.constraints_enabled is True assert template.minimizer_tag == 'bumps' @@ -802,7 +802,7 @@ def _precondition_project( return SimpleNamespace( structures=[object()] * n_structures, experiments=[object()] * n_experiments, - info=SimpleNamespace(path=path), + metadata=SimpleNamespace(path=path), parameters=params, ) @@ -854,8 +854,8 @@ def test_user_constrained_params_do_not_count_as_free(self, monkeypatch): class TestSetupCsvAndRecovery: def test_fresh_run_writes_header(self, tmp_path, monkeypatch): - project = SimpleNamespace(info=SimpleNamespace(path=tmp_path)) - template = _minimal_template(free_param_unique_names=['cell.a']) + project = SimpleNamespace(metadata=SimpleNamespace(path=tmp_path)) + template = _minimal_template(free_parameter_unique_names=['cell.a']) monkeypatch.setattr( sequential_mod, @@ -878,9 +878,9 @@ def test_fresh_run_writes_header(self, tmp_path, monkeypatch): assert first_row == header def test_resume_recovers_params_and_skips_header(self, tmp_path, monkeypatch): - project = SimpleNamespace(info=SimpleNamespace(path=tmp_path)) + project = SimpleNamespace(metadata=SimpleNamespace(path=tmp_path)) template = _minimal_template( - free_param_unique_names=['cell.a'], + free_parameter_unique_names=['cell.a'], initial_params={'cell.a': 1.0}, ) recovered = {'cell.a': 9.9} @@ -910,7 +910,7 @@ def test_resume_recovers_params_and_skips_header(self, tmp_path, monkeypatch): assert any('Resuming from CSV' in msg for msg in prints) def test_resume_silent_does_not_print(self, tmp_path, monkeypatch): - project = SimpleNamespace(info=SimpleNamespace(path=tmp_path)) + project = SimpleNamespace(metadata=SimpleNamespace(path=tmp_path)) template = _minimal_template() prints = [] monkeypatch.setattr( @@ -1102,7 +1102,7 @@ def test_empty_results_returns_none(self): class TestRunFitLoopSequential: def test_sequential_path_calls_worker_and_propagates_params(self, monkeypatch, tmp_path): - template = _minimal_template(free_param_unique_names=['cell.a']) + template = _minimal_template(free_parameter_unique_names=['cell.a']) appended = [] worker_templates = [] @@ -1148,7 +1148,7 @@ def test_failed_chunk_does_not_propagate_params(self, monkeypatch, tmp_path): # When a chunk has no successful result, the template's # initial_params must stay unchanged for the next chunk. template = _minimal_template( - free_param_unique_names=['cell.a'], + free_parameter_unique_names=['cell.a'], initial_params={'cell.a': 1.0}, ) seen_initial = [] diff --git a/tests/unit/easydiffraction/core/test_category.py b/tests/unit/easydiffraction/core/test_category.py index 6ae2fbd1c..444da6e02 100644 --- a/tests/unit/easydiffraction/core/test_category.py +++ b/tests/unit/easydiffraction/core/test_category.py @@ -9,7 +9,7 @@ from easydiffraction.core.category import CategoryItem from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class SimpleItem(CategoryItem): @@ -25,7 +25,7 @@ def __init__(self): name='a', description='', value_spec=AttributeSpec(default='_'), - cif_handler=CifHandler(names=['_simple.a']), + tags=TagSpec(edi_names=['_simple.a']), ), ) object.__setattr__( @@ -35,7 +35,7 @@ def __init__(self): name='b', description='', value_spec=AttributeSpec(default='_'), - cif_handler=CifHandler(names=['_simple.b']), + tags=TagSpec(edi_names=['_simple.b']), ), ) @@ -79,7 +79,7 @@ def test_category_item_uses_declared_identity_metadata(): pytest.param( 'easydiffraction.analysis.categories.aliases.default', 'Alias', - 'label', + 'id', 'alias_1', 'alias', id='alias', @@ -111,7 +111,7 @@ def test_category_item_uses_declared_identity_metadata(): pytest.param( 'easydiffraction.datablocks.structure.categories.atom_sites.default', 'AtomSite', - 'label', + 'id', 'Fe1', 'atom_site', id='atom_site', @@ -119,17 +119,17 @@ def test_category_item_uses_declared_identity_metadata(): pytest.param( 'easydiffraction.datablocks.structure.categories.atom_site_aniso.default', 'AtomSiteAniso', - 'label', + 'id', 'Fe1', 'atom_site_aniso', id='atom_site_aniso', ), pytest.param( - 'easydiffraction.datablocks.experiment.categories.linked_phases.default', - 'LinkedPhase', - 'id', + 'easydiffraction.datablocks.experiment.categories.linked_structures.default', + 'LinkedStructure', + 'structure_id', 'phase_1', - 'linked_phases', + 'linked_structure', id='linked_phases', ), pytest.param( @@ -167,25 +167,25 @@ def test_category_item_uses_declared_identity_metadata(): pytest.param( 'easydiffraction.datablocks.experiment.categories.data.bragg_pd', 'PdCwlDataPoint', - 'point_id', + 'id', '1', - 'pd_data', + 'data', id='pd_cwl_data', ), pytest.param( 'easydiffraction.datablocks.experiment.categories.data.bragg_pd', 'PdTofDataPoint', - 'point_id', + 'id', '2', - 'pd_data', + 'data', id='pd_tof_data', ), pytest.param( 'easydiffraction.datablocks.experiment.categories.data.total_pd', 'TotalDataPoint', - 'point_id', + 'id', '3', - 'total_data', + 'data', id='total_data', ), ], diff --git a/tests/unit/easydiffraction/core/test_category_owner.py b/tests/unit/easydiffraction/core/test_category_owner.py index 7b4f5ab1f..4e109746b 100644 --- a/tests/unit/easydiffraction/core/test_category_owner.py +++ b/tests/unit/easydiffraction/core/test_category_owner.py @@ -7,7 +7,7 @@ from easydiffraction.core.category_owner import CategoryOwner from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.io.cif.serialize import category_owner_to_cif @@ -23,7 +23,7 @@ def __init__(self, update_calls: list[tuple[str, bool]] | None = None) -> None: description='Fast category parameter', value_spec=AttributeSpec(default=0.0), units='', - cif_handler=CifHandler(names=['_fast.param']), + tags=TagSpec(edi_names=['_fast.param']), ) @property @@ -47,7 +47,7 @@ def __init__(self, update_calls: list[tuple[str, bool]] | None = None) -> None: description='Slow category parameter', value_spec=AttributeSpec(default=0.0), units='', - cif_handler=CifHandler(names=['_slow.param']), + tags=TagSpec(edi_names=['_slow.param']), ) @property diff --git a/tests/unit/easydiffraction/core/test_datablock.py b/tests/unit/easydiffraction/core/test_datablock.py index 64a2c0c29..74a157115 100644 --- a/tests/unit/easydiffraction/core/test_datablock.py +++ b/tests/unit/easydiffraction/core/test_datablock.py @@ -8,7 +8,7 @@ def test_datablock_collection_add_and_filters_with_real_parameters(): from easydiffraction.core.datablock import DatablockItem from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class Cat(CategoryItem): def __init__(self): @@ -21,14 +21,14 @@ def __init__(self): description='', value_spec=AttributeSpec(default=0.0), units='', - cif_handler=CifHandler(names=['_cat.p1']), + tags=TagSpec(edi_names=['_cat.p1']), ) self._p2 = Parameter( name='p2', description='', value_spec=AttributeSpec(default=0.0), units='', - cif_handler=CifHandler(names=['_cat.p2']), + tags=TagSpec(edi_names=['_cat.p2']), ) # Set actual values via setter self._p1.value = 1.0 @@ -82,7 +82,7 @@ def test_datablock_collection_fittable_excludes_symmetry_constrained_parameters( from easydiffraction.core.datablock import DatablockItem from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class Cat(CategoryItem): def __init__(self): @@ -94,14 +94,14 @@ def __init__(self): description='', value_spec=AttributeSpec(default=0.0), units='', - cif_handler=CifHandler(names=['_cat.free_param']), + tags=TagSpec(edi_names=['_cat.free_param']), ) self._fixed_param = Parameter( name='fixed_param', description='', value_spec=AttributeSpec(default=0.0), units='', - cif_handler=CifHandler(names=['_cat.fixed_param']), + tags=TagSpec(edi_names=['_cat.fixed_param']), ) self._free_param.value = 1.0 self._fixed_param.value = 2.0 @@ -140,7 +140,7 @@ def test_datablock_item_help(capsys): from easydiffraction.core.datablock import DatablockItem from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class Cat(CategoryItem): def __init__(self): @@ -152,7 +152,7 @@ def __init__(self): description='', value_spec=AttributeSpec(default=0.0), units='', - cif_handler=CifHandler(names=['_cat.p1']), + tags=TagSpec(edi_names=['_cat.p1']), ) @property diff --git a/tests/unit/easydiffraction/core/test_parameters.py b/tests/unit/easydiffraction/core/test_parameters.py index 8886c2f2d..5cac3d9aa 100644 --- a/tests/unit/easydiffraction/core/test_parameters.py +++ b/tests/unit/easydiffraction/core/test_parameters.py @@ -16,27 +16,27 @@ def test_string_descriptor_type_override_raises_type_error(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.validation import DataTypes from easydiffraction.core.variable import StringDescriptor - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec with pytest.raises(TypeError): StringDescriptor( name='title', value_spec=AttributeSpec(data_type=DataTypes.NUMERIC, default='x'), description='Title text', - cif_handler=CifHandler(names=['_proj.title']), + tags=TagSpec(edi_names=['_proj.title']), ) def test_numeric_descriptor_str_includes_units(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import NumericDescriptor - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec d = NumericDescriptor( name='w', value_spec=AttributeSpec(default=1.23), units='degrees', - cif_handler=CifHandler(names=['_x.w']), + tags=TagSpec(edi_names=['_x.w']), ) s = str(d) assert s.startswith('<') @@ -49,13 +49,13 @@ def test_numeric_descriptor_str_uses_pretty_display_units(): from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import NumericDescriptor - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec d = NumericDescriptor( name='a', value_spec=AttributeSpec(default=5.43), units='angstroms', - cif_handler=CifHandler(names=['_cell.a']), + tags=TagSpec(edi_names=['_cell.a']), display_handler=DisplayHandler(display_units='Å'), ) s = str(d) @@ -66,13 +66,13 @@ def test_numeric_descriptor_str_uses_pretty_display_units(): def test_parameter_string_repr_and_as_cif_and_flags(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='a', value_spec=AttributeSpec(default=0.0), units='angstroms', - cif_handler=CifHandler(names=['_param.a']), + tags=TagSpec(edi_names=['_param.a']), ) p.value = 2.5 # Update extra attributes @@ -87,21 +87,21 @@ def test_parameter_string_repr_and_as_cif_and_flags(): # CIF line: free param with uncertainty uses 2-sig-digit esd brackets assert p.as_cif == '_param.a 2.50(10)' - # CifHandler uid is owner's unique_name (parameter name here) - assert p._cif_handler.uid == p.unique_name == 'a' + # TagSpec uid is owner's unique_name (parameter name here) + assert p._tags.uid == p.unique_name == 'a' def test_parameter_str_uses_pretty_display_units(): from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='a', value_spec=AttributeSpec(default=0.0), units='angstroms', - cif_handler=CifHandler(names=['_cell.a']), + tags=TagSpec(edi_names=['_cell.a']), display_handler=DisplayHandler(display_units='Å'), ) p.value = 5.43 @@ -118,12 +118,12 @@ def test_parameter_str_uses_pretty_display_units(): def test_parameter_uncertainty_must_be_non_negative(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='b', value_spec=AttributeSpec(default=1.0), - cif_handler=CifHandler(names=['_param.b']), + tags=TagSpec(edi_names=['_param.b']), ) with pytest.raises(TypeError): p.uncertainty = -0.5 @@ -132,12 +132,12 @@ def test_parameter_uncertainty_must_be_non_negative(): def test_parameter_fit_bounds_assign_and_read(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='c', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_param.c']), + tags=TagSpec(edi_names=['_param.c']), ) p.fit_min = -1.0 p.fit_max = 10.0 @@ -148,12 +148,12 @@ def test_parameter_fit_bounds_assign_and_read(): def test_parameter_set_fit_bounds_from_uncertainty_sets_bounds_and_returns_none(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='d', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_param.d']), + tags=TagSpec(edi_names=['_param.d']), ) p.value = 2.0 p.uncertainty = 0.25 @@ -168,12 +168,12 @@ def test_parameter_set_fit_bounds_from_uncertainty_sets_bounds_and_returns_none( def test_parameter_set_fit_bounds_from_uncertainty_uses_default_multiplier(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='default_multiplier', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_param.default_multiplier']), + tags=TagSpec(edi_names=['_param.default_multiplier']), ) p.value = 2.0 p.uncertainty = 0.25 @@ -189,7 +189,7 @@ def test_parameter_set_fit_bounds_from_uncertainty_clips_to_physical_limits(): from easydiffraction.core.validation import DataTypes from easydiffraction.core.validation import RangeValidator from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='bounded', @@ -198,7 +198,7 @@ def test_parameter_set_fit_bounds_from_uncertainty_clips_to_physical_limits(): default=1.0, validator=RangeValidator(ge=0.5, le=1.5), ), - cif_handler=CifHandler(names=['_param.bounded']), + tags=TagSpec(edi_names=['_param.bounded']), ) p.value = 1.0 p.uncertainty = 0.3 @@ -212,12 +212,12 @@ def test_parameter_set_fit_bounds_from_uncertainty_clips_to_physical_limits(): def test_parameter_set_fit_bounds_from_uncertainty_requires_valid_uncertainty(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='invalid', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_param.invalid']), + tags=TagSpec(edi_names=['_param.invalid']), ) p.value = 2.0 p.uncertainty = None @@ -232,12 +232,12 @@ def test_parameter_set_fit_bounds_from_uncertainty_requires_valid_uncertainty(): def _make_param() -> object: from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec return Parameter( name='p', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_param.p']), + tags=TagSpec(edi_names=['_param.p']), ) diff --git a/tests/unit/easydiffraction/core/test_variable_posterior.py b/tests/unit/easydiffraction/core/test_variable_posterior.py index b87821abc..df930bd28 100644 --- a/tests/unit/easydiffraction/core/test_variable_posterior.py +++ b/tests/unit/easydiffraction/core/test_variable_posterior.py @@ -9,12 +9,12 @@ def test_parameter_posterior_summary_is_set_internally(): from easydiffraction.core.posterior import PosteriorParameterSummary from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec parameter = Parameter( name='length_a', value_spec=AttributeSpec(default=3.88), - cif_handler=CifHandler(names=['_cell.length_a']), + tags=TagSpec(edi_names=['_cell.length_a']), ) summary = PosteriorParameterSummary( unique_name=parameter.unique_name, diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_base.py index 50ec8b552..8bfb46d82 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_base.py @@ -11,12 +11,12 @@ def test_module_import(): assert MUT.__name__.endswith('absorption.base') -def test_type_descriptor_cif_handler_names(): +def test_type_descriptor_tag_names(): from easydiffraction.datablocks.experiment.categories.absorption.none import NoAbsorption absorption = NoAbsorption() - assert '_absorption.type' in absorption._type._cif_handler.names - assert absorption._type._cif_handler.iucr_name == '_easydiffraction_absorption.type' + assert '_absorption.type' in absorption._type._tags.edi_names + assert absorption._type._tags.cif_name == '_easydiffraction_absorption.type' def test_absorption_exposed_on_bragg_powder_only(): diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_cylinder_hewat.py b/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_cylinder_hewat.py index 7b7a7a6aa..e1570c210 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_cylinder_hewat.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/absorption/test_cylinder_hewat.py @@ -43,14 +43,14 @@ def test_cylinder_hewat_mu_r_must_be_non_negative(monkeypatch): assert absorption.mu_r.value == 0.7 -def test_cylinder_hewat_mu_r_cif_handler_names(): +def test_cylinder_hewat_mu_r_tag_names(): from easydiffraction.datablocks.experiment.categories.absorption.cylinder_hewat import ( CylinderHewatAbsorption, ) absorption = CylinderHewatAbsorption() - assert '_absorption.mu_r' in absorption._mu_r._cif_handler.names - assert absorption._mu_r._cif_handler.iucr_name == '_easydiffraction_absorption.mu_r' + assert '_absorption.mu_r' in absorption._mu_r._tags.edi_names + assert absorption._mu_r._tags.cif_name == '_easydiffraction_absorption.mu_r' def test_cylinder_hewat_is_constant_wavelength_only(): diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py index ddf229a6b..bdefc1abf 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_base.py @@ -11,7 +11,7 @@ def test_background_base_minimal_impl_and_collection_cif(): from easydiffraction.core.validation import DataTypes from easydiffraction.core.variable import Parameter from easydiffraction.datablocks.experiment.categories.background.base import BackgroundBase - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class ConstantBackground(CategoryItem): def __init__(self): @@ -20,7 +20,7 @@ def __init__(self): self._level = Parameter( name='level', value_spec=AttributeSpec(data_type=DataTypes.NUMERIC, default=0.0), - cif_handler=CifHandler(names=['_bkg.level']), + tags=TagSpec(edi_names=['_bkg.level']), ) self._identity.category_entry_name = lambda: str(self._level.value) diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py index 5ded323a4..a6a57ee8d 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_chebyshev.py @@ -28,5 +28,5 @@ def test_chebyshev_background_calculate_and_cif(): cb.create(order=0, coef=1.0) cb.create(order=1, coef=0.5) cif = cb.as_cif - assert '_pd_background.Chebyshev_order' in cif - assert '_pd_background.Chebyshev_coef' in cif + assert '_background.order' in cif + assert '_background.coef' in cif diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py index cd771476e..3cbd33c47 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/background/test_line_segment.py @@ -33,16 +33,16 @@ def test_line_segment_background_calculate_and_cif(): assert np.allclose(mock_data._bkg, [0.0, 0.0, 0.0]) # Add two points -> linear interpolation - bkg.create(id='1', x=0.0, y=0.0) - bkg.create(id='2', x=2.0, y=4.0) + bkg.create(id='1', position=0.0, intensity=0.0) + bkg.create(id='2', position=2.0, intensity=4.0) bkg._update() assert np.allclose(mock_data._bkg, [0.0, 2.0, 4.0]) # CIF loop has correct header and rows cif = bkg.as_cif assert 'loop_' in cif - assert '_pd_background.line_segment_X' in cif - assert '_pd_background.line_segment_intensity' in cif + assert '_background.position' in cif + assert '_background.intensity' in cif def _make_background(x, intensity_meas, intensity_calc=None, intensity_bkg=None): @@ -83,19 +83,19 @@ def test_auto_estimate_creates_sequential_fixed_points(): assert len(bkg) >= 2 ids = [p.id.value for p in bkg._items] assert ids == [str(i) for i in range(1, len(bkg) + 1)] - assert all(p.y.free is False for p in bkg._items) + assert all(p.intensity.free is False for p in bkg._items) def test_auto_estimate_overwrites_and_refixes(): x, y = _synthetic(seed=2) bkg = _make_background(x, y) - bkg.create(id='99', x=1.0, y=1.0) - bkg._items[0].y.free = True # user freed a hand-added point + bkg.create(id='99', position=1.0, intensity=1.0) + bkg._items[0].intensity.free = True # user freed a hand-added point bkg.auto_estimate() ids = [p.id.value for p in bkg._items] assert '99' not in ids assert ids[0] == '1' - assert all(p.y.free is False for p in bkg._items) + assert all(p.intensity.free is False for p in bkg._items) def test_auto_estimate_replace_notice(monkeypatch): @@ -116,7 +116,7 @@ def test_auto_estimate_model_guided_path(): bkg = _make_background(x, y, intensity_calc=calc, intensity_bkg=np.full_like(x, 6.0)) bkg.auto_estimate(use_model=True) assert len(bkg) >= 2 - assert all(p.y.free is False for p in bkg._items) + assert all(p.intensity.free is False for p in bkg._items) def test_auto_estimate_accepts_each_method(): @@ -220,6 +220,6 @@ def test_auto_estimate_clips_heights_to_measured(monkeypatch): _patch_helper(monkeypatch, captured, anchors=anchors) obj = _make_background(x, meas) obj.auto_estimate() - heights = [p.y.value for p in obj._items] + heights = [p.intensity.value for p in obj._items] # Absolute anchor heights clipped to [0, measured(=50)] -- no residual add-back. assert heights == [50.0, 0.0, 30.0] diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py index c50a095e5..3ef143085 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_bragg_pd.py @@ -20,7 +20,7 @@ def test_pd_cwl_data_point_defaults(): from easydiffraction.datablocks.experiment.categories.data.bragg_pd import PdCwlDataPoint pt = PdCwlDataPoint() - assert pt.point_id.value == '0' + assert pt.id.value == '0' assert pt.d_spacing.value == 0.0 assert pt.two_theta.value == 0.0 assert pt.intensity_meas.value == 0.0 @@ -28,14 +28,14 @@ def test_pd_cwl_data_point_defaults(): assert pt.intensity_calc.value == 0.0 assert pt.intensity_bkg.value == 0.0 assert pt.calc_status.value == 'incl' - assert pt._identity.category_code == 'pd_data' + assert pt._identity.category_code == 'data' def test_pd_tof_data_point_defaults(): from easydiffraction.datablocks.experiment.categories.data.bragg_pd import PdTofDataPoint pt = PdTofDataPoint() - assert pt.point_id.value == '0' + assert pt.id.value == '0' assert pt.d_spacing.value == 0.0 assert pt.time_of_flight.value == 0.0 assert pt.intensity_meas.value == 0.0 @@ -43,7 +43,7 @@ def test_pd_tof_data_point_defaults(): assert pt.intensity_calc.value == 0.0 assert pt.intensity_bkg.value == 0.0 assert pt.calc_status.value == 'incl' - assert pt._identity.category_code == 'pd_data' + assert pt._identity.category_code == 'data' def test_pd_cwl_data_collection_create_and_properties(): @@ -77,9 +77,9 @@ def test_pd_cwl_data_collection_create_and_properties(): np.testing.assert_array_almost_equal(coll.intensity_meas_su, su) # Check point IDs are set - assert coll._items[0].point_id.value == '1' - assert coll._items[1].point_id.value == '2' - assert coll._items[2].point_id.value == '3' + assert coll._items[0].id.value == '1' + assert coll._items[1].id.value == '2' + assert coll._items[2].id.value == '3' def test_pd_tof_data_collection_create_and_properties(): @@ -103,8 +103,8 @@ def test_pd_tof_data_collection_create_and_properties(): np.testing.assert_array_almost_equal(coll.unfiltered_x, x_vals) # Check point IDs are set - assert coll._items[0].point_id.value == '1' - assert coll._items[2].point_id.value == '3' + assert coll._items[0].id.value == '1' + assert coll._items[2].id.value == '3' def test_pd_data_items_resolve_experiment_datablock_name(): @@ -117,7 +117,7 @@ def test_pd_data_items_resolve_experiment_datablock_name(): param = coll._items[0].intensity_meas assert param._identity.datablock_entry_name == 'hrpt' - assert param.unique_name == 'hrpt.pd_data.1.intensity_meas' + assert param.unique_name == 'hrpt.data.1.intensity_meas' def test_pd_data_calc_status_exclusion(): diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py index 3198158e2..f681f8e98 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data/test_total_pd.py @@ -20,13 +20,13 @@ def test_total_data_point_defaults(): from easydiffraction.datablocks.experiment.categories.data.total_pd import TotalDataPoint pt = TotalDataPoint() - assert pt.point_id.value == '0' + assert pt.id.value == '0' assert pt.r.value == 0.0 assert pt.g_r_meas.value == 0.0 assert pt.g_r_meas_su.value == 0.0 assert pt.g_r_calc.value == 0.0 assert pt.calc_status.value == 'incl' - assert pt._identity.category_code == 'total_data' + assert pt._identity.category_code == 'data' def test_total_data_collection_create_and_properties(): @@ -57,8 +57,8 @@ def test_total_data_collection_create_and_properties(): np.testing.assert_array_almost_equal(coll.intensity_meas_su, g_su) # Point IDs - assert coll._items[0].point_id.value == '1' - assert coll._items[3].point_id.value == '4' + assert coll._items[0].id.value == '1' + assert coll._items[3].id.value == '4' def test_total_data_calc_status_and_exclusion(): @@ -113,4 +113,4 @@ def test_total_data_items_resolve_experiment_datablock_name(): param = coll._items[0].g_r_meas assert param._identity.datablock_entry_name == 'pdf-exp' - assert param.unique_name == 'pdf-exp.total_data.1.g_r_meas' + assert param.unique_name == 'pdf-exp.data.1.g_r_meas' diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/data_range/test_tof.py b/tests/unit/easydiffraction/datablocks/experiment/categories/data_range/test_tof.py index 2fb0b490d..4b323cf2c 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/data_range/test_tof.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/data_range/test_tof.py @@ -18,7 +18,7 @@ def _parent(*, calib=None, measured=False, x=None): instrument = SimpleNamespace( calib_d_to_tof_offset=SimpleNamespace(value=offset), calib_d_to_tof_linear=SimpleNamespace(value=linear), - calib_d_to_tof_quad=SimpleNamespace(value=quad), + calib_d_to_tof_quadratic=SimpleNamespace(value=quad), ) else: instrument = None diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py index 6a23bc7f5..d7616333c 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/instrument/test_tof.py @@ -13,21 +13,21 @@ def test_tof_instrument_defaults_and_setters_and_parameters_and_cif(): assert np.isclose(inst.setup_twotheta_bank.value, 150.0) assert np.isclose(inst.calib_d_to_tof_offset.value, 0.0) assert np.isclose(inst.calib_d_to_tof_linear.value, 10000.0) - assert np.isclose(inst.calib_d_to_tof_quad.value, 0.0) - assert np.isclose(inst.calib_d_to_tof_recip.value, 0.0) + assert np.isclose(inst.calib_d_to_tof_quadratic.value, 0.0) + assert np.isclose(inst.calib_d_to_tof_reciprocal.value, 0.0) # Setters inst.setup_twotheta_bank = 160.0 inst.calib_d_to_tof_offset = 1.0 inst.calib_d_to_tof_linear = 9000.0 - inst.calib_d_to_tof_quad = -2e-5 - inst.calib_d_to_tof_recip = 0.5 + inst.calib_d_to_tof_quadratic = -2e-5 + inst.calib_d_to_tof_reciprocal = 0.5 assert np.isclose(inst.setup_twotheta_bank.value, 160.0) assert np.isclose(inst.calib_d_to_tof_offset.value, 1.0) assert np.isclose(inst.calib_d_to_tof_linear.value, 9000.0) - assert np.isclose(inst.calib_d_to_tof_quad.value, -2e-5) - assert np.isclose(inst.calib_d_to_tof_recip.value, 0.5) + assert np.isclose(inst.calib_d_to_tof_quadratic.value, -2e-5) + assert np.isclose(inst.calib_d_to_tof_reciprocal.value, 0.5) # Parameters exposure via CategoryItem.parameters names = {p.name for p in inst.parameters} @@ -35,11 +35,11 @@ def test_tof_instrument_defaults_and_setters_and_parameters_and_cif(): 'twotheta_bank', 'd_to_tof_offset', 'd_to_tof_linear', - 'd_to_tof_quad', - 'd_to_tof_recip', + 'd_to_tof_quadratic', + 'd_to_tof_reciprocal', }.issubset(names) # CIF representation of the item should include tags in separate lines cif = inst.as_cif - assert '_instr.2theta_bank' in cif - assert '_instr.d_to_tof_linear' in cif + assert '_instrument.setup_twotheta_bank' in cif + assert '_instrument.calib_d_to_tof_linear' in cif diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py index a565b12c9..1dfad27da 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof.py @@ -9,30 +9,30 @@ def test_tof_jorgensen_has_broadening_and_bbe_params(): peak = TofJorgensen() - assert peak.broad_gauss_sigma_0.name == 'gauss_sigma_0' + assert peak.broad_gauss_sigma_0.name == 'broad_gauss_sigma_0' peak.broad_gauss_sigma_2 = 1.23 assert peak.broad_gauss_sigma_2.value == 1.23 - assert peak.exp_rise_alpha_0.name == 'rise_alpha_0' - assert peak.exp_decay_beta_0.name == 'decay_beta_0' + assert peak.rise_alpha_0.name == 'rise_alpha_0' + assert peak.decay_beta_0.name == 'decay_beta_0' def test_tof_jorgensen_von_dreele_has_lorentzian_broadening(): peak = TofJorgensenVonDreele() - assert peak.broad_lorentz_gamma_0.name == 'lorentz_gamma_0' - peak.exp_rise_alpha_1 = 0.77 - assert peak.exp_rise_alpha_1.value == 0.77 + assert peak.broad_lorentz_gamma_0.name == 'broad_lorentz_gamma_0' + peak.rise_alpha_1 = 0.77 + assert peak.rise_alpha_1.value == 0.77 def test_tof_jorgensen_von_dreele_has_bbe_decay(): peak = TofJorgensenVonDreele() - assert peak.exp_decay_beta_0.name == 'decay_beta_0' + assert peak.decay_beta_0.name == 'decay_beta_0' def test_tof_double_jorgensen_von_dreele_has_double_bbe_params(): peak = TofDoubleJorgensenVonDreele() # Gaussian + Lorentzian broadening - assert peak.broad_gauss_sigma_0.name == 'gauss_sigma_0' - assert peak.broad_lorentz_gamma_0.name == 'lorentz_gamma_0' + assert peak.broad_gauss_sigma_0.name == 'broad_gauss_sigma_0' + assert peak.broad_lorentz_gamma_0.name == 'broad_lorentz_gamma_0' # Double-exp parameters assert peak.dexp_rise_alpha_1.name == 'dexp_rise_alpha_1' assert peak.dexp_decay_beta_00.name == 'dexp_decay_beta_00' diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py index dcbcabab5..7216fe00c 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/peak/test_tof_mixins.py @@ -29,15 +29,15 @@ def __init__(self): names = {param.name for param in p.parameters} # Gaussian broadening assert { - 'gauss_sigma_0', - 'gauss_sigma_1', - 'gauss_sigma_2', + 'broad_gauss_sigma_0', + 'broad_gauss_sigma_1', + 'broad_gauss_sigma_2', }.issubset(names) # Lorentzian broadening assert { - 'lorentz_gamma_0', - 'lorentz_gamma_1', - 'lorentz_gamma_2', + 'broad_lorentz_gamma_0', + 'broad_lorentz_gamma_1', + 'broad_lorentz_gamma_2', }.issubset(names) # BBE rise and decay assert {'rise_alpha_0', 'rise_alpha_1'}.issubset(names) @@ -45,9 +45,9 @@ def __init__(self): # Verify setters update values p.broad_gauss_sigma_0 = 1.0 - p.exp_rise_alpha_1 = 0.5 + p.rise_alpha_1 = 0.5 assert np.isclose(p.broad_gauss_sigma_0.value, 1.0) - assert np.isclose(p.exp_rise_alpha_1.value, 0.5) + assert np.isclose(p.rise_alpha_1.value, 0.5) def test_tof_double_exponential_mixin(): @@ -74,8 +74,10 @@ def __init__(self): p = DoublePeak() names = {param.name for param in p.parameters} # Gaussian + Lorentzian broadening from existing mixins - assert {'gauss_sigma_0', 'gauss_sigma_1', 'gauss_sigma_2'}.issubset(names) - assert {'lorentz_gamma_0', 'lorentz_gamma_1', 'lorentz_gamma_2'}.issubset(names) + assert {'broad_gauss_sigma_0', 'broad_gauss_sigma_1', 'broad_gauss_sigma_2'}.issubset(names) + assert {'broad_lorentz_gamma_0', 'broad_lorentz_gamma_1', 'broad_lorentz_gamma_2'}.issubset( + names + ) # Double-exp rise assert {'dexp_rise_alpha_1', 'dexp_rise_alpha_2'}.issubset(names) # Double-exp decay diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/refln/test_bragg_pd.py b/tests/unit/easydiffraction/datablocks/experiment/categories/refln/test_bragg_pd.py index 45bf13728..8e83cb153 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/refln/test_bragg_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/refln/test_bragg_pd.py @@ -15,7 +15,7 @@ def test_powder_cwl_refln_defaults(): refln = PowderCwlRefln() assert refln.id.value == '0' - assert refln.phase_id.value == '' + assert refln.structure_id.value == '' assert refln.two_theta.value == 0.0 assert refln.d_spacing.value == 0.0 assert refln.f_calc.value == 0.0 @@ -29,7 +29,7 @@ def test_powder_cwl_refln_data_replace_from_records_sets_arrays(): refln = PowderCwlReflnData() refln._replace_from_records([ PowderReflnRecord( - phase_id='alpha', + structure_id='alpha', d_spacing=2.1, sin_theta_over_lambda=0.25, index_h=1, @@ -40,7 +40,7 @@ def test_powder_cwl_refln_data_replace_from_records_sets_arrays(): two_theta=14.5, ), PowderReflnRecord( - phase_id='beta', + structure_id='beta', d_spacing=1.5, sin_theta_over_lambda=0.33, index_h=2, @@ -53,7 +53,7 @@ def test_powder_cwl_refln_data_replace_from_records_sets_arrays(): ]) assert [item.id.value for item in refln._items] == ['1', '2'] - np.testing.assert_array_equal(refln.phase_id, np.array(['alpha', 'beta'])) + np.testing.assert_array_equal(refln.structure_id, np.array(['alpha', 'beta'])) np.testing.assert_allclose(refln.d_spacing, np.array([2.1, 1.5])) np.testing.assert_allclose(refln.two_theta, np.array([14.5, 22.0])) np.testing.assert_allclose(refln.f_calc, np.array([3.0, 4.0])) @@ -66,7 +66,7 @@ def test_powder_tof_refln_data_replace_from_records_sets_arrays(): refln = PowderTofReflnData() refln._replace_from_records([ PowderReflnRecord( - phase_id='gamma', + structure_id='gamma', d_spacing=3.2, sin_theta_over_lambda=0.15, index_h=1, @@ -79,7 +79,7 @@ def test_powder_tof_refln_data_replace_from_records_sets_arrays(): ]) assert [item.id.value for item in refln._items] == ['1'] - np.testing.assert_array_equal(refln.phase_id, np.array(['gamma'])) + np.testing.assert_array_equal(refln.structure_id, np.array(['gamma'])) np.testing.assert_allclose(refln.time_of_flight, np.array([1200.0])) np.testing.assert_allclose(refln.d_spacing, np.array([3.2])) @@ -98,7 +98,7 @@ def test_powder_refln_replace_from_records_rebuilds_index_and_parents(): refln = PowderCwlReflnData() refln._replace_from_records([ PowderReflnRecord( - phase_id='alpha', + structure_id='alpha', d_spacing=2.1, sin_theta_over_lambda=0.25, index_h=1, @@ -122,7 +122,7 @@ def test_powder_refln_replace_from_records_rebuilds_index_and_parents(): refln._replace_from_records([ PowderReflnRecord( - phase_id='beta', + structure_id='beta', d_spacing=1.5, sin_theta_over_lambda=0.33, index_h=2, @@ -137,7 +137,7 @@ def test_powder_refln_replace_from_records_rebuilds_index_and_parents(): new_item = refln['1'] assert new_item is refln._items[0] assert new_item._parent is refln - assert new_item.phase_id.value == 'beta' + assert new_item.structure_id.value == 'beta' def test_powder_refln_round_trips_via_experiment_cif(): @@ -150,7 +150,7 @@ def test_powder_refln_round_trips_via_experiment_cif(): ) experiment.refln._replace_from_records([ PowderReflnRecord( - phase_id='alpha', + structure_id='alpha', d_spacing=2.1, sin_theta_over_lambda=0.25, index_h=1, @@ -161,7 +161,7 @@ def test_powder_refln_round_trips_via_experiment_cif(): two_theta=14.5, ), PowderReflnRecord( - phase_id='beta', + structure_id='beta', d_spacing=1.5, sin_theta_over_lambda=0.33, index_h=2, @@ -177,10 +177,10 @@ def test_powder_refln_round_trips_via_experiment_cif(): cif = experiment.as_cif loaded = ExperimentFactory.from_cif_str(cif) - assert '_refln.phase_id' in cif + assert '_refln.structure_id' in cif assert '_refln.f_calc' in cif assert '_refln.f_squared_calc' in cif - np.testing.assert_array_equal(loaded.refln.phase_id, np.array(['alpha', 'beta'])) + np.testing.assert_array_equal(loaded.refln.structure_id, np.array(['alpha', 'beta'])) np.testing.assert_allclose(loaded.refln.two_theta, np.array([14.5, 22.0])) np.testing.assert_allclose(loaded.refln.f_calc, np.array([3.0, 4.0])) np.testing.assert_allclose(loaded.refln.f_squared_calc, np.array([9.0, 16.0])) diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py index c8eda06f3..4eb6a0e57 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_extinction.py @@ -67,20 +67,20 @@ def test_extinction_property_setters(): assert ext.radius.value == 10.0 -def test_extinction_cif_handler_names(): +def test_extinction_tags_names(): from easydiffraction.datablocks.experiment.categories.extinction.becker_coppens import ( BeckerCoppensExtinction, ) ext = BeckerCoppensExtinction() - model_cif_names = ext._model._cif_handler.names + model_cif_names = ext._model._tags.edi_names assert '_extinction.model' in model_cif_names - mosaicity_cif_names = ext._mosaicity._cif_handler.names + mosaicity_cif_names = ext._mosaicity._tags.edi_names assert '_extinction.mosaicity' in mosaicity_cif_names - radius_cif_names = ext._radius._cif_handler.names + radius_cif_names = ext._radius._tags.edi_names assert '_extinction.radius' in radius_cif_names diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py deleted file mode 100644 index 69f96e7b5..000000000 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_crystal.py +++ /dev/null @@ -1,88 +0,0 @@ -# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> -# SPDX-License-Identifier: BSD-3-Clause - - -def test_module_import(): - import easydiffraction.datablocks.experiment.categories.linked_crystal.default as MUT - - expected_module_name = ( - 'easydiffraction.datablocks.experiment.categories.linked_crystal.default' - ) - actual_module_name = MUT.__name__ - assert expected_module_name == actual_module_name - - -def test_linked_crystal_defaults(): - from easydiffraction.datablocks.experiment.categories.linked_crystal.default import ( - LinkedCrystal, - ) - - lc = LinkedCrystal() - assert lc.id.value == 'Si' - assert lc.scale.value == 1.0 - assert lc._identity.category_code == 'linked_crystal' - - -def test_linked_crystal_property_setters(): - from easydiffraction.datablocks.experiment.categories.linked_crystal.default import ( - LinkedCrystal, - ) - - lc = LinkedCrystal() - - lc.id = 'Ge' - assert lc.id.value == 'Ge' - - lc.scale = 2.5 - assert lc.scale.value == 2.5 - - -def test_linked_crystal_cif_handler_names(): - from easydiffraction.datablocks.experiment.categories.linked_crystal.default import ( - LinkedCrystal, - ) - - lc = LinkedCrystal() - - id_cif_names = lc._id._cif_handler.names - assert '_sc_crystal_block.id' in id_cif_names - - scale_cif_names = lc._scale._cif_handler.names - assert '_sc_crystal_block.scale' in scale_cif_names - - -def test_linked_crystal_type_info(): - from easydiffraction.datablocks.experiment.categories.linked_crystal.default import ( - LinkedCrystal, - ) - - assert LinkedCrystal.type_info.tag == 'default' - assert LinkedCrystal.type_info.description != '' - - -def test_linked_crystal_factory_registration(): - from easydiffraction.datablocks.experiment.categories.linked_crystal.factory import ( - LinkedCrystalFactory, - ) - - assert 'default' in LinkedCrystalFactory.supported_tags() - - -def test_linked_crystal_factory_create(): - from easydiffraction.datablocks.experiment.categories.linked_crystal.default import ( - LinkedCrystal, - ) - from easydiffraction.datablocks.experiment.categories.linked_crystal.factory import ( - LinkedCrystalFactory, - ) - - lc = LinkedCrystalFactory.create('default') - assert isinstance(lc, LinkedCrystal) - - -def test_linked_crystal_factory_default_tag(): - from easydiffraction.datablocks.experiment.categories.linked_crystal.factory import ( - LinkedCrystalFactory, - ) - - assert LinkedCrystalFactory.default_tag() == 'default' diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_structure.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_structure.py new file mode 100644 index 000000000..e54737f80 --- /dev/null +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_structure.py @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause + + +def test_module_import(): + import easydiffraction.datablocks.experiment.categories.linked_structure.default as MUT + + expected_module_name = ( + 'easydiffraction.datablocks.experiment.categories.linked_structure.default' + ) + actual_module_name = MUT.__name__ + assert expected_module_name == actual_module_name + + +def test_linked_structure_defaults(): + from easydiffraction.datablocks.experiment.categories.linked_structure.default import ( + LinkedStructure, + ) + + lc = LinkedStructure() + assert lc.structure_id.value == 'Si' + assert lc.scale.value == 1.0 + assert lc._identity.category_code == 'linked_structure' + + +def test_linked_structure_property_setters(): + from easydiffraction.datablocks.experiment.categories.linked_structure.default import ( + LinkedStructure, + ) + + lc = LinkedStructure() + + lc.structure_id = 'Ge' + assert lc.structure_id.value == 'Ge' + + lc.scale = 2.5 + assert lc.scale.value == 2.5 + + +def test_linked_structure_tags_names(): + from easydiffraction.datablocks.experiment.categories.linked_structure.default import ( + LinkedStructure, + ) + + lc = LinkedStructure() + + id_cif_names = lc._structure_id._tags.edi_names + assert '_linked_structure.structure_id' in id_cif_names + + scale_cif_names = lc._scale._tags.edi_names + assert '_linked_structure.scale' in scale_cif_names + + +def test_linked_structure_type_info(): + from easydiffraction.datablocks.experiment.categories.linked_structure.default import ( + LinkedStructure, + ) + + assert LinkedStructure.type_info.tag == 'default' + assert LinkedStructure.type_info.description != '' + + +def test_linked_structure_factory_registration(): + from easydiffraction.datablocks.experiment.categories.linked_structure.factory import ( + LinkedStructureFactory, + ) + + assert 'default' in LinkedStructureFactory.supported_tags() + + +def test_linked_structure_factory_create(): + from easydiffraction.datablocks.experiment.categories.linked_structure.default import ( + LinkedStructure, + ) + from easydiffraction.datablocks.experiment.categories.linked_structure.factory import ( + LinkedStructureFactory, + ) + + lc = LinkedStructureFactory.create('default') + assert isinstance(lc, LinkedStructure) + + +def test_linked_structure_factory_default_tag(): + from easydiffraction.datablocks.experiment.categories.linked_structure.factory import ( + LinkedStructureFactory, + ) + + assert LinkedStructureFactory.default_tag() == 'default' diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_structures.py similarity index 50% rename from tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py rename to tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_structures.py index 5e4f4ac4e..df34454c2 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_phases.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_linked_structures.py @@ -2,21 +2,21 @@ # SPDX-License-Identifier: BSD-3-Clause -def test_linked_phases_add_and_cif_headers(): - from easydiffraction.datablocks.experiment.categories.linked_phases import LinkedPhase - from easydiffraction.datablocks.experiment.categories.linked_phases import LinkedPhases +def test_linked_structures_add_and_cif_headers(): + from easydiffraction.datablocks.experiment.categories.linked_structures import LinkedStructure + from easydiffraction.datablocks.experiment.categories.linked_structures import LinkedStructures - lp = LinkedPhase() - lp.id = 'Si' + lp = LinkedStructure() + lp.structure_id = 'Si' lp.scale = 2.0 - assert lp.id.value == 'Si' + assert lp.structure_id.value == 'Si' assert lp.scale.value == 2.0 - coll = LinkedPhases() - coll.create(id='Si', scale=2.0) + coll = LinkedStructures() + coll.create(structure_id='Si', scale=2.0) # CIF loop header presence cif = coll.as_cif assert 'loop_' in cif - assert '_pd_phase_block.id' in cif - assert '_pd_phase_block.scale' in cif + assert '_linked_structure.structure_id' in cif + assert '_linked_structure.scale' in cif diff --git a/tests/unit/easydiffraction/datablocks/experiment/categories/test_pref_orient.py b/tests/unit/easydiffraction/datablocks/experiment/categories/test_pref_orient.py index 64b123bed..7bf077287 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/categories/test_pref_orient.py +++ b/tests/unit/easydiffraction/datablocks/experiment/categories/test_pref_orient.py @@ -15,21 +15,21 @@ def test_pref_orient_defaults_are_noop(): assert po.march_r.value == 1.0 # March coefficient: no texture assert po.march_random_fract.value == 0.0 # pure March-Dollase assert (po.index_h.value, po.index_k.value, po.index_l.value) == (0, 0, 1) - assert po.phase_id.value == 'Si' + assert po.structure_id.value == 'Si' def test_pref_orient_property_setters(): from easydiffraction.datablocks.experiment.categories.pref_orient import PrefOrient po = PrefOrient() - po.phase_id = 'lbco' + po.structure_id = 'lbco' po.march_r = 0.75 po.march_random_fract = 0.2 po.index_h = 1 po.index_k = 0 po.index_l = 2 - assert po.phase_id.value == 'lbco' + assert po.structure_id.value == 'lbco' assert po.march_r.value == 0.75 assert po.march_random_fract.value == 0.2 assert (po.index_h.value, po.index_k.value, po.index_l.value) == (1, 0, 2) @@ -67,17 +67,17 @@ def test_pref_orients_create_and_default_cif(): from easydiffraction.datablocks.experiment.categories.pref_orient import PrefOrients coll = PrefOrients() - coll.create(phase_id='lbco', march_r=0.8, index_h=0, index_k=0, index_l=1) + coll.create(structure_id='lbco', march_r=0.8, index_h=0, index_k=0, index_l=1) cif = coll.as_cif assert 'loop_' in cif for tag in ( - '_pref_orient.phase_id', - '_pref_orient.march_r', - '_pref_orient.index_h', - '_pref_orient.index_k', - '_pref_orient.index_l', - '_pref_orient.march_random_fract', + '_preferred_orientation.structure_id', + '_preferred_orientation.march_r', + '_preferred_orientation.index_h', + '_preferred_orientation.index_k', + '_preferred_orientation.index_l', + '_preferred_orientation.march_random_fract', ): assert tag in cif @@ -112,7 +112,7 @@ def test_preferred_orientation_exposed_on_bragg_powder_only(): ) assert hasattr(bragg, 'preferred_orientation') bragg.preferred_orientation.create( - phase_id='bragg', march_r=0.5, index_h=0, index_k=0, index_l=1 + structure_id='bragg', march_r=0.5, index_h=0, index_k=0, index_l=1 ) # The collection is parent-linked to the experiment, enabling dirty # tracking on row changes. @@ -139,7 +139,7 @@ def test_pref_orient_cif_round_trip(): scattering_type='bragg', ) experiment.preferred_orientation.create( - phase_id='lbco', + structure_id='lbco', march_r=0.75, march_random_fract=0.2, index_h=1, diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py index f2e808c5f..50d4ee527 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_base.py @@ -28,7 +28,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: et._set_radiation_probe(RadiationProbeEnum.NEUTRON.value) et._set_scattering_type(ScatteringTypeEnum.BRAGG.value) - ex = ConcretePd(name='ex1', type=et) + ex = ConcretePd(name='ex1', experiment_type=et) # valid switch using tag string import pytest @@ -61,7 +61,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: warnings: list[str] = [] monkeypatch.setattr(item_base.log, 'warning', warnings.append) - ex = ConcretePd(name='ex1', type=et) + ex = ConcretePd(name='ex1', experiment_type=et) ex.peak.type = 'pseudo-voigt + empirical asymmetry' @@ -97,7 +97,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: warnings: list[str] = [] monkeypatch.setattr(item_base.log, 'warning', warnings.append) - ex = ConcretePd(name='ex1', type=et) + ex = ConcretePd(name='ex1', experiment_type=et) ex.peak.broad_gauss_u = 0.05 ex.peak.type = 'pseudo-voigt + empirical asymmetry' @@ -129,7 +129,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: warnings: list[str] = [] monkeypatch.setattr(item_base.log, 'warning', warnings.append) - ex = ConcretePd(name='ex1', type=et) + ex = ConcretePd(name='ex1', experiment_type=et) ex.peak.type = 'pseudo-voigt + empirical asymmetry' warnings.clear() @@ -165,7 +165,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: et._set_radiation_probe(RadiationProbeEnum.NEUTRON.value) et._set_scattering_type(ScatteringTypeEnum.BRAGG.value) - ex = ConcretePd(name='ex1', type=et) + ex = ConcretePd(name='ex1', experiment_type=et) ex._set_peak_profile_type('pseudo-voigt + empirical asymmetry') # Profile type was switched @@ -196,7 +196,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: et._set_radiation_probe(RadiationProbeEnum.NEUTRON.value) et._set_scattering_type(ScatteringTypeEnum.BRAGG.value) - ex = ConcretePd(name='ex1', type=et) + ex = ConcretePd(name='ex1', experiment_type=et) original_type = ex.peak.type ex._set_peak_profile_type('nonexistent-profile') @@ -225,7 +225,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: et._set_radiation_probe(RadiationProbeEnum.NEUTRON.value) et._set_scattering_type(ScatteringTypeEnum.BRAGG.value) - ex = ConcretePd(name='ex1', type=et) + ex = ConcretePd(name='ex1', experiment_type=et) cif = 'data_ex1\n_peak.type "pseudo-voigt + empirical asymmetry"\n' doc = gemmi.cif.read_string(cif) @@ -258,7 +258,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> None: et._set_radiation_probe(RadiationProbeEnum.NEUTRON.value) et._set_scattering_type(ScatteringTypeEnum.BRAGG.value) - ex = ConcreteBase(name='ex1', type=et) + ex = ConcreteBase(name='ex1', experiment_type=et) cif = 'data_ex1\n_peak.type "pseudo-voigt + empirical asymmetry"\n' doc = gemmi.cif.read_string(cif) diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_base_coverage.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_base_coverage.py index 3b0e501ce..ccf27e60f 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_base_coverage.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_base_coverage.py @@ -42,34 +42,34 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: class TestExperimentBaseName: def test_name_getter(self): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex.name == 'ex1' def test_name_setter(self): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) ex.name = 'ex2' assert ex.name == 'ex2' def test_type_property(self): et = _mk_type_powder_cwl_bragg() - ex = ConcreteBase(name='ex1', type=et) - assert ex.type is et + ex = ConcreteBase(name='ex1', experiment_type=et) + assert ex.experiment_type is et class TestExperimentBaseDiffrn: def test_diffrn_defaults(self): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex.diffrn is not None class TestExperimentBaseCalculator: def test_calculator_auto_resolves(self): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) # calculator should auto-resolve on first access assert ex.calculator.calculator is not None def test_calculator_type_auto_resolves(self): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) ct = ex.calculator.type assert isinstance(ct, str) assert len(ct) > 0 @@ -77,7 +77,7 @@ def test_calculator_type_auto_resolves(self): def test_calculator_type_invalid(self): import pytest - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) _ = ex.calculator.calculator # trigger resolve old = ex.calculator.type with pytest.raises(ValueError, match='Unsupported calculator'): @@ -85,13 +85,13 @@ def test_calculator_type_invalid(self): assert ex.calculator.type == old def test_show_calculator_types(self, capsys): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) ex.calculator.show_supported() out = capsys.readouterr().out assert len(out) > 0 def test_show_calculator_types_includes_current(self, capsys): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) ex.calculator.show_supported() out = capsys.readouterr().out assert ex.calculator.type in out @@ -99,13 +99,13 @@ def test_show_calculator_types_includes_current(self, capsys): class TestExperimentBaseAsCif: def test_as_cif_returns_str(self): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) cif = ex.as_cif assert isinstance(cif, str) - def test_show_as_cif(self, capsys): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) - ex.show_as_cif() + def test_show_as_text(self, capsys): + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) + ex.show_as_text() out = capsys.readouterr().out assert 'ex1' in out @@ -115,38 +115,38 @@ def test_show_as_cif(self, capsys): # ------------------------------------------------------------------ -class TestPdExperimentLinkedPhases: - def test_linked_phases_defaults(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) - assert ex.linked_phases is not None +class TestPdExperimentLinkedStructures: + def test_linked_structures_defaults(self): + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) + assert ex.linked_structures is not None class TestPdExperimentExcludedRegions: def test_excluded_regions_defaults(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex.excluded_regions is not None class TestPdExperimentData: def test_data_defaults(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex.data is not None class TestPdExperimentPeak: def test_peak_defaults(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex.peak is not None assert ex.peak.type is not None def test_show_peak_profile_types(self, capsys): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) ex.peak.show_supported() out = capsys.readouterr().out assert len(out) > 0 def test_show_peak_profile_types_uses_context_aliases(self, capsys): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) ex.peak.show_supported() out = capsys.readouterr().out assert 'Alias' not in out @@ -179,7 +179,7 @@ def _mk_bragg_pd(name='bpd1'): # whose replacement logic lives on ExperimentBase. from easydiffraction.datablocks.experiment.item.bragg_pd import BraggPdExperiment - return BraggPdExperiment(name=name, type=_mk_type_powder_cwl_bragg()) + return BraggPdExperiment(name=name, experiment_type=_mk_type_powder_cwl_bragg()) # ------------------------------------------------------------------ @@ -259,7 +259,7 @@ def test_calc_range_is_nan_without_data_or_wavelength(self): # the data_range category; see its own unit tests.) import math - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) range_min, range_max, increment = ex.measured_range assert math.isnan(range_min) assert math.isnan(range_max) @@ -268,7 +268,7 @@ def test_calc_range_is_nan_without_data_or_wavelength(self): def test_uniform_grid_reports_increment(self): import numpy as np - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) ex.data._create_items_set_xcoord_and_id(np.array([10.0, 20.0, 30.0, 40.0])) range_min, range_max, increment = ex.measured_range @@ -279,7 +279,7 @@ def test_uniform_grid_reports_increment(self): def test_single_point_has_no_increment(self): import numpy as np - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) ex.data._create_items_set_xcoord_and_id(np.array([15.0])) assert ex.measured_range == (15.0, 15.0, None) @@ -287,7 +287,7 @@ def test_single_point_has_no_increment(self): def test_non_uniform_grid_drops_increment(self): import numpy as np - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) ex.data._create_items_set_xcoord_and_id(np.array([0.0, 1.0, 9.0, 10.0])) range_min, range_max, increment = ex.measured_range @@ -305,7 +305,7 @@ class TestExperimentBaseIntensityCategory: def test_base_intensity_category_raises(self): import pytest - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) with pytest.raises(AttributeError, match="'ex1' has no intensity category"): ex._intensity_category() @@ -314,7 +314,7 @@ def test_abstract_loader_raises_not_implemented(self): from easydiffraction.datablocks.experiment.item.base import ExperimentBase - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) with pytest.raises(NotImplementedError): ExperimentBase._load_ascii_data_to_experiment(ex, 'some/path') @@ -328,7 +328,7 @@ class TestSwapCalculator: def test_unsupported_strict_false_warns_and_keeps(self, monkeypatch): from easydiffraction.datablocks.experiment.item import base as item_base - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) _ = ex.calculator.calculator # resolve current = ex.calculator.type @@ -341,7 +341,7 @@ def test_unsupported_strict_false_warns_and_keeps(self, monkeypatch): assert ex.calculator.type == current def test_already_set_announces(self, capsys): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) _ = ex.calculator.calculator # resolve current = ex.calculator.type capsys.readouterr() @@ -353,7 +353,7 @@ def test_already_set_announces(self, capsys): assert ex.calculator.type == current def test_already_set_silent_when_announce_false(self, capsys): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) _ = ex.calculator.calculator # resolve current = ex.calculator.type capsys.readouterr() @@ -370,7 +370,7 @@ def test_already_set_silent_when_announce_false(self, capsys): class TestResolveCalculatorFallback: def test_falls_back_to_first_supported_when_default_unsupported(self, monkeypatch): - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) # Force the default tag to be unsupported so the fallback to the # first supported tag is exercised. @@ -386,7 +386,7 @@ def test_falls_back_to_first_supported_when_default_unsupported(self, monkeypatc def test_supported_tags_returns_all_when_no_support_constraint(self, monkeypatch): from easydiffraction.analysis.calculators.factory import CalculatorFactory - ex = ConcreteBase(name='ex1', type=_mk_type_powder_cwl_bragg()) + ex = ConcreteBase(name='ex1', experiment_type=_mk_type_powder_cwl_bragg()) # ConcreteBase has neither _data nor _refln, so support category # is None and all importable tags are returned unfiltered. assert ex._calculator_support_category() is None @@ -395,7 +395,7 @@ def test_supported_tags_returns_all_when_no_support_constraint(self, monkeypatch def test_supported_tags_returns_all_when_support_lacks_calculators(self, monkeypatch): from easydiffraction.analysis.calculators.factory import CalculatorFactory - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) class NoSupport: calculator_support = None @@ -459,7 +459,7 @@ def test_switch_warns_about_discarded_points(self, monkeypatch): other = next(t for t in tags if t != ex.background.type) # Put an existing background point in so the discard branch runs. - ex.background.create(id='1', x=10.0, y=1.0) + ex.background.create(id='1', position=10.0, intensity=1.0) assert len(ex.background) > 0 warnings: list[str] = [] @@ -484,7 +484,7 @@ def test_silent_switch_emits_no_output_or_warning(self, monkeypatch, capsys): ] other = next(t for t in tags if t != ex.background.type) - ex.background.create(id='1', x=10.0, y=1.0) + ex.background.create(id='1', position=10.0, intensity=1.0) capsys.readouterr() warnings: list[str] = [] @@ -503,7 +503,7 @@ def test_silent_switch_emits_no_output_or_warning(self, monkeypatch, capsys): class TestReplaceExtinction: def test_replace_extinction_same_type_succeeds(self, capsys): - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) current = ex.extinction.type ex._replace_extinction(current, announce=True) @@ -516,7 +516,7 @@ def test_replace_extinction_same_type_succeeds(self, capsys): def test_replace_extinction_unsupported_strict_false_warns(self, monkeypatch): from easydiffraction.datablocks.experiment.item import base as item_base - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) original = ex.extinction.type warnings: list[str] = [] @@ -529,14 +529,14 @@ def test_replace_extinction_unsupported_strict_false_warns(self, monkeypatch): def test_replace_extinction_unsupported_strict_raises(self): import pytest - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) with pytest.raises(ValueError, match='Unsupported extinction type'): ex._replace_extinction('bogus-extinction', announce=False, strict=True) def test_restore_switchable_types_reads_extinction(self): import gemmi - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) cif = 'data_sc1\n_extinction.type becker-coppens\n' block = gemmi.cif.read_string(cif).sole_block() @@ -548,7 +548,7 @@ def test_restore_switchable_types_reads_extinction(self): def test_restore_switchable_types_without_extinction_tag_is_noop(self): import gemmi - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) original = ex.extinction.type # Block has no _extinction.type; restore leaves extinction as-is. @@ -563,26 +563,26 @@ def test_restore_switchable_types_without_extinction_tag_is_noop(self): class TestScExperimentAccessors: - def test_linked_crystal_instrument_refln(self): - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) - assert ex.linked_crystal is not None + def test_linked_structure_instrument_refln(self): + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) + assert ex.linked_structure is not None assert ex.instrument is not None assert ex.refln is not None def test_x_descriptor_is_none(self): - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) assert ex.x_descriptor is None def test_fit_data_arrays_empty(self): - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) assert ex.fit_data_arrays() == {} def test_intensity_category_is_refln(self): - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) assert ex._intensity_category() is ex.refln def test_calculator_support_category_is_refln(self): - ex = ConcreteSc(name='sc1', type=_mk_type_sc_cwl_bragg()) + ex = ConcreteSc(name='sc1', experiment_type=_mk_type_sc_cwl_bragg()) assert ex._calculator_support_category() is ex.refln @@ -593,27 +593,27 @@ def test_calculator_support_category_is_refln(self): class TestPdExperimentAccessors: def test_x_descriptor_delegates_to_data(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) # x_descriptor forwards to data.x_descriptor; both reference the # same underlying 2θ metadata name. assert ex.x_descriptor.name == ex.data.x_descriptor.name def test_fit_data_arrays_delegates_to_data(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex.fit_data_arrays().keys() == ex.data.fit_data_arrays().keys() def test_intensity_category_is_data(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex._intensity_category() is ex.data def test_calculator_support_category_is_data(self): - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) assert ex._calculator_support_category() is ex.data def test_restore_switchable_types_without_peak_tag_keeps_default(self): import gemmi - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) original = ex.peak.type # No _peak.type in the block; the peak profile is left unchanged. @@ -623,7 +623,7 @@ def test_restore_switchable_types_without_peak_tag_keeps_default(self): # ------------------------------------------------------------------ -# _get_valid_linked_phases +# _get_valid_linked_structures # ------------------------------------------------------------------ @@ -632,45 +632,45 @@ def __init__(self, names): self.names = list(names) -class TestGetValidLinkedPhases: - def test_no_linked_phases_warns_and_returns_empty(self, monkeypatch): +class TestGetValidLinkedStructures: + def test_no_linked_structures_warns_and_returns_empty(self, monkeypatch): from easydiffraction.datablocks.experiment.item import base as item_base - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) warnings: list[str] = [] monkeypatch.setattr(item_base.log, 'warning', warnings.append) - result = ex._get_valid_linked_phases(_FakeStructures([])) + result = ex._get_valid_linked_structures(_FakeStructures([])) assert result == [] - assert any('No linked phases defined' in w for w in warnings) + assert any('No linked structures defined' in w for w in warnings) def test_skips_phases_absent_from_structures(self, monkeypatch): from easydiffraction.datablocks.experiment.item import base as item_base - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) - ex.linked_phases.create(id='present', scale=1.0) - ex.linked_phases.create(id='absent', scale=1.0) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) + ex.linked_structures.create(structure_id='present', scale=1.0) + ex.linked_structures.create(structure_id='absent', scale=1.0) warnings: list[str] = [] monkeypatch.setattr(item_base.log, 'warning', warnings.append) - result = ex._get_valid_linked_phases(_FakeStructures(['present'])) + result = ex._get_valid_linked_structures(_FakeStructures(['present'])) assert len(result) == 1 - assert result[0].id.value == 'present' + assert result[0].structure_id.value == 'present' assert any("'absent' not" in w for w in warnings) def test_all_phases_missing_warns_returns_empty(self, monkeypatch): from easydiffraction.datablocks.experiment.item import base as item_base - ex = ConcretePd(name='pd1', type=_mk_type_powder_cwl_bragg()) - ex.linked_phases.create(id='absent', scale=1.0) + ex = ConcretePd(name='pd1', experiment_type=_mk_type_powder_cwl_bragg()) + ex.linked_structures.create(structure_id='absent', scale=1.0) warnings: list[str] = [] monkeypatch.setattr(item_base.log, 'warning', warnings.append) - result = ex._get_valid_linked_phases(_FakeStructures(['other'])) + result = ex._get_valid_linked_structures(_FakeStructures(['other'])) assert result == [] - assert any('None of the linked phases' in w for w in warnings) + assert any('None of the linked structures' in w for w in warnings) diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py index 1bbd9a916..93de635cc 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_pd.py @@ -36,7 +36,7 @@ def _mk_type_powder_tof_bragg(): def test_background_defaults_and_change(): - expt = BraggPdExperiment(name='e1', type=_mk_type_powder_cwl_bragg()) + expt = BraggPdExperiment(name='e1', experiment_type=_mk_type_powder_cwl_bragg()) # default background type assert expt.background.type == BackgroundFactory.default_tag() @@ -53,7 +53,7 @@ def test_background_defaults_and_change(): def test_load_ascii_data_rounds_and_defaults_sy(tmp_path: pytest.TempPathFactory): - expt = BraggPdExperiment(name='e1', type=_mk_type_powder_cwl_bragg()) + expt = BraggPdExperiment(name='e1', experiment_type=_mk_type_powder_cwl_bragg()) # Case 1: provide only two columns -> sy defaults to sqrt(y) and min clipped to 1.0 p = tmp_path / 'data2col.dat' @@ -90,15 +90,15 @@ def test_load_ascii_data_rounds_and_defaults_sy(tmp_path: pytest.TempPathFactory def test_bragg_pd_experiment_creates_beam_mode_specific_refln_collection(): - cwl_experiment = BraggPdExperiment(name='cwl', type=_mk_type_powder_cwl_bragg()) - tof_experiment = BraggPdExperiment(name='tof', type=_mk_type_powder_tof_bragg()) + cwl_experiment = BraggPdExperiment(name='cwl', experiment_type=_mk_type_powder_cwl_bragg()) + tof_experiment = BraggPdExperiment(name='tof', experiment_type=_mk_type_powder_tof_bragg()) assert isinstance(cwl_experiment.refln, PowderCwlReflnData) assert isinstance(tof_experiment.refln, PowderTofReflnData) def test_bragg_pd_experiment_disables_refln_for_crysfml_and_restores_it_for_cryspy(): - experiment = BraggPdExperiment(name='powder', type=_mk_type_powder_cwl_bragg()) + experiment = BraggPdExperiment(name='powder', experiment_type=_mk_type_powder_cwl_bragg()) assert isinstance(experiment.refln, PowderCwlReflnData) @@ -127,8 +127,8 @@ def calculate_pattern(self, structure, experiment, *, called_by_minimizer=False) del experiment, called_by_minimizer return structure.pattern - def last_powder_refln_records(self, structure, experiment, *, phase_id): - del experiment, phase_id + def last_powder_refln_records(self, structure, experiment, *, structure_id): + del experiment, structure_id if not self.return_records: return None return structure.records @@ -139,9 +139,9 @@ def __init__(self, name, pattern, records): self.pattern = pattern self.records = records - experiment = BraggPdExperiment(name='powder', type=_mk_type_powder_cwl_bragg()) - experiment.linked_phases.create(id='phase_a', scale=2.0) - experiment.linked_phases.create(id='phase_b', scale=3.0) + experiment = BraggPdExperiment(name='powder', experiment_type=_mk_type_powder_cwl_bragg()) + experiment.linked_structures.create(structure_id='phase_a', scale=2.0) + experiment.linked_structures.create(structure_id='phase_b', scale=3.0) experiment.data._create_items_set_xcoord_and_id(np.array([10.0, 20.0, 30.0])) experiment.data._set_intensity_meas(np.array([100.0, 110.0, 120.0])) @@ -151,7 +151,7 @@ def __init__(self, name, pattern, records): np.array([1.0, 2.0, 3.0]), [ PowderReflnRecord( - phase_id='phase_a', + structure_id='phase_a', d_spacing=2.1, sin_theta_over_lambda=0.25, index_h=1, @@ -168,7 +168,7 @@ def __init__(self, name, pattern, records): np.array([4.0, 5.0, 6.0]), [ PowderReflnRecord( - phase_id='phase_b', + structure_id='phase_b', d_spacing=1.8, sin_theta_over_lambda=0.28, index_h=2, @@ -189,7 +189,7 @@ def __init__(self, name, pattern, records): experiment.data._update() np.testing.assert_allclose(experiment.data.intensity_calc, np.array([14.0, 19.0, 24.0])) - np.testing.assert_array_equal(experiment.refln.phase_id, np.array(['phase_a', 'phase_b'])) + np.testing.assert_array_equal(experiment.refln.structure_id, np.array(['phase_a', 'phase_b'])) np.testing.assert_allclose(experiment.refln.two_theta, np.array([14.5, 18.5])) experiment._calculator.return_records = False @@ -213,8 +213,8 @@ def calculate_pattern(self, structure, experiment, *, called_by_minimizer=False) del experiment, called_by_minimizer return structure.pattern - def last_powder_refln_records(self, structure, experiment, *, phase_id): - del structure, experiment, phase_id + def last_powder_refln_records(self, structure, experiment, *, structure_id): + del structure, experiment, structure_id msg = 'Powder reflection metadata should not be requested' raise AssertionError(msg) @@ -223,8 +223,8 @@ def __init__(self, name, pattern): self.name = name self.pattern = pattern - experiment = BraggPdExperiment(name='powder', type=_mk_type_powder_cwl_bragg()) - experiment.linked_phases.create(id='phase_a', scale=2.0) + experiment = BraggPdExperiment(name='powder', experiment_type=_mk_type_powder_cwl_bragg()) + experiment.linked_structures.create(structure_id='phase_a', scale=2.0) experiment.data._create_items_set_xcoord_and_id(np.array([10.0, 20.0, 30.0])) experiment.data._set_intensity_meas(np.array([100.0, 110.0, 120.0])) diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py index 014f65fe4..cd334b4ed 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc.py @@ -30,7 +30,7 @@ def _load_ascii_data_to_experiment(self, data_path: str) -> int: def test_init_and_placeholder_no_crash(monkeypatch: pytest.MonkeyPatch): # Prevent logger from raising on attribute errors inside __init__ monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.WARN, raising=True) - expt = _ConcreteCwlSc(name='sc1', type=_mk_type_sc_bragg()) + expt = _ConcreteCwlSc(name='sc1', experiment_type=_mk_type_sc_bragg()) # Verify that experiment was created successfully with expected properties assert expt.name == 'sc1' - assert expt.type is not None + assert expt.experiment_type is not None diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc_coverage.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc_coverage.py index a8eeaa2a9..92c21dc46 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc_coverage.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_bragg_sc_coverage.py @@ -35,15 +35,15 @@ def _mk_type_sc_tof(): class TestCwlScExperiment: def test_init(self): - ex = CwlScExperiment(name='cwl_sc', type=_mk_type_sc_cwl()) + ex = CwlScExperiment(name='cwl_sc', experiment_type=_mk_type_sc_cwl()) assert ex.name == 'cwl_sc' - assert ex.type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL.value + assert ex.experiment_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL.value def test_type_info(self): assert CwlScExperiment.type_info.tag == 'bragg-sc-cwl' def test_load_ascii_5col(self, tmp_path): - ex = CwlScExperiment(name='cwl_sc', type=_mk_type_sc_cwl()) + ex = CwlScExperiment(name='cwl_sc', experiment_type=_mk_type_sc_cwl()) data = np.column_stack([ np.array([1, 0, 0]), np.array([0, 1, 0]), @@ -58,7 +58,7 @@ def test_load_ascii_5col(self, tmp_path): def test_load_ascii_too_few_columns(self, tmp_path, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.RAISE, raising=True) - ex = CwlScExperiment(name='cwl_sc', type=_mk_type_sc_cwl()) + ex = CwlScExperiment(name='cwl_sc', experiment_type=_mk_type_sc_cwl()) data = np.column_stack([np.array([1, 2, 3]), np.array([4, 5, 6])]) p = tmp_path / 'bad.dat' np.savetxt(p, data) @@ -66,12 +66,12 @@ def test_load_ascii_too_few_columns(self, tmp_path, monkeypatch): ex._load_ascii_data_to_experiment(str(p)) def test_switchable_categories(self): - ex = CwlScExperiment(name='cwl_sc', type=_mk_type_sc_cwl()) + ex = CwlScExperiment(name='cwl_sc', experiment_type=_mk_type_sc_cwl()) # extinction assert ex.extinction is not None assert isinstance(ex.extinction.type, str) - # linked crystal - assert ex.linked_crystal is not None + # linked structure + assert ex.linked_structure is not None # instrument assert ex.instrument is not None # refln @@ -80,20 +80,20 @@ def test_switchable_categories(self): def test_extinction_type_invalid(self): import pytest - ex = CwlScExperiment(name='cwl_sc', type=_mk_type_sc_cwl()) + ex = CwlScExperiment(name='cwl_sc', experiment_type=_mk_type_sc_cwl()) old = ex.extinction.type with pytest.raises(ValueError, match='Unsupported extinction type'): ex.extinction.type = 'bogus' assert ex.extinction.type == old def test_show_extinction_types(self, capsys): - ex = CwlScExperiment(name='cwl_sc', type=_mk_type_sc_cwl()) + ex = CwlScExperiment(name='cwl_sc', experiment_type=_mk_type_sc_cwl()) ex.extinction.show_supported() out = capsys.readouterr().out assert len(out) > 0 def test_show_extinction_types_includes_current(self, capsys): - ex = CwlScExperiment(name='cwl_sc', type=_mk_type_sc_cwl()) + ex = CwlScExperiment(name='cwl_sc', experiment_type=_mk_type_sc_cwl()) ex.extinction.show_supported() out = capsys.readouterr().out assert ex.extinction.type in out @@ -101,15 +101,15 @@ def test_show_extinction_types_includes_current(self, capsys): class TestTofScExperiment: def test_init(self): - ex = TofScExperiment(name='tof_sc', type=_mk_type_sc_tof()) + ex = TofScExperiment(name='tof_sc', experiment_type=_mk_type_sc_tof()) assert ex.name == 'tof_sc' - assert ex.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT.value + assert ex.experiment_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT.value def test_type_info(self): assert TofScExperiment.type_info.tag == 'bragg-sc-tof' def test_load_ascii_6col(self, tmp_path): - ex = TofScExperiment(name='tof_sc', type=_mk_type_sc_tof()) + ex = TofScExperiment(name='tof_sc', experiment_type=_mk_type_sc_tof()) data = np.column_stack([ np.array([1, 0, 0]), np.array([0, 1, 0]), @@ -125,7 +125,7 @@ def test_load_ascii_6col(self, tmp_path): def test_load_ascii_too_few_columns(self, tmp_path, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.RAISE, raising=True) - ex = TofScExperiment(name='tof_sc', type=_mk_type_sc_tof()) + ex = TofScExperiment(name='tof_sc', experiment_type=_mk_type_sc_tof()) data = np.column_stack([ np.array([1, 2]), np.array([0, 1]), @@ -140,6 +140,6 @@ def test_load_ascii_too_few_columns(self, tmp_path, monkeypatch): def test_load_ascii_nonexistent_file(self, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.RAISE, raising=True) - ex = TofScExperiment(name='tof_sc', type=_mk_type_sc_tof()) + ex = TofScExperiment(name='tof_sc', experiment_type=_mk_type_sc_tof()) with pytest.raises(OSError, match='No such file'): ex._load_ascii_data_to_experiment('/no/such/file.dat') diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py index d450e942e..654c5f26c 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory.py @@ -25,8 +25,8 @@ def test_experiment_factory_from_scratch(): scattering_type=ScatteringTypeEnum.BRAGG.value, ) # Instance should be created (BraggPdExperiment) - assert hasattr(ex, 'type') - assert ex.type.sample_form.value == SampleFormEnum.POWDER.value + assert hasattr(ex, 'experiment_type') + assert ex.experiment_type.sample_form.value == SampleFormEnum.POWDER.value def test_from_cif_str_restores_non_default_peak_profile_type(): diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory_coverage.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory_coverage.py index 690455c3f..85e4f6432 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_factory_coverage.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_factory_coverage.py @@ -22,9 +22,9 @@ def test_powder_bragg_cwl(self): scattering_type='bragg', ) assert ex.name == 'test_pd' - assert ex.type.sample_form.value == SampleFormEnum.POWDER.value - assert ex.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH.value - assert ex.type.scattering_type.value == ScatteringTypeEnum.BRAGG.value + assert ex.experiment_type.sample_form.value == SampleFormEnum.POWDER.value + assert ex.experiment_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH.value + assert ex.experiment_type.scattering_type.value == ScatteringTypeEnum.BRAGG.value def test_powder_bragg_tof(self): ex = ExperimentFactory.from_scratch( @@ -35,7 +35,7 @@ def test_powder_bragg_tof(self): scattering_type='bragg', ) assert ex.name == 'test_tof' - assert ex.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT.value + assert ex.experiment_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT.value def test_single_crystal_cwl(self): ex = ExperimentFactory.from_scratch( @@ -46,7 +46,7 @@ def test_single_crystal_cwl(self): scattering_type='bragg', ) assert ex.name == 'test_sc' - assert ex.type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL.value + assert ex.experiment_type.sample_form.value == SampleFormEnum.SINGLE_CRYSTAL.value def test_single_crystal_tof(self): ex = ExperimentFactory.from_scratch( @@ -57,7 +57,7 @@ def test_single_crystal_tof(self): scattering_type='bragg', ) assert ex.name == 'test_sc_tof' - assert ex.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT.value + assert ex.experiment_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT.value def test_total_scattering(self): ex = ExperimentFactory.from_scratch( @@ -65,14 +65,14 @@ def test_total_scattering(self): sample_form='powder', scattering_type='total', ) - assert ex.type.scattering_type.value == ScatteringTypeEnum.TOTAL.value + assert ex.experiment_type.scattering_type.value == ScatteringTypeEnum.TOTAL.value def test_defaults_used_when_none(self): ex = ExperimentFactory.from_scratch(name='defaults') - assert ex.type.sample_form.value == SampleFormEnum.default().value - assert ex.type.beam_mode.value == BeamModeEnum.default().value - assert ex.type.scattering_type.value == ScatteringTypeEnum.default().value - assert ex.type.radiation_probe.value == RadiationProbeEnum.default().value + assert ex.experiment_type.sample_form.value == SampleFormEnum.default().value + assert ex.experiment_type.beam_mode.value == BeamModeEnum.default().value + assert ex.experiment_type.scattering_type.value == ScatteringTypeEnum.default().value + assert ex.experiment_type.radiation_probe.value == RadiationProbeEnum.default().value class TestExperimentFactoryInstantiationBlocked: diff --git a/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py b/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py index 319057b7e..4c7d6e06c 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py +++ b/tests/unit/easydiffraction/datablocks/experiment/item/test_total_pd.py @@ -22,7 +22,7 @@ def _mk_type_powder_total(): def test_load_ascii_data_pdf(tmp_path: pytest.TempPathFactory): - expt = TotalPdExperiment(name='pdf1', type=_mk_type_powder_total()) + expt = TotalPdExperiment(name='pdf1', experiment_type=_mk_type_powder_total()) # Mock diffpy.utils.parsers.loaddata.loadData by creating a small parser module on sys.path data = np.column_stack([ diff --git a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py index 8ffe9af4a..45390c17c 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/test_collection.py +++ b/tests/unit/easydiffraction/datablocks/experiment/test_collection.py @@ -22,7 +22,7 @@ def __init__(self): class DummyExp(ExperimentBase): def __init__(self, name='e1'): - super().__init__(name=name, type=DummyType()) + super().__init__(name=name, experiment_type=DummyType()) def _load_ascii_data_to_experiment(self, data_path: str) -> int: return 0 diff --git a/tests/unit/easydiffraction/datablocks/experiment/test_collection_coverage.py b/tests/unit/easydiffraction/datablocks/experiment/test_collection_coverage.py index a46b0d78d..e28d13c85 100644 --- a/tests/unit/easydiffraction/datablocks/experiment/test_collection_coverage.py +++ b/tests/unit/easydiffraction/datablocks/experiment/test_collection_coverage.py @@ -29,7 +29,7 @@ class _DummyExp(ExperimentBase): """Lightweight experiment that records data-load calls.""" def __init__(self, name='e1', *, num_points=7): - super().__init__(name=name, type=_DummyType()) + super().__init__(name=name, experiment_type=_DummyType()) self._num_points = num_points self._loaded_paths = [] self._show_params_calls = 0 diff --git a/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_default.py b/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_default.py index e8e514cce..98915bd8c 100644 --- a/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_default.py +++ b/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_default.py @@ -65,36 +65,36 @@ def test_min_bond_distance_cutoff_is_numeric_descriptor(self): geom = Geom() assert isinstance(geom.min_bond_distance_cutoff, NumericDescriptor) - def test_bond_distance_incr_is_numeric_descriptor(self): + def test_bond_distance_inc_is_numeric_descriptor(self): geom = Geom() - assert isinstance(geom.bond_distance_incr, NumericDescriptor) + assert isinstance(geom.bond_distance_inc, NumericDescriptor) def test_default_min_bond_distance_cutoff(self): geom = Geom() assert geom.min_bond_distance_cutoff.value == 0.0 - def test_default_bond_distance_incr(self): + def test_default_bond_distance_inc(self): geom = Geom() - assert geom.bond_distance_incr.value == 0.25 + assert geom.bond_distance_inc.value == 0.25 def test_descriptor_names(self): geom = Geom() assert geom.min_bond_distance_cutoff.name == 'min_bond_distance_cutoff' - assert geom.bond_distance_incr.name == 'bond_distance_incr' + assert geom.bond_distance_inc.name == 'bond_distance_inc' def test_descriptor_descriptions(self): geom = Geom() assert geom.min_bond_distance_cutoff.description == ( 'Minimum permitted bonded distance (angstrom).' ) - assert geom.bond_distance_incr.description == ( + assert geom.bond_distance_inc.description == ( 'Increment added to the summed bonding radii (angstrom).' ) def test_parameters_lists_both_descriptors(self): geom = Geom() names = {p.name for p in geom.parameters} - assert names == {'min_bond_distance_cutoff', 'bond_distance_incr'} + assert names == {'min_bond_distance_cutoff', 'bond_distance_inc'} # ---------------------------------------------------------------------- @@ -102,17 +102,17 @@ def test_parameters_lists_both_descriptors(self): # ---------------------------------------------------------------------- -class TestGeomCifHandlers: +class TestGeomTagSpecs: def test_min_bond_distance_cutoff_cif_name(self): geom = Geom() - assert geom.min_bond_distance_cutoff._cif_handler.names == [ + assert geom.min_bond_distance_cutoff._tags.edi_names == [ '_geom.min_bond_distance_cutoff', ] - def test_bond_distance_incr_cif_name(self): + def test_bond_distance_inc_cif_name(self): geom = Geom() - assert geom.bond_distance_incr._cif_handler.names == [ - '_geom.bond_distance_incr', + assert geom.bond_distance_inc._tags.edi_names == [ + '_geom.bond_distance_inc', ] @@ -127,10 +127,10 @@ def test_set_min_bond_distance_cutoff(self): geom.min_bond_distance_cutoff = 0.5 assert geom.min_bond_distance_cutoff.value == 0.5 - def test_set_bond_distance_incr(self): + def test_set_bond_distance_inc(self): geom = Geom() - geom.bond_distance_incr = 0.4 - assert geom.bond_distance_incr.value == 0.4 + geom.bond_distance_inc = 0.4 + assert geom.bond_distance_inc.value == 0.4 def test_set_min_bond_distance_cutoff_to_zero_boundary(self): # The validator allows ge=0.0, so the lower boundary is valid. @@ -139,10 +139,10 @@ def test_set_min_bond_distance_cutoff_to_zero_boundary(self): geom.min_bond_distance_cutoff = 0.0 assert geom.min_bond_distance_cutoff.value == 0.0 - def test_set_bond_distance_incr_to_zero_boundary(self): + def test_set_bond_distance_inc_to_zero_boundary(self): geom = Geom() - geom.bond_distance_incr = 0.0 - assert geom.bond_distance_incr.value == 0.0 + geom.bond_distance_inc = 0.0 + assert geom.bond_distance_inc.value == 0.0 def test_set_min_bond_distance_cutoff_accepts_int(self): geom = Geom() @@ -168,11 +168,11 @@ def test_negative_min_bond_distance_cutoff_raises_in_raise_mode(self, monkeypatc with pytest.raises(TypeError): geom.min_bond_distance_cutoff = -1.0 - def test_negative_bond_distance_incr_raises_in_raise_mode(self, monkeypatch): + def test_negative_bond_distance_inc_raises_in_raise_mode(self, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.RAISE, raising=True) geom = Geom() with pytest.raises(TypeError): - geom.bond_distance_incr = -0.5 + geom.bond_distance_inc = -0.5 def test_negative_min_bond_distance_cutoff_kept_in_warn_mode(self, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.WARN, raising=True) @@ -181,11 +181,11 @@ def test_negative_min_bond_distance_cutoff_kept_in_warn_mode(self, monkeypatch): # The out-of-range write is rejected; the default is retained. assert geom.min_bond_distance_cutoff.value == 0.0 - def test_negative_bond_distance_incr_kept_in_warn_mode(self, monkeypatch): + def test_negative_bond_distance_inc_kept_in_warn_mode(self, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.WARN, raising=True) geom = Geom() - geom.bond_distance_incr = -0.5 - assert geom.bond_distance_incr.value == 0.25 + geom.bond_distance_inc = -0.5 + assert geom.bond_distance_inc.value == 0.25 def test_wrong_type_min_bond_distance_cutoff_kept_in_warn_mode(self, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.WARN, raising=True) @@ -193,11 +193,11 @@ def test_wrong_type_min_bond_distance_cutoff_kept_in_warn_mode(self, monkeypatch geom.min_bond_distance_cutoff = 'not-a-number' assert geom.min_bond_distance_cutoff.value == 0.0 - def test_wrong_type_bond_distance_incr_raises_in_raise_mode(self, monkeypatch): + def test_wrong_type_bond_distance_inc_raises_in_raise_mode(self, monkeypatch): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.RAISE, raising=True) geom = Geom() with pytest.raises(TypeError): - geom.bond_distance_incr = 'not-a-number' + geom.bond_distance_inc = 'not-a-number' # ---------------------------------------------------------------------- @@ -214,23 +214,23 @@ def test_as_cif_contains_both_tags(self): geom = Geom() cif = geom.as_cif assert '_geom.min_bond_distance_cutoff' in cif - assert '_geom.bond_distance_incr' in cif + assert '_geom.bond_distance_inc' in cif def test_as_cif_default_lines(self): geom = Geom() lines = geom.as_cif.splitlines() assert lines == [ '_geom.min_bond_distance_cutoff 0.', - '_geom.bond_distance_incr 0.25', + '_geom.bond_distance_inc 0.25', ] def test_as_cif_reflects_updated_values(self): geom = Geom() geom.min_bond_distance_cutoff = 0.8 - geom.bond_distance_incr = 0.3 + geom.bond_distance_inc = 0.3 cif = geom.as_cif assert '_geom.min_bond_distance_cutoff 0.8' in cif - assert '_geom.bond_distance_incr 0.3' in cif + assert '_geom.bond_distance_inc 0.3' in cif def test_from_cif_round_trip(self): import gemmi @@ -239,7 +239,7 @@ def test_from_cif_round_trip(self): # the round-trip is independent of the global Logger reaction. source = Geom() source.min_bond_distance_cutoff = 0.6 - source.bond_distance_incr = 0.45 + source.bond_distance_inc = 0.45 block = gemmi.cif.read_string(f'data_test\n\n{source.as_cif}\n').sole_block() @@ -247,4 +247,4 @@ def test_from_cif_round_trip(self): restored.from_cif(block) assert restored.min_bond_distance_cutoff.value == 0.6 - assert restored.bond_distance_incr.value == 0.45 + assert restored.bond_distance_inc.value == 0.45 diff --git a/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_factory.py b/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_factory.py index f023c2d7e..25db1fb2d 100644 --- a/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_factory.py +++ b/tests/unit/easydiffraction/datablocks/structure/categories/geom/test_factory.py @@ -73,7 +73,7 @@ def test_create_unknown_tag_raises_value_error(self): def test_create_default_has_expected_defaults(self): geom = GeomFactory.create('default') assert geom.min_bond_distance_cutoff.value == 0.0 - assert geom.bond_distance_incr.value == 0.25 + assert geom.bond_distance_inc.value == 0.25 class TestCreateDefaultFor: diff --git a/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_site_aniso.py b/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_site_aniso.py index 8e30cd548..46860adf1 100644 --- a/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_site_aniso.py +++ b/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_site_aniso.py @@ -80,7 +80,7 @@ def test_defaults(self): ) entry = AtomSiteAniso() - assert entry.label.value == '' + assert entry.id.value == '' assert entry.adp_11.value == 0.0 assert entry.adp_22.value == 0.0 assert entry.adp_33.value == 0.0 @@ -88,14 +88,14 @@ def test_defaults(self): assert entry.adp_13.value == 0.0 assert entry.adp_23.value == 0.0 - def test_label_setter(self): + def test_id_setter(self): from easydiffraction.datablocks.structure.categories.atom_site_aniso.default import ( AtomSiteAniso, ) entry = AtomSiteAniso() - entry.label = 'Si' - assert entry.label.value == 'Si' + entry.id = 'Si' + assert entry.id.value == 'Si' def test_tensor_setters(self): from easydiffraction.datablocks.structure.categories.atom_site_aniso.default import ( @@ -122,17 +122,23 @@ def test_dual_cif_names(self): ) entry = AtomSiteAniso() - # Default order: B first - assert entry._adp_11._cif_handler.names[0] == '_atom_site_aniso.B_11' - assert entry._adp_11._cif_handler.names[1] == '_atom_site_aniso.U_11' - - def test_identity_entry_name_follows_label(self): + # Canonical persistence name plus the B/U/beta import aliases, + # with the IUCr export convention defaulting to B first. + assert entry._adp_11._tags.edi_names == ['_atom_site_aniso.adp_11'] + assert entry._adp_11._tags.cif_names == [ + '_atom_site_aniso.B_11', + '_atom_site_aniso.U_11', + '_atom_site_aniso.beta_11', + ] + assert entry._adp_11._tags.cif_name == '_atom_site_aniso.B_11' + + def test_identity_entry_name_follows_id(self): from easydiffraction.datablocks.structure.categories.atom_site_aniso.default import ( AtomSiteAniso, ) entry = AtomSiteAniso() - entry.label = 'Fe1' + entry.id = 'Fe1' assert entry._identity.category_entry_name == 'Fe1' @@ -171,7 +177,7 @@ def test_skip_cif_serialization_isotropic(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure._sync_atom_site_aniso() assert structure.atom_site_aniso._skip_cif_serialization() is True @@ -179,7 +185,7 @@ def test_skip_cif_serialization_anisotropic(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure._sync_atom_site_aniso() structure.atom_sites['Si'].adp_type = 'Bani' assert structure.atom_site_aniso._skip_cif_serialization() is False @@ -195,7 +201,7 @@ def test_sync_does_not_add_iso_entries(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure._sync_atom_site_aniso() assert 'Si' not in structure.atom_site_aniso assert len(structure.atom_site_aniso) == 0 @@ -204,18 +210,18 @@ def test_sync_adds_aniso_entry(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure.atom_sites['Si'].adp_type = 'Bani' # adp_type setter triggers sync internally; verify idempotent on explicit call structure._sync_atom_site_aniso() assert 'Si' in structure.atom_site_aniso - assert structure.atom_site_aniso['Si'].label.value == 'Si' + assert structure.atom_site_aniso['Si'].id.value == 'Si' def test_sync_removes_entry_when_atom_switches_to_iso(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure.atom_sites['Si'].adp_type = 'Bani' assert 'Si' in structure.atom_site_aniso structure.atom_sites['Si'].adp_type = 'Biso' @@ -225,8 +231,8 @@ def test_sync_removes_stale_entries(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) - structure.atom_sites.create(label='O', type_symbol='O', adp_type='Biso', adp_iso=0.3) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='O', type_symbol='O', adp_type='Biso', adp_iso=0.3) structure.atom_sites['Si'].adp_type = 'Bani' structure.atom_sites['O'].adp_type = 'Bani' assert len(structure.atom_site_aniso) == 2 @@ -240,9 +246,9 @@ def test_sync_multiple_aniso_atoms(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='La', type_symbol='La', adp_type='Biso', adp_iso=0.5) - structure.atom_sites.create(label='Ba', type_symbol='Ba', adp_type='Biso', adp_iso=0.5) - structure.atom_sites.create(label='Co', type_symbol='Co', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='La', type_symbol='La', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Ba', type_symbol='Ba', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Co', type_symbol='Co', adp_type='Biso', adp_iso=0.5) for lbl in ('La', 'Ba', 'Co'): structure.atom_sites[lbl].adp_type = 'Bani' structure._sync_atom_site_aniso() @@ -262,7 +268,7 @@ def _make_structure_with_atom(self, adp_type='Biso', adp_iso=0.5): structure = Structure(name='test') structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', adp_type=adp_type, adp_iso=adp_iso, @@ -336,39 +342,45 @@ def test_biso_cif_names_default_order(self): from easydiffraction.datablocks.structure.categories.atom_sites.default import AtomSite site = AtomSite() - assert site._adp_iso._cif_handler.names[0] == '_atom_site.B_iso_or_equiv' + # Canonical persistence name plus the B/U import aliases, with the + # IUCr export convention defaulting to B first. + assert site._adp_iso._tags.edi_names == ['_atom_site.adp_iso'] + assert site._adp_iso._tags.cif_names == [ + '_atom_site.B_iso_or_equiv', + '_atom_site.U_iso_or_equiv', + ] + assert site._adp_iso._tags.cif_name == '_atom_site.B_iso_or_equiv' def test_uiso_reorders_iso_cif_names(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure._sync_atom_site_aniso() structure.atom_sites['Si'].adp_type = 'Uiso' assert ( - structure.atom_sites['Si']._adp_iso._cif_handler.names[0] - == '_atom_site.U_iso_or_equiv' + structure.atom_sites['Si']._adp_iso._tags.cif_names[0] == '_atom_site.U_iso_or_equiv' ) def test_bani_reorders_aniso_cif_names(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure._sync_atom_site_aniso() structure.atom_sites['Si'].adp_type = 'Bani' aniso = structure.atom_site_aniso['Si'] - assert aniso._adp_11._cif_handler.names[0] == '_atom_site_aniso.B_11' + assert aniso._adp_11._tags.cif_names[0] == '_atom_site_aniso.B_11' def test_uani_reorders_aniso_cif_names(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='Si', type_symbol='Si', adp_type='Biso', adp_iso=0.5) structure._sync_atom_site_aniso() structure.atom_sites['Si'].adp_type = 'Uani' aniso = structure.atom_site_aniso['Si'] - assert aniso._adp_11._cif_handler.names[0] == '_atom_site_aniso.U_11' + assert aniso._adp_11._tags.cif_names[0] == '_atom_site_aniso.U_11' # ------------------------------------------------------------------ @@ -381,8 +393,8 @@ def _make_structure(self): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='test') - structure.atom_sites.create(label='A', type_symbol='Si', adp_type='Biso', adp_iso=0.5) - structure.atom_sites.create(label='B', type_symbol='O', adp_type='Biso', adp_iso=0.3) + structure.atom_sites.create(id='A', type_symbol='Si', adp_type='Biso', adp_iso=0.5) + structure.atom_sites.create(id='B', type_symbol='O', adp_type='Biso', adp_iso=0.3) return structure def test_iso_atom_absent_from_collection(self): @@ -423,7 +435,7 @@ def _make_structure(self, adp_type='Biso', adp_iso=0.5): structure = Structure(name='test') structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', adp_type=adp_type, adp_iso=adp_iso, @@ -508,7 +520,7 @@ def _make_beta_structure(self): structure.cell.length_a = 5.0 structure.cell.length_b = 6.0 structure.cell.length_c = 8.0 - structure.atom_sites.create(label='Fe', type_symbol='Fe', adp_iso=0.0) + structure.atom_sites.create(id='Fe', type_symbol='Fe', adp_iso=0.0) structure.atom_sites['Fe'].adp_type = 'beta' structure._sync_atom_site_aniso() return structure @@ -531,8 +543,8 @@ def test_beta_cif_names_registered_on_aniso_components(self): ) entry = AtomSiteAniso() - assert '_atom_site_aniso.beta_11' in entry.adp_11._cif_handler.names - assert '_atom_site_aniso.beta_23' in entry.adp_23._cif_handler.names + assert '_atom_site_aniso.beta_11' in entry.adp_11._tags.cif_names + assert '_atom_site_aniso.beta_23' in entry.adp_23._tags.cif_names def test_off_diagonal_accepts_negative_value(self): from easydiffraction.datablocks.structure.categories.atom_site_aniso.default import ( diff --git a/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_sites.py b/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_sites.py index baf4374e4..f4894c44a 100644 --- a/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_sites.py +++ b/tests/unit/easydiffraction/datablocks/structure/categories/test_atom_sites.py @@ -54,7 +54,7 @@ def test_defaults(self): from easydiffraction.datablocks.structure.categories.atom_sites.default import AtomSite site = AtomSite() - assert site.label.value == 'Si' + assert site.id.value == 'Si' assert site.type_symbol.value == 'Tb' assert site.fract_x.value == 0.0 assert site.fract_y.value == 0.0 @@ -63,12 +63,12 @@ def test_defaults(self): assert site.adp_iso.value == 0.0 assert site.adp_type.value == 'Biso' - def test_label_setter(self): + def test_id_setter(self): from easydiffraction.datablocks.structure.categories.atom_sites.default import AtomSite site = AtomSite() - site.label = 'Fe1' - assert site.label.value == 'Fe1' + site.id = 'Fe1' + assert site.id.value == 'Fe1' def test_type_symbol_setter(self): from easydiffraction.datablocks.structure.categories.atom_sites.default import AtomSite @@ -120,7 +120,7 @@ def test_wyckoff_letter_allowed_values(self): # letters. structure = Structure(name='s') structure.space_group.name_h_m = 'P m -3 m' - structure.atom_sites.create(label='X', type_symbol='O', adp_iso=0.5) + structure.atom_sites.create(id='X', type_symbol='O', adp_iso=0.5) allowed = structure.atom_sites['X']._wyckoff_letter_allowed_values assert 'a' in allowed @@ -129,14 +129,12 @@ def test_uses_iucr_casing_with_legacy_aliases(self): site = AtomSite() - assert site.adp_type._cif_handler.names == [ - '_atom_site.ADP_type', - '_atom_site.adp_type', - ] - assert site.wyckoff_letter._cif_handler.names == [ + assert site.adp_type._tags.edi_names == ['_atom_site.adp_type'] + assert site.adp_type._tags.cif_names == ['_atom_site.ADP_type'] + assert site.wyckoff_letter._tags.edi_names == ['_atom_site.wyckoff_letter'] + assert site.wyckoff_letter._tags.cif_names == [ '_atom_site.Wyckoff_symbol', '_atom_site.Wyckoff_letter', - '_atom_site.wyckoff_letter', ] @@ -164,7 +162,7 @@ def _make_structure(self, adp_type='Biso', adp_iso=0.5): structure = Structure(name='test') structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', adp_type=adp_type, adp_iso=adp_iso, @@ -235,7 +233,7 @@ def _make_structure(self, adp_type='Biso', adp_iso=0.5): structure = Structure(name='test') structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', adp_type=adp_type, adp_iso=adp_iso, @@ -286,7 +284,7 @@ def _structure(name_hm='P m -3 m'): def test_fill_if_empty_on_update(self): structure = self._structure() - structure.atom_sites.create(label='A', type_symbol='O', adp_iso=0.5) + structure.atom_sites.create(id='A', type_symbol='O', adp_iso=0.5) structure._update_categories() atom = structure.atom_sites['A'] assert atom.wyckoff_letter.value == 'a' @@ -294,7 +292,7 @@ def test_fill_if_empty_on_update(self): def test_redetect_via_property_setter(self): structure = self._structure() - structure.atom_sites.create(label='A', type_symbol='O', adp_iso=0.5) + structure.atom_sites.create(id='A', type_symbol='O', adp_iso=0.5) structure._update_categories() structure.atom_sites['A'].fract_x = 0.3 structure._update_categories() @@ -302,7 +300,7 @@ def test_redetect_via_property_setter(self): def test_redetect_via_descriptor_value(self): structure = self._structure() - structure.atom_sites.create(label='A', type_symbol='O', adp_iso=0.5) + structure.atom_sites.create(id='A', type_symbol='O', adp_iso=0.5) structure._update_categories() structure.atom_sites['A'].fract_x.value = 0.3 structure._update_categories() @@ -313,7 +311,7 @@ def test_explicit_letter_preserved_on_first_update(self): # 'd' = (1/2,0,0); an explicit 'e' must be kept, not detected 'd'. structure = self._structure() structure.atom_sites.create( - label='E', + id='E', type_symbol='O', fract_x=0.5, fract_y=0.0, @@ -330,7 +328,7 @@ def test_invalid_explicit_letter_raises_on_update(self): import pytest structure = self._structure() - structure.atom_sites.create(label='Z', type_symbol='O', adp_iso=0.5, wyckoff_letter='z') + structure.atom_sites.create(id='Z', type_symbol='O', adp_iso=0.5, wyckoff_letter='z') with pytest.raises(ValueError, match='Invalid Wyckoff letter'): structure._update_categories() @@ -340,7 +338,7 @@ def test_same_letter_edit_snaps_off_orbit_coordinate(self): # to 0 while fract_x stays free. structure = self._structure() structure.atom_sites.create( - label='E', + id='E', type_symbol='O', fract_x=0.3, fract_y=0.0, @@ -359,7 +357,7 @@ def test_same_letter_edit_snaps_off_orbit_coordinate(self): def test_space_group_change_redetects(self): structure = self._structure() - structure.atom_sites.create(label='A', type_symbol='O', adp_iso=0.5) + structure.atom_sites.create(id='A', type_symbol='O', adp_iso=0.5) structure._update_categories() assert structure.atom_sites['A'].multiplicity.value == 1 # Pm-3m 'a' structure.space_group.name_h_m = 'F m -3 m' @@ -371,7 +369,7 @@ def test_space_group_change_redetects(self): def test_minimizer_path_keeps_letter_fixed(self): structure = self._structure() structure.atom_sites.create( - label='E', + id='E', type_symbol='O', fract_x=0.3, fract_y=0.0, @@ -391,7 +389,7 @@ def test_untabulated_group_preserves_letter_without_multiplicity(self, monkeypat monkeypatch.setattr(ecr, 'space_group_wyckoff_table', lambda *a, **k: None) structure = self._structure() - structure.atom_sites.create(label='X', type_symbol='O', adp_iso=0.5, wyckoff_letter='a') + structure.atom_sites.create(id='X', type_symbol='O', adp_iso=0.5, wyckoff_letter='a') structure._update_categories() atom = structure.atom_sites['X'] assert atom.wyckoff_letter.value == 'a' @@ -402,16 +400,16 @@ def test_no_record_contract_clears_multiplicity(self, monkeypatch): monkeypatch.setattr(ecr, 'space_group_wyckoff_table', lambda *a, **k: None) structure = self._structure() - structure.atom_sites.create(label='X', type_symbol='O', adp_iso=0.5) + structure.atom_sites.create(id='X', type_symbol='O', adp_iso=0.5) structure._update_categories() assert structure.atom_sites['X'].multiplicity.value is None - assert '_atom_site.site_symmetry_multiplicity' in structure.as_cif + assert '_atom_site.multiplicity' in structure.as_cif def test_cif_round_trip_redrives_letter(self): from easydiffraction.datablocks.structure.item.factory import StructureFactory structure = self._structure() - structure.atom_sites.create(label='A', type_symbol='O', adp_iso=0.5) + structure.atom_sites.create(id='A', type_symbol='O', adp_iso=0.5) structure._update_categories() reloaded = StructureFactory.from_cif_str(structure.as_cif) reloaded._update_categories() @@ -433,7 +431,7 @@ def _make_structure(self): structure.cell.length_a = 5.0 structure.cell.length_b = 6.0 structure.cell.length_c = 8.0 - structure.atom_sites.create(label='Fe', type_symbol='Fe', adp_iso=0.0) + structure.atom_sites.create(id='Fe', type_symbol='Fe', adp_iso=0.0) structure.atom_sites['Fe'].adp_type = 'Uani' structure._sync_atom_site_aniso() return structure @@ -528,7 +526,7 @@ def test_create_with_inline_beta_adp_type(self): # adp_type='beta' passed inline to create(): the atom is created # with a zero-filled aniso row, ready for direct assignment. structure.atom_sites.create( - label='Fe', + id='Fe', type_symbol='Fe', fract_x=0.1, fract_y=0.2, @@ -578,7 +576,7 @@ def test_uiso_to_beta_seeds_and_converts_diagonal(self): structure.cell.length_a = 5.0 structure.cell.length_b = 6.0 structure.cell.length_c = 8.0 - structure.atom_sites.create(label='Fe', type_symbol='Fe', adp_type='Uiso', adp_iso=0.01) + structure.atom_sites.create(id='Fe', type_symbol='Fe', adp_type='Uiso', adp_iso=0.01) structure.atom_sites['Fe'].adp_type = 'beta' aniso = structure.atom_site_aniso['Fe'] assert math.isclose(aniso.adp_11.value, 2.0 * math.pi**2 * 0.01 * (1.0 / 5.0) ** 2) @@ -609,7 +607,7 @@ def _make_cubic_bani(self): # P m -3 m Wyckoff a forces β11=β22=β33 and zero off-diagonals. structure.space_group.name_h_m = 'P m -3 m' structure.atom_sites.create( - label='Si', + id='Si', type_symbol='Si', adp_type='Bani', adp_iso=0.3, diff --git a/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py b/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py index dc338bb9c..7dc72e133 100644 --- a/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py +++ b/tests/unit/easydiffraction/datablocks/structure/categories/test_space_group.py @@ -8,25 +8,25 @@ def test_space_group_name_updates_it_code(): sg = SpaceGroup() # default name 'P 1' should set code to the first available sg.name_h_m = 'P 1' - assert sg.it_coordinate_system_code.value == sg._it_coordinate_system_code_allowed_values[0] + assert sg.coord_system_code.value == sg._coord_system_code_allowed_values[0] # changing name resets the code again sg.name_h_m = 'P -1' - assert sg.it_coordinate_system_code.value == sg._it_coordinate_system_code_allowed_values[0] + assert sg.coord_system_code.value == sg._coord_system_code_allowed_values[0] def test_space_group_uses_iucr_casing_with_legacy_aliases(): sg = SpaceGroup() - assert sg.name_h_m._cif_handler.names == [ + assert sg.name_h_m._tags.edi_names == ['_space_group.name_h_m'] + assert sg.name_h_m._tags.cif_names == [ '_space_group.name_H-M_alt', - '_space_group.name_h_m', '_space_group_name_H-M_alt', '_symmetry.space_group_name_H-M', '_symmetry_space_group_name_H-M', ] - assert sg.it_coordinate_system_code._cif_handler.names == [ + assert sg.coord_system_code._tags.edi_names == ['_space_group.coord_system_code'] + assert sg.coord_system_code._tags.cif_names == [ '_space_group.IT_coordinate_system_code', - '_space_group.it_coordinate_system_code', '_space_group_IT_coordinate_system_code', '_symmetry.IT_coordinate_system_code', '_symmetry_IT_coordinate_system_code', diff --git a/tests/unit/easydiffraction/datablocks/structure/item/test_base_coverage.py b/tests/unit/easydiffraction/datablocks/structure/item/test_base_coverage.py index 74d9e9e7e..32650118c 100644 --- a/tests/unit/easydiffraction/datablocks/structure/item/test_base_coverage.py +++ b/tests/unit/easydiffraction/datablocks/structure/item/test_base_coverage.py @@ -92,7 +92,7 @@ def test_atom_sites_setter_replaces_instance(self, structure): class TestStructureDisplay: - def test_show_as_cif(self, structure, capsys): - structure.show_as_cif() + def test_show_as_text(self, structure, capsys): + structure.show_as_text() out = capsys.readouterr().out assert 'test_struct' in out diff --git a/tests/unit/easydiffraction/display/plotters/test_ascii.py b/tests/unit/easydiffraction/display/plotters/test_ascii.py index e8579a2c9..903e9e3f7 100644 --- a/tests/unit/easydiffraction/display/plotters/test_ascii.py +++ b/tests/unit/easydiffraction/display/plotters/test_ascii.py @@ -129,7 +129,7 @@ def test_ascii_plotter_plot_powder_meas_vs_calc_announces_plotly_only_bragg_row( y_resid=np.array([0.5, -0.5, 1.0]), bragg_tick_sets=( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([0.5]), h=np.array([1]), k=np.array([0]), diff --git a/tests/unit/easydiffraction/display/plotters/test_plotly.py b/tests/unit/easydiffraction/display/plotters/test_plotly.py index 932e87f99..da42dd0ff 100644 --- a/tests/unit/easydiffraction/display/plotters/test_plotly.py +++ b/tests/unit/easydiffraction/display/plotters/test_plotly.py @@ -494,7 +494,7 @@ def test_get_bragg_tick_trace_includes_peak_metadata(): trace = PlotlyPlotter._get_bragg_tick_trace( tick_set=BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5, 2.5]), h=np.array([1, 2]), k=np.array([0, 1]), @@ -536,7 +536,7 @@ def fake_show_figure(self, fig): y_meas_su=np.array([0.2, 0.3, 0.4]), bragg_tick_sets=( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5]), h=np.array([1]), k=np.array([0]), @@ -545,7 +545,7 @@ def fake_show_figure(self, fig): f_calc=np.array([10.0]), ), BraggTickSet( - phase_id='phase-b', + structure_id='phase-b', x=np.array([2.5]), h=np.array([2]), k=np.array([1]), @@ -650,7 +650,7 @@ def fake_show_figure(self, fig): y_resid=np.array([1.0, 1.0, 0.5]), bragg_tick_sets=( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5]), h=np.array([1]), k=np.array([0]), @@ -731,7 +731,7 @@ def test_bragg_row_height_pixels_scale_linearly_with_phase_count(): y_resid=np.array([0.0, 0.0]), bragg_tick_sets=( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5]), h=np.array([1]), k=np.array([0]), @@ -754,7 +754,7 @@ def test_bragg_row_height_pixels_scale_linearly_with_phase_count(): bragg_tick_sets=( single_phase.bragg_tick_sets[0], BraggTickSet( - phase_id='phase-b', + structure_id='phase-b', x=np.array([2.5]), h=np.array([2]), k=np.array([1]), @@ -792,7 +792,7 @@ def fake_show_figure(self, fig): def plot_spec(phase_count: int) -> PowderMeasVsCalcSpec: bragg_tick_sets = tuple( BraggTickSet( - phase_id=f'phase-{idx}', + structure_id=f'phase-{idx}', x=np.array([1.0 + idx]), h=np.array([idx + 1]), k=np.array([0]), @@ -859,7 +859,7 @@ def fake_show_figure(self, fig): y_resid=np.array([1.0, 1.0, 0.5]), bragg_tick_sets=( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5]), h=np.array([1]), k=np.array([0]), @@ -896,7 +896,7 @@ def fake_show_figure(self, fig): bragg_tick_sets = ( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5]), h=np.array([1]), k=np.array([0]), diff --git a/tests/unit/easydiffraction/display/structure/test_builder.py b/tests/unit/easydiffraction/display/structure/test_builder.py index 7dfad78b3..0ac818819 100644 --- a/tests/unit/easydiffraction/display/structure/test_builder.py +++ b/tests/unit/easydiffraction/display/structure/test_builder.py @@ -58,7 +58,7 @@ def _add_atom( adp_iso=0.5, ): structure.atom_sites.create( - label=label, + id=label, type_symbol=type_symbol, fract_x=fract_x, fract_y=fract_y, @@ -496,7 +496,7 @@ def test_min_distance_cutoff_suppresses_bond(self): ) assert scene.bonds == () - def test_bond_distance_incr_enables_distant_bond(self): + def test_bond_distance_inc_enables_distant_bond(self): # Two atoms 2.5 angstrom apart (beyond summed covalent radii) # bond only once the increment is generous enough. structure = _make_structure('stretch') @@ -504,7 +504,7 @@ def test_bond_distance_incr_enables_distant_bond(self): _add_atom(structure, label='Fe2', type_symbol='Fe', fract_x=0.5, fract_y=0.0, fract_z=0.0) structure._sync_atom_site_aniso() - structure.geom.bond_distance_incr = 0.0 + structure.geom.bond_distance_inc = 0.0 scene_tight = build_scene( structure, style=StructureStyle(), @@ -512,7 +512,7 @@ def test_bond_distance_incr_enables_distant_bond(self): features=frozenset({'bonds'}), ) - structure.geom.bond_distance_incr = 5.0 + structure.geom.bond_distance_inc = 5.0 scene_loose = build_scene( structure, style=StructureStyle(), @@ -646,13 +646,11 @@ def test_default_atom_view_is_covalent(self): def test_default_color_scheme_is_jmol(self): assert StructureStyle().color_scheme.value == ColorSchemeEnum.JMOL.value - def test_atom_view_cif_handler_name(self): - assert StructureStyle().atom_view._cif_handler.names == ['_structure_style.atom_view'] + def test_atom_view_tags_name(self): + assert StructureStyle().atom_view._tags.edi_names == ['_structure_style.atom_view'] - def test_color_scheme_cif_handler_name(self): - assert StructureStyle().color_scheme._cif_handler.names == [ - '_structure_style.color_scheme' - ] + def test_color_scheme_tags_name(self): + assert StructureStyle().color_scheme._tags.edi_names == ['_structure_style.color_scheme'] def test_invalid_atom_view_rejected(self): with pytest.raises(ValueError, match='not a valid AtomViewEnum'): diff --git a/tests/unit/easydiffraction/display/tablers/test_pandas.py b/tests/unit/easydiffraction/display/tablers/test_pandas.py index d5b13592b..083f675c0 100644 --- a/tests/unit/easydiffraction/display/tablers/test_pandas.py +++ b/tests/unit/easydiffraction/display/tablers/test_pandas.py @@ -112,6 +112,23 @@ def test_html_special_characters_are_escaped(self): assert '<b>&x' in html assert '<b>' not in html + def test_table_link_becomes_anchor(self): + from easydiffraction.display.links import TableLink + + link = TableLink( + text='length_a', + url='https://example.test/docs?x=1&y=2', + title='Docs for length_a', + ) + + html = _backend().build_renderable(['left'], _indexed({'A': [link]})) + + assert ( + '<a href="https://example.test/docs?x=1&y=2" ' + 'title="Docs for length_a" target="_blank" rel="noopener noreferrer">' + 'length_a</a>' + ) in html + def test_render_displays_inline_html(self, monkeypatch): import easydiffraction.display.tablers.pandas as mod diff --git a/tests/unit/easydiffraction/display/tablers/test_rich.py b/tests/unit/easydiffraction/display/tablers/test_rich.py index c7599865a..d92f66986 100644 --- a/tests/unit/easydiffraction/display/tablers/test_rich.py +++ b/tests/unit/easydiffraction/display/tablers/test_rich.py @@ -22,6 +22,23 @@ def test_build_renderable_returns_table(self): table = backend.build_renderable(['left'], df) assert isinstance(table, Table) + def test_table_link_becomes_rich_link_text(self): + from rich.text import Text + + from easydiffraction.display.links import TableLink + from easydiffraction.display.tablers.rich import RichTableBackend + + backend = RichTableBackend() + df = pd.DataFrame({'parameter': [TableLink('length_a', 'https://example.test/docs')]}) + df.index += 1 + + table = backend.build_renderable(['left'], df) + cell = next(iter(table.columns[1].cells)) + + assert isinstance(cell, Text) + assert str(cell) == 'length_a' + assert cell.style == 'link https://example.test/docs' + def test_to_html_returns_string(self): from easydiffraction.display.tablers.rich import RichTableBackend @@ -32,6 +49,11 @@ def test_to_html_returns_string(self): html = backend._to_html(table) assert isinstance(html, str) assert '<pre' in html + # Compact line spacing merged into a single <pre> style attribute + # (no duplicate), with Rich's own font-family preserved. + assert html.count('<pre style=') == 1 + assert 'line-height:1.2 !important' in html + assert 'font-family:' in html def test_render_prints_to_console(self, capsys): from easydiffraction.display.tablers.rich import RichTableBackend diff --git a/tests/unit/easydiffraction/display/test_links.py b/tests/unit/easydiffraction/display/test_links.py new file mode 100644 index 000000000..86bed0ec4 --- /dev/null +++ b/tests/unit/easydiffraction/display/test_links.py @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Tests for display/links.py.""" + + +def test_table_link_behaves_like_visible_text(): + from easydiffraction.display.links import TableLink + + cell = TableLink('length_a', 'https://example.test/docs', title='Docs') + + assert cell == 'length_a' + assert str(cell) == 'length_a' + assert cell.text == 'length_a' + assert cell.url == 'https://example.test/docs' + assert cell.title == 'Docs' + + +def test_parameter_docs_link_uses_parameter_url(): + from easydiffraction.display.links import TableLink + from easydiffraction.display.links import parameter_docs_link + + class Parameter: + name = 'length_a' + url = 'https://example.test/docs' + + cell = parameter_docs_link(Parameter()) + + assert isinstance(cell, TableLink) + assert cell == 'length_a' + assert cell.url == 'https://example.test/docs' + + +def test_parameter_docs_link_falls_back_to_plain_name(): + from easydiffraction.display.links import parameter_docs_link + + class Parameter: + name = 'length_a' + + assert parameter_docs_link(Parameter()) == 'length_a' diff --git a/tests/unit/easydiffraction/display/test_plotting.py b/tests/unit/easydiffraction/display/test_plotting.py index 286065b78..9244ea4a7 100644 --- a/tests/unit/easydiffraction/display/test_plotting.py +++ b/tests/unit/easydiffraction/display/test_plotting.py @@ -85,7 +85,7 @@ def test_plot_param_series_reads_fit_result_columns_from_csv( from easydiffraction.project.project import Project project = Project(name='series') - project.info.path = tmp_path + project.metadata.path = tmp_path analysis_dir = tmp_path / 'analysis' analysis_dir.mkdir(parents=True) @@ -209,7 +209,7 @@ def __init__(self): class Expt: def __init__(self, pattern, expt_type): self.data = pattern - self.type = expt_type + self.experiment_type = expt_type p._plot_meas_vs_calc_data( Expt(Ptn(two_theta=None, intensity_meas=None, intensity_calc=None), ExptType()), @@ -316,7 +316,7 @@ def test_extract_bragg_tick_sets_groups_and_filters(): from easydiffraction.display.plotting import XAxisType class Refln: - phase_id = np.array(['phase-a', 'phase-a', 'phase-b', 'phase-b']) + structure_id = np.array(['phase-a', 'phase-a', 'phase-b', 'phase-b']) two_theta = np.array([0.5, 1.5, 2.5, 3.5]) index_h = np.array([1, 2, 3, 4]) index_k = np.array([0, 1, 1, 2]) @@ -335,7 +335,7 @@ class Experiment: x_max=3.0, ) - assert [tick_set.phase_id for tick_set in tick_sets] == ['phase-a', 'phase-b'] + assert [tick_set.structure_id for tick_set in tick_sets] == ['phase-a', 'phase-b'] assert np.allclose(tick_sets[0].x, np.array([1.5])) assert np.array_equal(tick_sets[0].h, np.array([2])) assert np.array_equal(tick_sets[1].h, np.array([3])) @@ -1005,7 +1005,7 @@ class Pattern: intensity_bkg = np.array([1.0, 1.0, 1.0]) class Experiment: - type = ExptType() + experiment_type = ExptType() data = Pattern() plotter = Plotter() @@ -1062,7 +1062,7 @@ class ExptType: beam_mode = type('B', (), {'value': BeamModeEnum.CONSTANT_WAVELENGTH})() class Experiment: - type = ExptType() + experiment_type = ExptType() class Project: experiments = {'hrpt': Experiment()} @@ -1163,7 +1163,7 @@ class Pattern: intensity_bkg = np.array([1.0, 1.0, 1.0]) class Experiment: - type = ExptType() + experiment_type = ExptType() data = Pattern() plotter = Plotter() @@ -1234,7 +1234,7 @@ class Pattern: intensity_bkg = np.array([1.0, 1.0, 1.0]) class Refln: - phase_id = np.array(['phase-a']) + structure_id = np.array(['phase-a']) two_theta = np.array([2.0]) index_h = np.array([1]) index_k = np.array([0]) @@ -1249,7 +1249,7 @@ class ExptType: class Experiment: data = Pattern() - type = ExptType() + experiment_type = ExptType() refln = Refln() plotter = Plotter() @@ -1539,7 +1539,7 @@ class ExptType: beam_mode = type('B', (), {'value': BeamModeEnum.CONSTANT_WAVELENGTH})() class Experiment: - type = ExptType() + experiment_type = ExptType() class Project: experiments = {'hrpt': Experiment()} @@ -1600,7 +1600,7 @@ class Pattern: intensity_meas = np.array([10.0, 20.0, 30.0]) class Experiment: - type = ExptType() + experiment_type = ExptType() data = Pattern() class Project: @@ -1671,7 +1671,7 @@ def test_extract_bragg_tick_sets_uses_derived_d_spacing_for_cwl_ticks(): from easydiffraction.utils.utils import twotheta_to_d class Refln: - phase_id = np.array(['phase-a']) + structure_id = np.array(['phase-a']) two_theta = np.array([20.0]) d_spacing = np.array([999.0]) index_h = np.array([1]) @@ -1725,7 +1725,7 @@ class Pattern: intensity_calc = np.array([9.0, 18.0, 27.0, 39.0]) class Refln: - phase_id = np.array(['phase-a', 'phase-a', 'phase-b']) + structure_id = np.array(['phase-a', 'phase-a', 'phase-b']) two_theta = np.array([0.5, 1.5, 2.0]) index_h = np.array([1, 2, 3]) index_k = np.array([0, 1, 1]) @@ -1740,7 +1740,7 @@ class ExptType: class Experiment: data = Pattern() - type = ExptType() + experiment_type = ExptType() refln = Refln() plotter = Plotter() @@ -1760,7 +1760,7 @@ class Experiment: assert np.allclose(call.y_bkg, np.array([2.0, 3.0])) assert np.allclose(call.y_calc, np.array([18.0, 27.0])) assert np.allclose(call.y_resid, np.array([2.0, 3.0])) - assert [tick_set.phase_id for tick_set in call.bragg_tick_sets] == [ + assert [tick_set.structure_id for tick_set in call.bragg_tick_sets] == [ 'phase-a', 'phase-b', ] @@ -1789,7 +1789,7 @@ class Pattern: intensity_calc = np.array([99.0, 108.0, 104.0]) class Refln: - phase_id = np.array(['phase-a']) + structure_id = np.array(['phase-a']) time_of_flight = np.array([11.0]) index_h = np.array([1]) index_k = np.array([0]) @@ -1804,7 +1804,7 @@ class ExptType: class Experiment: data = Pattern() - type = ExptType() + experiment_type = ExptType() refln = Refln() plotter = Plotter() @@ -1817,7 +1817,7 @@ class Experiment: call = captured['powder_meas_vs_calc'] assert np.allclose(call.x, np.array([10.0, 11.0, 12.0])) - assert [tick_set.phase_id for tick_set in call.bragg_tick_sets] == ['phase-a'] + assert [tick_set.structure_id for tick_set in call.bragg_tick_sets] == ['phase-a'] assert np.allclose(call.bragg_tick_sets[0].x, np.array([11.0])) @@ -1842,7 +1842,7 @@ class Pattern: intensity_calc = np.array([99.0, 108.0, 104.0]) class Refln: - phase_id = np.array([1, 1, 2]) + structure_id = np.array([1, 1, 2]) time_of_flight = np.array([10.0, 11.0, 12.0]) index_h = np.array([1, 2, 3]) index_k = np.array([0, 1, 1]) @@ -1857,7 +1857,7 @@ class ExptType: class Experiment: data = Pattern() - type = ExptType() + experiment_type = ExptType() refln = Refln() plotter = Plotter() @@ -1869,7 +1869,7 @@ class Experiment: ) call = captured['powder_meas_vs_calc'] - assert [tick_set.phase_id for tick_set in call.bragg_tick_sets] == ['1', '2'] + assert [tick_set.structure_id for tick_set in call.bragg_tick_sets] == ['1', '2'] assert np.allclose(call.bragg_tick_sets[0].x, np.array([10.0, 11.0])) assert np.allclose(call.bragg_tick_sets[1].x, np.array([12.0])) @@ -1895,7 +1895,7 @@ class Pattern: intensity_calc = np.array([99.0, 108.0, 104.0]) class Refln: - phase_id = np.array(['phase-a']) + structure_id = np.array(['phase-a']) time_of_flight = np.array([8.0]) index_h = np.array([1]) index_k = np.array([0]) @@ -1910,7 +1910,7 @@ class ExptType: class Experiment: data = Pattern() - type = ExptType() + experiment_type = ExptType() refln = Refln() plotter = Plotter() @@ -1968,7 +1968,7 @@ class ExptType: class Experiment: data = Pattern() - type = ExptType() + experiment_type = ExptType() plotter = Plotter() plotter._backend = FakeBackend() @@ -2018,7 +2018,7 @@ class ExptType: class Experiment: data = Pattern() - type = ExptType() + experiment_type = ExptType() plotter = Plotter() plotter._backend = FakeBackend() @@ -2273,7 +2273,7 @@ class Project: assert len(fig.layout.shapes) == 15 -def test_plot_param_correlations_limits_default_table_to_six_parameters(monkeypatch): +def test_plot_param_correlations_limits_default_table_to_five_parameters(monkeypatch): from easydiffraction.display.plotting import Plotter from easydiffraction.display.tables import TableRenderer @@ -2329,6 +2329,9 @@ class Project: p._set_project(Project()) p.plot_param_correlations() + # The default cap is DEFAULT_CORRELATION_MAX_PARAMETERS (5), so the + # weakest-correlated parameter (p6, |corr| 0.91) is trimmed and the + # auto threshold settles at 0.92. df = captured['df'] assert [column.strip() for column in df.columns.get_level_values(0)] == [ 'parameter', @@ -2337,24 +2340,20 @@ class Project: '3', '4', '5', - '6', ] - assert list(df.index) == [0, 1, 2, 3, 4, 5] + assert list(df.index) == [0, 1, 2, 3, 4] assert list(df.iloc[:, 0]) == [ 'phase.scale', 'phase.cell.length_a', 'phase.background', 'phase.profile.u', 'phase.profile.v', - 'phase.profile.w', ] assert df.iloc[0, 1] == '' assert _strip_markup(df.iloc[1, 1]).strip() == '0.95' assert _strip_markup(df.iloc[2, 2]).strip() == '0.94' assert _strip_markup(df.iloc[3, 3]).strip() == '0.93' assert _strip_markup(df.iloc[4, 4]).strip() == '0.92' - assert _strip_markup(df.iloc[5, 5]).strip() == '0.91' - assert df.iloc[5, 6] == '' def test_plot_posterior_pairs_uses_default_max_parameter_limit(monkeypatch): diff --git a/tests/unit/easydiffraction/display/test_plotting_coverage.py b/tests/unit/easydiffraction/display/test_plotting_coverage.py index aa9f1f453..cec75b42c 100644 --- a/tests/unit/easydiffraction/display/test_plotting_coverage.py +++ b/tests/unit/easydiffraction/display/test_plotting_coverage.py @@ -511,7 +511,7 @@ class Data: class Expt: data = Data() - type = ExptType() + experiment_type = ExptType() def _update_categories(self): pass @@ -691,7 +691,7 @@ class FakeAnalysis: _parameter_snapshots = {'expt1': {'param_a': {}}} class FakeProject: - info = SimpleNamespace(path=None) + metadata = SimpleNamespace(path=None) experiments = {'expt1': object()} analysis = FakeAnalysis() @@ -722,7 +722,7 @@ def test_uses_csv_when_results_file_present(self, monkeypatch, tmp_path): captured = {} class FakeProject: - info = SimpleNamespace(path=str(tmp_path)) + metadata = SimpleNamespace(path=str(tmp_path)) experiments = {} analysis = SimpleNamespace(_parameter_snapshots={}) @@ -755,7 +755,7 @@ def test_warns_when_no_fitted_params(self, monkeypatch, capsys): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.WARN, raising=True) class FakeProject: - info = SimpleNamespace(path=None) + metadata = SimpleNamespace(path=None) analysis = SimpleNamespace(_parameter_snapshots={}) p = Plotter() @@ -776,7 +776,7 @@ def test_plots_each_descriptor_and_skips_missing(self, monkeypatch, capsys): # Two fitted names; only one has a descriptor in the project. monkeypatch.setattr( Plotter, - '_collect_fitted_param_unique_names', + '_collect_fitted_parameter_unique_names', lambda self: ['present', 'missing'], ) descriptor = SimpleNamespace(unique_name='present') @@ -797,7 +797,7 @@ def test_plots_each_descriptor_and_skips_missing(self, monkeypatch, capsys): # ------------------------------------------------------------------ -# Plotter._collect_fitted_param_unique_names +# Plotter._collect_fitted_parameter_unique_names # ------------------------------------------------------------------ @@ -815,12 +815,12 @@ def test_from_csv_filters_meta_and_diffrn_and_uncertainty(self, tmp_path): (analysis_dir / 'results.csv').write_text(header + '1,2,0.1,300\n') class FakeProject: - info = SimpleNamespace(path=str(tmp_path)) + metadata = SimpleNamespace(path=str(tmp_path)) analysis = SimpleNamespace(_parameter_snapshots={}) p = Plotter() p._set_project(FakeProject()) - assert p._collect_fitted_param_unique_names() == ['param_a'] + assert p._collect_fitted_parameter_unique_names() == ['param_a'] def test_from_snapshots_when_no_csv(self): from types import SimpleNamespace @@ -828,12 +828,12 @@ def test_from_snapshots_when_no_csv(self): from easydiffraction.display.plotting import Plotter class FakeProject: - info = SimpleNamespace(path=None) + metadata = SimpleNamespace(path=None) analysis = SimpleNamespace(_parameter_snapshots={'e1': {'param_a': {}, 'param_b': {}}}) p = Plotter() p._set_project(FakeProject()) - assert p._collect_fitted_param_unique_names() == ['param_a', 'param_b'] + assert p._collect_fitted_parameter_unique_names() == ['param_a', 'param_b'] def test_empty_when_no_csv_and_no_snapshots(self): from types import SimpleNamespace @@ -841,12 +841,12 @@ def test_empty_when_no_csv_and_no_snapshots(self): from easydiffraction.display.plotting import Plotter class FakeProject: - info = SimpleNamespace(path=None) + metadata = SimpleNamespace(path=None) analysis = SimpleNamespace(_parameter_snapshots={}) p = Plotter() p._set_project(FakeProject()) - assert p._collect_fitted_param_unique_names() == [] + assert p._collect_fitted_parameter_unique_names() == [] # ------------------------------------------------------------------ @@ -1096,7 +1096,7 @@ def _fit_results(self, multipliers): from types import SimpleNamespace parameters = [ - SimpleNamespace(unique_name=f'p{i}', fit_bounds_uncertainty_multiplier=mult) + SimpleNamespace(unique_name=f'p{i}', bounds_uncertainty_multiplier=mult) for i, mult in enumerate(multipliers) ] return SimpleNamespace(parameters=parameters) @@ -1358,7 +1358,7 @@ def test_time_of_flight_path(self): instrument = SimpleNamespace( calib_d_to_tof_offset=SimpleNamespace(value=0.0), calib_d_to_tof_linear=SimpleNamespace(value=1.0), - calib_d_to_tof_quad=SimpleNamespace(value=0.0), + calib_d_to_tof_quadratic=SimpleNamespace(value=0.0), ) experiment = SimpleNamespace(instrument=instrument) result = Plotter._bragg_tick_d_spacing(refln=refln, experiment=experiment) @@ -1421,9 +1421,9 @@ def test_arrays_missing_field_warns_and_returns_none(self, monkeypatch, capsys): monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.WARN, raising=True) - # phase_id present but f_calc missing -> returns None. + # structure_id present but f_calc missing -> returns None. refln = SimpleNamespace( - phase_id=np.array(['a']), + structure_id=np.array(['a']), index_h=np.array([1]), index_k=np.array([0]), index_l=np.array([1]), @@ -3367,7 +3367,7 @@ class ExptType: scattering_type = type('S', (), {'value': ScatteringTypeEnum.BRAGG})() beam_mode = type('B', (), {'value': BeamModeEnum.CONSTANT_WAVELENGTH})() - experiment = SimpleNamespace(type=ExptType()) + experiment = SimpleNamespace(experiment_type=ExptType()) project = SimpleNamespace(experiments={'E1': experiment}) p = Plotter() @@ -3599,7 +3599,7 @@ def test_group_splits_by_phase(self): from easydiffraction.display.plotting import Plotter arrays = { - 'phase_id': np.array(['a', 'a', 'b']), + 'structure_id': np.array(['a', 'a', 'b']), 'index_h': np.array([1, 2, 3]), 'index_k': np.array([0, 0, 0]), 'index_l': np.array([1, 1, 1]), @@ -3609,7 +3609,7 @@ def test_group_splits_by_phase(self): } mask = np.array([True, True, True]) tick_sets = Plotter._group_bragg_tick_sets(arrays=arrays, mask=mask) - assert [ts.phase_id for ts in tick_sets] == ['a', 'b'] + assert [ts.structure_id for ts in tick_sets] == ['a', 'b'] np.testing.assert_allclose(tick_sets[0].x, [0.5, 1.5]) np.testing.assert_allclose(tick_sets[1].f_calc, [5.0]) @@ -3627,7 +3627,7 @@ def test_no_ticks_in_window_returns_empty(self): from easydiffraction.display.plotting import XAxisType class Refln: - phase_id = np.array(['phase-a']) + structure_id = np.array(['phase-a']) two_theta = np.array([5.0]) index_h = np.array([1]) index_k = np.array([0]) @@ -3972,8 +3972,8 @@ def _corr_row(i_name, j_name, value, source_kind): from types import SimpleNamespace return SimpleNamespace( - param_unique_name_i=SimpleNamespace(value=i_name), - param_unique_name_j=SimpleNamespace(value=j_name), + parameter_unique_name_i=SimpleNamespace(value=i_name), + parameter_unique_name_j=SimpleNamespace(value=j_name), correlation=SimpleNamespace(value=value), source_kind=SimpleNamespace(value=source_kind), ) @@ -4234,7 +4234,7 @@ def _experiment(self): intensity_meas=np.array([10.0, 12.0, 11.0]), intensity_bkg=np.array([1.0, 1.0, 1.0]), ) - return SimpleNamespace(type=expt_type, data=pattern) + return SimpleNamespace(experiment_type=expt_type, data=pattern) def test_plotly_band_draws_builds_composite_spec(self, monkeypatch): from types import SimpleNamespace @@ -4344,7 +4344,7 @@ def _make_project(self, sample_form, scattering_type): scattering_type=SimpleNamespace(value=scattering_type), beam_mode=SimpleNamespace(value=BeamModeEnum.CONSTANT_WAVELENGTH), ) - experiment = SimpleNamespace(type=expt_type) + experiment = SimpleNamespace(experiment_type=expt_type) return SimpleNamespace(experiments={'E1': experiment}), experiment def test_unsupported_sample_form_warns(self, monkeypatch, capsys): diff --git a/tests/unit/easydiffraction/io/cif/test_handler.py b/tests/unit/easydiffraction/io/cif/test_handler.py index 120195dd5..1a7a7e6bc 100644 --- a/tests/unit/easydiffraction/io/cif/test_handler.py +++ b/tests/unit/easydiffraction/io/cif/test_handler.py @@ -2,12 +2,12 @@ # SPDX-License-Identifier: BSD-3-Clause -def test_cif_handler_names_and_uid(): +def test_tags_names_and_uid(): import easydiffraction.io.cif.handler as H names = ['_cell.length_a', '_cell.length_b'] - h = H.CifHandler(names=names) - assert h.names == names + h = H.TagSpec(edi_names=names) + assert h.edi_names == names assert h.uid is None class Owner: @@ -17,20 +17,63 @@ class Owner: assert h.uid == 'db.cat.entry.param' -def test_cif_handler_iucr_name_falls_back_to_first_name(): - from easydiffraction.io.cif.handler import CifHandler +def test_tags_cif_name_falls_back_to_first_name(): + from easydiffraction.io.cif.handler import TagSpec - handler = CifHandler(names=['_calculator.type']) + handler = TagSpec(edi_names=['_calculator.type']) - assert handler.iucr_name == '_calculator.type' + assert handler.cif_name == '_calculator.type' -def test_cif_handler_iucr_name_uses_explicit_value(): - from easydiffraction.io.cif.handler import CifHandler +def test_tags_cif_name_uses_explicit_value(): + from easydiffraction.io.cif.handler import TagSpec - handler = CifHandler( - names=['_calculator.type'], - iucr_name='_easydiffraction_calculator.type', + handler = TagSpec( + edi_names=['_calculator.type'], cif_names=['_easydiffraction_calculator.type'] ) - assert handler.iucr_name == '_easydiffraction_calculator.type' + assert handler.cif_name == '_easydiffraction_calculator.type' + + +def test_explicit_edi_name_overrides_first_and_leads_read_order(): + from easydiffraction.io.cif.handler import TagSpec + + handler = TagSpec(edi_names=['_a.x', '_a.y'], edi_name='_a.z') + + assert handler.edi_name == '_a.z' + assert handler.edi_read_names == ['_a.z', '_a.x', '_a.y'] + + +def test_edi_read_names_remove_duplicates(): + from easydiffraction.io.cif.handler import TagSpec + + handler = TagSpec(edi_names=['_a.x', '_a.x']) + + assert handler.edi_read_names == ['_a.x'] + + +def test_cif_read_names_dedup_and_canonical_first(): + from easydiffraction.io.cif.handler import TagSpec + + handler = TagSpec(edi_names=['_a.x'], cif_names=['_b.y', '_b.z', '_b.y']) + + assert handler.cif_name == '_b.y' + assert handler.cif_read_names == ['_b.y', '_b.z'] + + +def test_cif_names_default_to_edi_names(): + from easydiffraction.io.cif.handler import TagSpec + + handler = TagSpec(edi_names=['_a.x']) + + assert handler.cif_names == ['_a.x'] + assert handler.cif_name == '_a.x' + + +def test_read_names_union_orders_edi_before_cif_and_dedupes(): + from easydiffraction.io.cif.handler import TagSpec + + handler = TagSpec(edi_names=['_a.x'], cif_names=['_a.x', '_b.y']) + + # Edi name first, then CIF-only aliases, with duplicates removed. + assert handler.read_names == ['_a.x', '_b.y'] diff --git a/tests/unit/easydiffraction/io/cif/test_iucr_transformers.py b/tests/unit/easydiffraction/io/cif/test_iucr_transformers.py index 2b4731bbd..e5d5fadbc 100644 --- a/tests/unit/easydiffraction/io/cif/test_iucr_transformers.py +++ b/tests/unit/easydiffraction/io/cif/test_iucr_transformers.py @@ -6,13 +6,14 @@ from types import SimpleNamespace -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class _Descriptor: - def __init__(self, value, tag='_x.value', iucr_name=None): + def __init__(self, value, tag='_x.value', cif_name=None): self.value = value - self._cif_handler = CifHandler(names=[tag], iucr_name=iucr_name) + cif_names = [cif_name] if cif_name is not None else None + self._tags = TagSpec(edi_names=[tag], cif_names=cif_names) def _items_by_tag(items): @@ -39,8 +40,8 @@ def test_tof_calibration_transformer_emits_powers_and_ids(): instrument = SimpleNamespace( calib_d_to_tof_offset=_Descriptor(1.0), calib_d_to_tof_linear=_Descriptor(2.0), - calib_d_to_tof_quad=_Descriptor(3.0), - calib_d_to_tof_recip=_Descriptor(4.0), + calib_d_to_tof_quadratic=_Descriptor(3.0), + calib_d_to_tof_reciprocal=_Descriptor(4.0), ) experiment = SimpleNamespace(name='bank1', instrument=instrument) @@ -93,15 +94,15 @@ def test_extinction_transformer_emits_becker_coppens_type_1(): extinction = SimpleNamespace( type=_Descriptor( 'becker-coppens', - iucr_name='_easydiffraction_extinction.type', + cif_name='_easydiffraction_extinction.type', ), model=_Descriptor( 'gaussian_isotropic_type1', - iucr_name='_easydiffraction_extinction.model', + cif_name='_easydiffraction_extinction.model', ), mosaicity=_Descriptor( 0.12, - iucr_name='_easydiffraction_extinction.mosaicity', + cif_name='_easydiffraction_extinction.mosaicity', ), ) items = _items_by_tag(ExtinctionTransformer().items(SimpleNamespace(extinction=extinction))) @@ -133,15 +134,15 @@ def test_extinction_transformer_emits_becker_coppens_type_2(): extinction = SimpleNamespace( type=_Descriptor( 'becker-coppens', - iucr_name='_easydiffraction_extinction.type', + cif_name='_easydiffraction_extinction.type', ), model=_Descriptor( 'lorentzian_anisotropic_type2', - iucr_name='_easydiffraction_extinction.model', + cif_name='_easydiffraction_extinction.model', ), radius=_Descriptor( 2.5, - iucr_name='_easydiffraction_extinction.radius', + cif_name='_easydiffraction_extinction.radius', ), ) items = _items_by_tag(ExtinctionTransformer().items(SimpleNamespace(extinction=extinction))) @@ -159,19 +160,19 @@ def test_extinction_transformer_emits_mixed_becker_coppens_details(): extinction = SimpleNamespace( type=_Descriptor( 'becker-coppens', - iucr_name='_easydiffraction_extinction.type', + cif_name='_easydiffraction_extinction.type', ), model=_Descriptor( 'mixed_gaussian', - iucr_name='_easydiffraction_extinction.model', + cif_name='_easydiffraction_extinction.model', ), mosaicity=_Descriptor( 0.12, - iucr_name='_easydiffraction_extinction.mosaicity', + cif_name='_easydiffraction_extinction.mosaicity', ), radius=_Descriptor( 2.5, - iucr_name='_easydiffraction_extinction.radius', + cif_name='_easydiffraction_extinction.radius', ), ) items = _items_by_tag(ExtinctionTransformer().items(SimpleNamespace(extinction=extinction))) @@ -189,11 +190,11 @@ def test_extinction_transformer_emits_zachariasen_method(): extinction = SimpleNamespace( type=_Descriptor( 'zachariasen', - iucr_name='_easydiffraction_extinction.type', + cif_name='_easydiffraction_extinction.type', ), mosaicity=_Descriptor( 0.05, - iucr_name='_easydiffraction_extinction.mosaicity', + cif_name='_easydiffraction_extinction.mosaicity', ), ) items = _items_by_tag(ExtinctionTransformer().items(SimpleNamespace(extinction=extinction))) diff --git a/tests/unit/easydiffraction/io/cif/test_iucr_writer.py b/tests/unit/easydiffraction/io/cif/test_iucr_writer.py index ccffa8428..f39b1042e 100644 --- a/tests/unit/easydiffraction/io/cif/test_iucr_writer.py +++ b/tests/unit/easydiffraction/io/cif/test_iucr_writer.py @@ -7,14 +7,16 @@ from collections import UserDict from types import SimpleNamespace -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class _Descriptor: - def __init__(self, value, tag='_x.value', iucr_name=None): + def __init__(self, value, tag='_x.value', cif_name=None): self.name = tag.rsplit('.', maxsplit=1)[-1] self.value = value - self._cif_handler = CifHandler(names=[tag], iucr_name=iucr_name) + self._tags = TagSpec( + edi_names=[tag], cif_names=[cif_name] if cif_name is not None else None + ) class _SwitchableCategory: @@ -56,8 +58,8 @@ def _collection(*items): return _Collection({item.name: item for item in items}) -def _descriptor(value, tag='_x.value', iucr_name=None): - return _Descriptor(value, tag=tag, iucr_name=iucr_name) +def _descriptor(value, tag='_x.value', cif_name=None): + return _Descriptor(value, tag=tag, cif_name=cif_name) def _experiment_type(*, sample_form, beam_mode='constant wavelength'): @@ -113,7 +115,7 @@ def _structure(name='phase1'): structure.cell.length_b = 5.43 structure.cell.length_c = 5.43 structure.atom_sites.create( - label='Si1', + id='Si1', type_symbol='Si', fract_x=0.0, fract_y=0.0, @@ -127,7 +129,7 @@ def _structure(name='phase1'): def _project(name, tmp_path, structures, experiments): return SimpleNamespace( name=name, - info=SimpleNamespace(path=tmp_path), + metadata=SimpleNamespace(path=tmp_path), structures=structures, experiments=experiments, analysis=SimpleNamespace( @@ -140,9 +142,9 @@ def _project(name, tmp_path, structures, experiments): def _single_crystal_experiment(name='sc1'): return SimpleNamespace( name=name, - type=_experiment_type(sample_form='single crystal'), - linked_crystal=SimpleNamespace( - id=_descriptor( + experiment_type=_experiment_type(sample_form='single crystal'), + linked_structure=SimpleNamespace( + structure_id=_descriptor( 'phase1', '_sc_crystal_block.id', '_easydiffraction_sc_crystal_block.id', @@ -189,7 +191,7 @@ def _single_crystal_experiment(name='sc1'): def _linked_phase(): return SimpleNamespace( - id=_descriptor('phase1'), + structure_id=_descriptor('phase1'), scale=_descriptor(1.0), ) @@ -214,8 +216,8 @@ def _powder_experiment(name, *, beam_mode='constant wavelength'): ) return SimpleNamespace( name=name, - type=_experiment_type(sample_form='powder', beam_mode=beam_mode), - linked_phases=[_linked_phase()], + experiment_type=_experiment_type(sample_form='powder', beam_mode=beam_mode), + linked_structures=[_linked_phase()], diffrn=SimpleNamespace( ambient_temperature=_descriptor(295.0), ambient_pressure=_descriptor(101.3), @@ -224,8 +226,8 @@ def _powder_experiment(name, *, beam_mode='constant wavelength'): setup_wavelength=_descriptor(1.5406 if beam_mode == 'constant wavelength' else None), calib_d_to_tof_offset=_descriptor(1.0), calib_d_to_tof_linear=_descriptor(2.0), - calib_d_to_tof_quad=_descriptor(3.0), - calib_d_to_tof_recip=_descriptor(4.0), + calib_d_to_tof_quadratic=_descriptor(3.0), + calib_d_to_tof_reciprocal=_descriptor(4.0), ), calculator=_SwitchableCategory( 'cryspy', @@ -254,7 +256,7 @@ def _powder_experiment(name, *, beam_mode='constant wavelength'): index_k=_descriptor(0), index_l=_descriptor(0), f_squared_calc=_descriptor(25.0), - phase_id=_descriptor('phase1'), + structure_id=_descriptor('phase1'), d_spacing=_descriptor(2.5), ) ], @@ -443,7 +445,7 @@ def test_iucr_atom_site_rows_preserve_parameter_uncertainties(): from easydiffraction.io.cif.serialize import format_param_value atom_site = AtomSite() - atom_site.label = 'Si1' + atom_site.id = 'Si1' atom_site.type_symbol = 'Si' atom_site.fract_x = 11.98509310 atom_site.fract_x.free = True @@ -465,7 +467,7 @@ def test_iucr_atom_site_aniso_rows_preserve_parameter_uncertainties(): from easydiffraction.io.cif.serialize import format_param_value aniso_site = AtomSiteAniso() - aniso_site.label = 'Si1' + aniso_site.id = 'Si1' aniso_site.adp_11 = 0.00658189 aniso_site.adp_11.free = True aniso_site.adp_11.uncertainty = 0.00014 @@ -486,9 +488,9 @@ def test_iucr_extension_items_preserve_parameter_uncertainties(): scale = Parameter( name='scale', value_spec=AttributeSpec(default=1.0), - cif_handler=CifHandler( - names=['_sc_crystal_block.scale'], - iucr_name='_easydiffraction_sc_crystal_block.scale', + tags=TagSpec( + edi_names=['_sc_crystal_block.scale'], + cif_names=['_easydiffraction_sc_crystal_block.scale'], ), ) scale.value = 2.87438284 @@ -562,7 +564,7 @@ def test_atom_site_aniso_section_renders_beta_header_and_tags(): structure.cell.length_a = 5.0 structure.cell.length_b = 6.0 structure.cell.length_c = 8.0 - structure.atom_sites.create(label='Fe', type_symbol='Fe', adp_type='beta') + structure.atom_sites.create(id='Fe', type_symbol='Fe', adp_type='beta') structure.atom_site_aniso['Fe'].adp_11 = 0.001 structure._update_categories() @@ -579,7 +581,7 @@ def test_write_pref_orient_loop_standard_and_fraction(): from easydiffraction.io.cif import iucr_writer as W coll = PrefOrients() - coll.create(phase_id='lbco', march_r=0.75, index_h=0, index_k=0, index_l=1) # fraction=0 + coll.create(structure_id='lbco', march_r=0.75, index_h=0, index_k=0, index_l=1) # fraction=0 experiment = SimpleNamespace(preferred_orientation=coll) lines: list[str] = [] diff --git a/tests/unit/easydiffraction/io/cif/test_iucr_writer_coverage.py b/tests/unit/easydiffraction/io/cif/test_iucr_writer_coverage.py index 39cac749d..61e89326a 100644 --- a/tests/unit/easydiffraction/io/cif/test_iucr_writer_coverage.py +++ b/tests/unit/easydiffraction/io/cif/test_iucr_writer_coverage.py @@ -9,20 +9,22 @@ import pytest -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec class _Descriptor: """Minimal CIF descriptor exposing a value and a handler.""" - def __init__(self, value, tag='_x.value', iucr_name=None): + def __init__(self, value, tag='_x.value', cif_name=None): self.name = tag.rsplit('.', maxsplit=1)[-1] self.value = value - self._cif_handler = CifHandler(names=[tag], iucr_name=iucr_name) + self._tags = TagSpec( + edi_names=[tag], cif_names=[cif_name] if cif_name is not None else None + ) -def _descriptor(value, tag='_x.value', iucr_name=None): - return _Descriptor(value, tag=tag, iucr_name=iucr_name) +def _descriptor(value, tag='_x.value', cif_name=None): + return _Descriptor(value, tag=tag, cif_name=cif_name) # --- _report_path / iucr_report_path ---------------------------------- @@ -32,7 +34,7 @@ def test_report_path_uses_explicit_path(tmp_path): from easydiffraction.io.cif.iucr_writer import iucr_report_path target = tmp_path / 'custom' / 'out.cif' - project = SimpleNamespace(name='demo', info=SimpleNamespace(path=tmp_path)) + project = SimpleNamespace(name='demo', metadata=SimpleNamespace(path=tmp_path)) assert iucr_report_path(project, target) == target @@ -40,7 +42,7 @@ def test_report_path_uses_explicit_path(tmp_path): def test_report_path_raises_when_project_unsaved(): from easydiffraction.io.cif.iucr_writer import iucr_report_path - project = SimpleNamespace(name='demo', info=SimpleNamespace(path=None)) + project = SimpleNamespace(name='demo', metadata=SimpleNamespace(path=None)) with pytest.raises(FileNotFoundError, match='Save the project first'): iucr_report_path(project) @@ -49,7 +51,7 @@ def test_report_path_raises_when_project_unsaved(): def test_report_path_defaults_to_reports_dir(tmp_path): from easydiffraction.io.cif.iucr_writer import iucr_report_path - project = SimpleNamespace(name='demo', info=SimpleNamespace(path=tmp_path)) + project = SimpleNamespace(name='demo', metadata=SimpleNamespace(path=tmp_path)) assert iucr_report_path(project) == tmp_path / 'reports' / 'demo.cif' @@ -305,13 +307,13 @@ def test_adp_family_distinguishes_b_and_u(): def test_atom_site_for_aniso_matches_by_label(): from easydiffraction.io.cif.iucr_writer import _atom_site_for_aniso - site = SimpleNamespace(label=_descriptor('Si1')) - by_label = {'Si1': site} - aniso = SimpleNamespace(label=_descriptor('Si1')) - missing = SimpleNamespace(label=_descriptor('O1')) + site = SimpleNamespace(id=_descriptor('Si1')) + by_id = {'Si1': site} + aniso = SimpleNamespace(id=_descriptor('Si1')) + missing = SimpleNamespace(id=_descriptor('O1')) - assert _atom_site_for_aniso(by_label, aniso) is site - assert _atom_site_for_aniso(by_label, missing) is None + assert _atom_site_for_aniso(by_id, aniso) is site + assert _atom_site_for_aniso(by_id, missing) is None # --- formula helpers -------------------------------------------------- @@ -371,9 +373,9 @@ def test_software_role_label_unknown_without_name(): project = SimpleNamespace( analysis=SimpleNamespace( - software=SimpleNamespace( - framework=SimpleNamespace(name=_descriptor(None)), - ) + software={ + 'framework': SimpleNamespace(name=_descriptor(None)), + } ) ) assert _software_role_label(project, 'framework') == '?' @@ -384,12 +386,12 @@ def test_software_role_label_name_only_when_version_missing(): project = SimpleNamespace( analysis=SimpleNamespace( - software=SimpleNamespace( - framework=SimpleNamespace( + software={ + 'framework': SimpleNamespace( name=_descriptor('CrysPy'), version=_descriptor(''), ), - ) + } ) ) assert _software_role_label(project, 'framework') == 'CrysPy' @@ -400,12 +402,12 @@ def test_software_role_label_name_and_version(): project = SimpleNamespace( analysis=SimpleNamespace( - software=SimpleNamespace( - calculator=SimpleNamespace( + software={ + 'calculator': SimpleNamespace( name=_descriptor('cryspy'), version=_descriptor('1.2'), ), - ) + } ) ) assert _software_role_label(project, 'calculator') == 'cryspy 1.2' @@ -414,16 +416,10 @@ def test_software_role_label_name_and_version(): def test_software_fit_datetime_none_and_value(): from easydiffraction.io.cif.iucr_writer import _software_fit_datetime - empty = SimpleNamespace( - analysis=SimpleNamespace(software=SimpleNamespace(timestamp=_descriptor(''))) - ) + empty = SimpleNamespace(metadata=SimpleNamespace(timestamp='')) assert _software_fit_datetime(empty) is None - populated = SimpleNamespace( - analysis=SimpleNamespace( - software=SimpleNamespace(timestamp=_descriptor('2026-06-06T00:00:00')) - ) - ) + populated = SimpleNamespace(metadata=SimpleNamespace(timestamp='2026-06-06T00:00:00')) assert _software_fit_datetime(populated) == '2026-06-06T00:00:00' @@ -475,7 +471,7 @@ def test_linked_structure_falls_back_to_single_structure(): structures = _Structures(only=only) experiment = SimpleNamespace( name='expt', - linked_crystal=SimpleNamespace(id=_descriptor('missing')), + linked_structure=SimpleNamespace(structure_id=_descriptor('missing')), ) project = SimpleNamespace(structures=structures) assert _linked_structure(project, experiment) is only @@ -490,7 +486,7 @@ def test_linked_structure_raises_on_ambiguous_link(): ) experiment = SimpleNamespace( name='expt', - linked_crystal=SimpleNamespace(id=_descriptor('missing')), + linked_structure=SimpleNamespace(structure_id=_descriptor('missing')), ) project = SimpleNamespace(structures=structures) with pytest.raises(ValueError, match="links crystal 'missing'"): @@ -502,8 +498,8 @@ def test_linked_powder_structures_falls_back_to_single(): only = SimpleNamespace(name='only') structures = _Structures(only=only) - linked_phase = SimpleNamespace(id=_descriptor('missing')) - experiment = SimpleNamespace(name='expt', linked_phases=[linked_phase]) + linked_phase = SimpleNamespace(structure_id=_descriptor('missing')) + experiment = SimpleNamespace(name='expt', linked_structures=[linked_phase]) project = SimpleNamespace(structures=structures) result = _linked_powder_structures(project, experiment) @@ -519,10 +515,10 @@ def test_linked_powder_structures_raises_on_ambiguous_link(): ) experiment = SimpleNamespace( name='expt', - linked_phases=[SimpleNamespace(id=_descriptor('missing'))], + linked_structures=[SimpleNamespace(structure_id=_descriptor('missing'))], ) project = SimpleNamespace(structures=structures) - with pytest.raises(ValueError, match='links phases'): + with pytest.raises(ValueError, match='links structures'): _linked_powder_structures(project, experiment) @@ -553,7 +549,7 @@ def test_collection_values_wraps_scalar_object(): def test_iucr_descriptor_fallback_through_parameters(): from easydiffraction.io.cif.iucr_writer import _iucr_descriptor - descriptor = _descriptor('val', '_a.b', iucr_name='_iucr.a') + descriptor = _descriptor('val', '_a.b', cif_name='_iucr.a') descriptor.name = 'target' owner = SimpleNamespace( target='plain string', @@ -594,11 +590,11 @@ def test_iucr_descriptor_for_tag_returns_none_for_missing_owner(): assert _iucr_descriptor_for_tag(None, '_iucr.radius') is None -def test_descriptor_iucr_name_handles_handlerless_value(): - from easydiffraction.io.cif.iucr_writer import _descriptor_iucr_name +def test_descriptor_cif_name_handles_handlerless_value(): + from easydiffraction.io.cif.iucr_writer import _descriptor_cif_name - assert _descriptor_iucr_name(SimpleNamespace(value=1.0)) is None - assert _descriptor_iucr_name(_descriptor(1.0, '_a.b', '_iucr.a')) == '_iucr.a' + assert _descriptor_cif_name(SimpleNamespace(value=1.0)) is None + assert _descriptor_cif_name(_descriptor(1.0, '_a.b', '_iucr.a')) == '_iucr.a' def test_iucr_items_empty_for_missing_owner(): diff --git a/tests/unit/easydiffraction/io/cif/test_serialize.py b/tests/unit/easydiffraction/io/cif/test_serialize.py index 592c8a250..069dbd600 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize.py @@ -21,11 +21,11 @@ def test_format_value_quotes_whitespace_strings(): def test_param_to_cif_minimal(): import easydiffraction.io.cif.serialize as MUT - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class P: def __init__(self): - self._cif_handler = CifHandler(names=['_x.y']) + self._tags = TagSpec(edi_names=['_x.y']) self.value = 3 p = P() @@ -36,12 +36,12 @@ def test_format_param_value_with_uncertainty_uses_two_sig_digits(): import easydiffraction.io.cif.serialize as MUT from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='p', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_x.p']), + tags=TagSpec(edi_names=['_x.p']), ) p.value = 11.98509310 p.free = True @@ -54,12 +54,12 @@ def test_format_param_value_with_large_uncertainty_is_readable(): import easydiffraction.io.cif.serialize as MUT from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='p', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_x.p']), + tags=TagSpec(edi_names=['_x.p']), ) p.value = 882.16515040 p.free = True @@ -75,12 +75,12 @@ def test_param_from_cif_empty_brackets_marks_free_without_uncertainty(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='2theta_offset', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_instr.2theta_offset']), + tags=TagSpec(edi_names=['_instr.2theta_offset']), ) doc = gemmi.cif.read_string('data_test\n_instr.2theta_offset 0.5()\n') @@ -107,7 +107,7 @@ def test_param_from_cif_missing_tag_keeps_sentinel_default_without_validating(): 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 + from easydiffraction.io.cif.handler import TagSpec p = Parameter( name='two_theta_min', @@ -115,7 +115,7 @@ def test_param_from_cif_missing_tag_keeps_sentinel_default_without_validating(): default=float('nan'), validator=RangeValidator(ge=0, le=180), ), - cif_handler=CifHandler(names=['_data_range.2theta_min']), + tags=TagSpec(edi_names=['_data_range.2theta_min']), ) # Block without the tag: the absent value falls back to the default. doc = gemmi.cif.read_string('data_test\n_instr.2theta_offset 0.5\n') @@ -129,14 +129,14 @@ def test_category_collection_to_cif_empty_and_one_row(): import easydiffraction.io.cif.serialize as MUT from easydiffraction.core.category import CategoryCollection from easydiffraction.core.category import CategoryItem - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class Item(CategoryItem): def __init__(self, name, value): super().__init__() self._identity.category_entry_name = name self._p = type('P', (), {})() - self._p._cif_handler = CifHandler(names=['_x']) + self._p._tags = TagSpec(edi_names=['_x']) self._p.value = value @property @@ -170,7 +170,7 @@ def as_cif(self): class Project: def __init__(self): - self.info = Obj('I') + self.metadata = Obj('I') self.structures = None self.experiments = Obj('E') self.analysis = None @@ -210,44 +210,47 @@ class Project: assert analysis.fit_parameters['scale'].start_value.value == 1.0 -def test_atom_site_cif_emits_one_adp_family_per_row(): +def test_atom_site_cif_emits_type_neutral_iso_adp_for_mixed_families(): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='mixed') - structure.atom_sites.create(label='B1', type_symbol='Si', adp_type='Biso', adp_iso=0.4) - structure.atom_sites.create(label='U1', type_symbol='O', adp_type='Uiso', adp_iso=0.01) + structure.atom_sites.create(id='B1', type_symbol='Si', adp_type='Biso', adp_iso=0.4) + structure.atom_sites.create(id='U1', type_symbol='O', adp_type='Uiso', adp_iso=0.01) - b_loop, u_loop = structure.atom_sites.as_cif.split('\n\n') + cif = structure.atom_sites.as_cif - assert '_atom_site.B_iso_or_equiv' in b_loop - assert '_atom_site.U_iso_or_equiv' not in b_loop - assert 'B1' in b_loop - assert 'U1' not in b_loop - assert '_atom_site.U_iso_or_equiv' in u_loop - assert '_atom_site.B_iso_or_equiv' not in u_loop - assert 'U1' in u_loop - assert 'B1' not in u_loop + # Edi persistence is type-neutral: a single loop carries both + # atoms under _atom_site.adp_iso, with the family recorded in the + # co-persisted _atom_site.adp_type column (no per-family split, and + # no strict B_iso_or_equiv/U_iso_or_equiv report names). + assert cif.count('loop_') == 1 + assert '_atom_site.adp_iso' in cif + assert '_atom_site.adp_type' in cif + assert '_atom_site.B_iso_or_equiv' not in cif + assert '_atom_site.U_iso_or_equiv' not in cif + assert 'B1 Si 0. 0. 0. ? ? 1. 0.4 Biso' in cif + assert 'U1 O 0. 0. 0. ? ? 1. 0.01 Uiso' in cif -def test_atom_site_aniso_cif_emits_one_adp_family_per_row(): +def test_atom_site_aniso_cif_emits_type_neutral_adp_for_mixed_families(): from easydiffraction.datablocks.structure.item.base import Structure structure = Structure(name='mixed') - structure.atom_sites.create(label='B1', type_symbol='Si', adp_iso=0.4) - structure.atom_sites.create(label='U1', type_symbol='O', adp_iso=0.01) + structure.atom_sites.create(id='B1', type_symbol='Si', adp_iso=0.4) + structure.atom_sites.create(id='U1', type_symbol='O', adp_iso=0.01) structure.atom_sites['B1'].adp_type = 'Bani' structure.atom_sites['U1'].adp_type = 'Uani' - b_loop, u_loop = structure.atom_site_aniso.as_cif.split('\n\n') + cif = structure.atom_site_aniso.as_cif - assert '_atom_site_aniso.B_11' in b_loop - assert '_atom_site_aniso.U_11' not in b_loop - assert 'B1' in b_loop - assert 'U1' not in b_loop - assert '_atom_site_aniso.U_11' in u_loop - assert '_atom_site_aniso.B_11' not in u_loop - assert 'U1' in u_loop - assert 'B1' not in u_loop + # Anisotropic ADPs are likewise type-neutral: one loop, both atoms, + # under _atom_site_aniso.adp_11.. with no B_NN/U_NN family split. + assert cif.count('loop_') == 1 + assert '_atom_site_aniso.adp_11' in cif + assert '_atom_site_aniso.B_11' not in cif + assert '_atom_site_aniso.U_11' not in cif + assert 'B1' in cif + assert 'U1' in cif def _make_beta_structure(): @@ -258,7 +261,7 @@ def _make_beta_structure(): structure.cell.length_a = 5.0 structure.cell.length_b = 6.0 structure.cell.length_c = 8.0 - structure.atom_sites.create(label='Fe', type_symbol='Fe', adp_iso=0.0) + structure.atom_sites.create(id='Fe', type_symbol='Fe', adp_iso=0.0) structure.atom_sites['Fe'].adp_type = 'beta' structure._sync_atom_site_aniso() aniso = structure.atom_site_aniso['Fe'] @@ -269,13 +272,17 @@ def _make_beta_structure(): return structure -def test_atom_site_aniso_cif_emits_beta_family(): +def test_atom_site_aniso_cif_emits_type_neutral_adp_for_beta_family(): structure = _make_beta_structure() cif = structure.atom_site_aniso.as_cif - assert '_atom_site_aniso.beta_11' in cif - assert '_atom_site_aniso.beta_23' in cif + # Beta-convention atoms persist under the same type-neutral + # _atom_site_aniso.adp_NN tags; the beta family is recorded via the + # atom site's adp_type, not a beta_NN tag in the aniso loop. + assert '_atom_site_aniso.adp_11' in cif + assert '_atom_site_aniso.adp_23' in cif + assert '_atom_site_aniso.beta_11' not in cif assert '_atom_site_aniso.U_11' not in cif assert '_atom_site_aniso.B_11' not in cif diff --git a/tests/unit/easydiffraction/io/cif/test_serialize_category_owner_baseline.py b/tests/unit/easydiffraction/io/cif/test_serialize_category_owner_baseline.py index 808b1a948..0fe267b17 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize_category_owner_baseline.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize_category_owner_baseline.py @@ -44,40 +44,38 @@ def test_real_analysis_as_cif_is_singleton_section_without_data_header() -> None def test_real_analysis_as_cif_includes_stamped_software() -> None: project = Project(name='proj') analysis = project.analysis - analysis.software.framework.name = 'EasyDiffraction' - analysis.software.framework.version = '0.17.0' - analysis.software.framework.url = 'https://github.com/easyscience/diffraction-lib' - analysis.software.calculator.name = 'cryspy' - analysis.software.calculator.version = '0.11.0' - analysis.software.minimizer.name = 'lmfit' - analysis.software.minimizer.version = '1.3.4' - analysis.software.timestamp = '2026-05-29T12:00:00+00:00' + analysis.software['framework'].name = 'EasyDiffraction' + analysis.software['framework'].version = '0.17.0' + analysis.software['framework'].url = 'https://github.com/easyscience/diffraction-lib' + analysis.software['calculator'].name = 'cryspy' + analysis.software['calculator'].version = '0.11.0' + analysis.software['minimizer'].name = 'lmfit' + analysis.software['minimizer'].version = '1.3.4' analysis_cif = analysis.as_cif - assert '_software.framework_name EasyDiffraction' in analysis_cif - assert '_software.framework_version 0.17.0' in analysis_cif - assert '_software.calculator_name cryspy' in analysis_cif - assert '_software.calculator_version 0.11.0' in analysis_cif - assert '_software.minimizer_name lmfit' in analysis_cif - assert '_software.minimizer_version 1.3.4' in analysis_cif - assert '_software.timestamp 2026-05-29T12:00:00+00:00' in analysis_cif + assert '_software.id' in analysis_cif + assert '_software.name' in analysis_cif + assert '_software.version' in analysis_cif + assert 'framework EasyDiffraction 0.17.0' in analysis_cif + assert 'calculator cryspy 0.11.0' in analysis_cif + assert 'minimizer lmfit 1.3.4' in analysis_cif def test_real_analysis_from_cif_restores_stamped_software() -> None: source = Project(name='proj') - source.analysis.software.framework.name = 'EasyDiffraction' - source.analysis.software.framework.version = '0.17.0' - source.analysis.software.calculator.name = 'cryspy' - source.analysis.software.minimizer.name = 'lmfit' + source.analysis.software['framework'].name = 'EasyDiffraction' + source.analysis.software['framework'].version = '0.17.0' + source.analysis.software['calculator'].name = 'cryspy' + source.analysis.software['minimizer'].name = 'lmfit' target = Project(name='restored') analysis_from_cif(target.analysis, source.analysis.as_cif) - assert target.analysis.software.framework.name.value == 'EasyDiffraction' - assert target.analysis.software.framework.version.value == '0.17.0' - assert target.analysis.software.calculator.name.value == 'cryspy' - assert target.analysis.software.minimizer.name.value == 'lmfit' + assert target.analysis.software['framework'].name.value == 'EasyDiffraction' + assert target.analysis.software['framework'].version.value == '0.17.0' + assert target.analysis.software['calculator'].name.value == 'cryspy' + assert target.analysis.software['minimizer'].name.value == 'lmfit' def test_real_analysis_as_cif_includes_aliases_and_constraints_when_present() -> None: @@ -86,13 +84,13 @@ def test_real_analysis_as_cif_includes_aliases_and_constraints_when_present() -> parameter = project.structures['phase_1'].cell.length_a analysis = project.analysis - analysis.aliases.create(label='a_param', param=parameter) + analysis.aliases.create(id='a_param', param=parameter) analysis.constraints.create(expression='a_param = a_param') analysis_cif = analysis.as_cif - assert '_alias.label' in analysis_cif - assert '_alias.param_unique_name' in analysis_cif + assert '_alias.id' in analysis_cif + assert '_alias.parameter_unique_name' in analysis_cif assert '_constraint.id' in analysis_cif assert '_constraint.expression' in analysis_cif assert 'a_param = a_param' in analysis_cif diff --git a/tests/unit/easydiffraction/io/cif/test_serialize_coverage.py b/tests/unit/easydiffraction/io/cif/test_serialize_coverage.py index fdad619d9..ff6df0913 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize_coverage.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize_coverage.py @@ -24,14 +24,14 @@ from easydiffraction.core.variable import NumericDescriptor from easydiffraction.core.variable import Parameter from easydiffraction.core.variable import StringDescriptor -from easydiffraction.io.cif.handler import CifHandler +from easydiffraction.io.cif.handler import TagSpec from easydiffraction.utils.logging import Logger def _bare_param(name: str, value: object) -> object: """Return a minimal serialize-only param exposing handler + value.""" param = type('P', (), {})() - param._cif_handler = CifHandler(names=[name]) + param._tags = TagSpec(edi_names=[name]) param.value = value return param @@ -93,7 +93,7 @@ def test_format_param_value_free_without_uncertainty_uses_empty_brackets(): param = Parameter( name='p', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_x.p']), + tags=TagSpec(edi_names=['_x.p']), ) param.value = 3.5 param.free = True @@ -105,7 +105,7 @@ def test_format_param_value_user_constrained_free_param_has_no_brackets(): param = Parameter( name='p', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_x.p']), + tags=TagSpec(edi_names=['_x.p']), ) param.value = 2.0 param._set_value_user_constrained(2.0) @@ -148,7 +148,7 @@ def test_adp_atom_site_loop_truncates_to_max_display(): structure = Structure(name='many') for i in range(6): structure.atom_sites.create( - label=f'U{i}', + id=f'U{i}', type_symbol='O', adp_type='Uiso', adp_iso=0.01, @@ -157,7 +157,8 @@ def test_adp_atom_site_loop_truncates_to_max_display(): out = MUT.category_collection_to_cif(structure.atom_sites, max_display=4) assert '...' in out.splitlines() - assert '_atom_site.U_iso_or_equiv' in out + # Edi persistence uses the type-neutral isotropic ADP tag. + assert '_atom_site.adp_iso' in out # ---------------------------------------------------------------------- @@ -256,14 +257,14 @@ def test_format_project_description_blank_is_unknown_marker(): def test_project_info_to_cif_title_without_space_is_unquoted(): - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - info = ProjectInfo(name='p1', title='NoSpaces', description='short') + metadata = ProjectMetadata(name='p1', title='NoSpaces', description='short') - out = MUT.project_info_to_cif(info) + out = MUT.project_metadata_to_cif(metadata) - assert '_project.title NoSpaces' in out - assert '_project.title "' not in out + assert '_metadata.title NoSpaces' in out + assert '_metadata.title "' not in out # ---------------------------------------------------------------------- @@ -289,7 +290,7 @@ def test_project_config_to_cif_includes_publication_and_method_sections(monkeypa monkeypatch.setattr(MUT, 'category_owner_to_cif', lambda owner: 'PUBLICATION') class Project: - info = _Section('INFO') + metadata = _Section('INFO') rendering_plot = _Section('PLOT') report = _Section('REPORT') publication = object() @@ -305,7 +306,7 @@ def test_project_to_cif_assembles_structures_experiments_and_analysis(monkeypatc monkeypatch.setattr(MUT, 'project_config_to_cif', lambda project: 'CONFIG') class Project: - info = _Section('CFG') + metadata = _Section('CFG') structures = _Section('STRUCT') experiments = _Section('EXP') analysis = _Section('ANALYSIS') @@ -331,7 +332,7 @@ class PlainInfo: ).sole_block() info = PlainInfo() - MUT._populate_project_info_from_block(info, block) + MUT._populate_project_metadata_from_block(info, block) assert info.name == 'MYID' assert info.title == 'My Title' @@ -348,7 +349,7 @@ class PlainInfo: block = gemmi.cif.read_string('data_p\n_project.id ONLYID\n').sole_block() info = PlainInfo() - MUT._populate_project_info_from_block(info, block) + MUT._populate_project_metadata_from_block(info, block) assert info.name == 'ONLYID' assert info.title == 'unchanged' @@ -356,18 +357,18 @@ class PlainInfo: def test_project_info_from_cif_populates_real_project_info(): - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - info = ProjectInfo(name='orig', title='Orig', description='orig desc') + metadata = ProjectMetadata(name='orig', title='Orig', description='orig desc') - MUT.project_info_from_cif( - info, + MUT.project_metadata_from_cif( + metadata, "_project.id restored\n_project.title 'New Title'\n_project.description Desc\n", ) - assert info.name == 'restored' - assert info.title == 'New Title' - assert info.description == 'Desc' + assert metadata.name == 'restored' + assert metadata.title == 'New Title' + assert metadata.description == 'Desc' def test_make_cif_string_reader_handles_unknown_and_text_fields(): @@ -392,7 +393,7 @@ def from_cif(self, block: object) -> None: class FakeProject: def __init__(self) -> None: - self.info = FakeSection() + self.metadata = FakeSection() self.rendering_plot = FakeSection() self.report = FakeSection() self.publication = FakeSection() @@ -407,7 +408,7 @@ def __init__(self) -> None: MUT.project_config_from_cif(project, '_project.id foo\n') sections = ( - project.info, + project.metadata, project.rendering_plot, project.report, project.publication, @@ -552,7 +553,7 @@ def test_set_param_from_raw_integer_value(): param = IntegerDescriptor( name='n', value_spec=AttributeSpec(data_type=DataTypes.INTEGER, default=0), - cif_handler=CifHandler(names=['_x.n']), + tags=TagSpec(edi_names=['_x.n']), ) MUT._set_param_from_raw_cif_value(param, '7') @@ -565,7 +566,7 @@ def test_set_param_from_raw_non_integer_is_ignored_with_warning(monkeypatch): param = IntegerDescriptor( name='n', value_spec=AttributeSpec(data_type=DataTypes.INTEGER, default=0), - cif_handler=CifHandler(names=['_x.n']), + tags=TagSpec(edi_names=['_x.n']), ) param.value = 5 @@ -579,7 +580,7 @@ def test_set_param_from_raw_numeric_with_brackets_marks_free_and_uncertainty(): param = Parameter( name='p', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_x.p']), + tags=TagSpec(edi_names=['_x.p']), ) MUT._set_param_from_raw_cif_value(param, '1.23(45)') @@ -593,7 +594,7 @@ def test_set_param_from_raw_string_strips_quotes(): param = StringDescriptor( name='s', value_spec=AttributeSpec(default='x'), - cif_handler=CifHandler(names=['_x.s']), + tags=TagSpec(edi_names=['_x.s']), ) MUT._set_param_from_raw_cif_value(param, "'hello'") @@ -602,7 +603,7 @@ def test_set_param_from_raw_string_strips_quotes(): def test_set_param_from_raw_bool_value(): - param = BoolDescriptor(name='b', cif_handler=CifHandler(names=['_x.b'])) + param = BoolDescriptor(name='b', tags=TagSpec(edi_names=['_x.b'])) MUT._set_param_from_raw_cif_value(param, 'true') @@ -613,7 +614,7 @@ def test_set_param_from_raw_unknown_marker_resets_to_default(): param = StringDescriptor( name='s', value_spec=AttributeSpec(default='def'), - cif_handler=CifHandler(names=['_x.s']), + tags=TagSpec(edi_names=['_x.s']), ) param.value = 'changed' @@ -626,7 +627,7 @@ def test_set_param_to_default_restores_descriptor_default(): param = StringDescriptor( name='s', value_spec=AttributeSpec(default='dd'), - cif_handler=CifHandler(names=['_x.s']), + tags=TagSpec(edi_names=['_x.s']), ) param.value = 'changed' @@ -640,7 +641,7 @@ def test_set_param_to_default_raises_for_required_field_without_default(monkeypa param = StringDescriptor( name='nd', value_spec=AttributeSpec(), - cif_handler=CifHandler(names=['_x.nd']), + tags=TagSpec(edi_names=['_x.nd']), ) with pytest.raises(ValueError, match='Cannot load required CIF field'): @@ -656,7 +657,7 @@ def test_param_from_cif_missing_tag_uses_default(): param = Parameter( name='p', value_spec=AttributeSpec(default=9.0), - cif_handler=CifHandler(names=['_x.absent']), + tags=TagSpec(edi_names=['_x.absent']), ) param.value = 3.0 block = gemmi.cif.read_string('data_t\n_x.other 1\n').sole_block() @@ -670,7 +671,7 @@ def test_param_from_cif_selects_value_at_loop_index(): param = NumericDescriptor( name='a', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_loop.a']), + tags=TagSpec(edi_names=['_loop.a']), ) block = gemmi.cif.read_string('data_t\nloop_\n_loop.a\n10\n20\n30\n').sole_block() @@ -697,12 +698,12 @@ def __init__(self) -> None: self._a = NumericDescriptor( name='a', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_pc.a']), + tags=TagSpec(edi_names=['_pc.a']), ) self._b = NumericDescriptor( name='b', value_spec=AttributeSpec(default=99.0), - cif_handler=CifHandler(names=['_pc.b']), + tags=TagSpec(edi_names=['_pc.b']), ) @property @@ -747,7 +748,7 @@ def __init__(self) -> None: self._count = NumericDescriptor( name='count', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_pc.count']), + tags=TagSpec(edi_names=['_pc.count']), ) coll = ScalarCollection() diff --git a/tests/unit/easydiffraction/io/cif/test_serialize_more.py b/tests/unit/easydiffraction/io/cif/test_serialize_more.py index 1820bc9e7..931807b28 100644 --- a/tests/unit/easydiffraction/io/cif/test_serialize_more.py +++ b/tests/unit/easydiffraction/io/cif/test_serialize_more.py @@ -6,13 +6,13 @@ def test_datablock_item_to_cif_includes_item_and_collection(): import easydiffraction.io.cif.serialize as MUT from easydiffraction.core.category import CategoryCollection from easydiffraction.core.category import CategoryItem - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class Item(CategoryItem): def __init__(self, val): super().__init__() self._p = type('P', (), {})() - self._p._cif_handler = CifHandler(names=['_aa']) + self._p._tags = TagSpec(edi_names=['_aa']) self._p.value = val @property @@ -44,13 +44,13 @@ def test_datablock_item_to_cif_skips_empty_category_fragments(): import easydiffraction.io.cif.serialize as MUT from easydiffraction.core.category import CategoryCollection from easydiffraction.core.category import CategoryItem - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class Item(CategoryItem): def __init__(self, val): super().__init__() self._p = type('P', (), {})() - self._p._cif_handler = CifHandler(names=['_aa']) + self._p._tags = TagSpec(edi_names=['_aa']) self._p.value = val @property @@ -102,27 +102,27 @@ def as_cif(self): def test_project_info_to_cif_contains_core_fields(): import easydiffraction.io.cif.serialize as MUT - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - info = ProjectInfo(name='p1', title='My Title', description='Some description text') - out = MUT.project_info_to_cif(info) - assert '_project.id p1' in out - assert '_project.title "My Title"' in out - assert '_project.description "Some description text"' in out - assert '_project.created "' in out - assert '_project.last_modified "' in out + metadata = ProjectMetadata(name='p1', title='My Title', description='Some description text') + out = MUT.project_metadata_to_cif(metadata) + assert '_metadata.name p1' in out + assert '_metadata.title "My Title"' in out + assert '_metadata.description "Some description text"' in out + assert '_metadata.created "' in out + assert '_metadata.last_modified "' in out def test_project_info_to_cif_wraps_long_description_as_text_field(): import easydiffraction.io.cif.serialize as MUT - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata description = ' '.join(['long'] * 20) - info = ProjectInfo(name='p1', title='My Title', description=description) + metadata = ProjectMetadata(name='p1', title='My Title', description=description) - out = MUT.project_info_to_cif(info) + out = MUT.project_metadata_to_cif(metadata) - assert '_project.description ' in out + assert '_metadata.description ' in out assert '\n;\n' in out assert 'long long long long long long long long long long long long' in out @@ -144,13 +144,13 @@ def __init__(self, data_text): self.datastore = DS(data_text) # Minimal CategoryItem to be picked up by datablock_item_to_cif from easydiffraction.core.category import CategoryItem - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec class Item(CategoryItem): def __init__(self): super().__init__() self._p = type('P', (), {})() - self._p._cif_handler = CifHandler(names=['_k']) + self._p._tags = TagSpec(edi_names=['_k']) self._p.value = 1 @property diff --git a/tests/unit/easydiffraction/io/edi/test_serialize.py b/tests/unit/easydiffraction/io/edi/test_serialize.py new file mode 100644 index 000000000..bb757405f --- /dev/null +++ b/tests/unit/easydiffraction/io/edi/test_serialize.py @@ -0,0 +1,149 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Tests for Edi schema-marker serialization and validation.""" + +from __future__ import annotations + +import pytest + +from easydiffraction.io import edi +from easydiffraction.io.edi.serialize import edi_body_from_text +from easydiffraction.io.edi.serialize import section_to_edi + +_MARKER = '_edi.schema_version 1' + + +# ---------------------------------------------------------------------- +# Package re-exports +# ---------------------------------------------------------------------- + + +def test_package_reexports_public_helpers(): + assert edi.section_to_edi is section_to_edi + assert edi.edi_body_from_text is edi_body_from_text + + +# ---------------------------------------------------------------------- +# section_to_edi +# ---------------------------------------------------------------------- + + +def test_section_to_edi_prepends_marker_for_headerless_body(): + out = section_to_edi('_metadata.name demo') + + assert out.startswith(_MARKER) + assert '_metadata.name demo' in out + assert out.endswith('\n') + + +def test_section_to_edi_keeps_data_header_first(): + out = section_to_edi('data_demo\n_metadata.name demo') + + lines = [line for line in out.splitlines() if line.strip()] + assert lines[0] == 'data_demo' + assert '_edi.schema_version 1' in out + # Marker sits between the data header and the body content. + assert out.index('_edi.schema_version') < out.index('_metadata.name demo') + + +# ---------------------------------------------------------------------- +# section_to_edi / edi_body_from_text round trip +# ---------------------------------------------------------------------- + + +def test_round_trip_recovers_body_without_marker(): + body = 'data_demo\n_metadata.name demo' + text = section_to_edi(body) + + recovered = edi_body_from_text(text) + + assert '_edi.schema_name' not in recovered + assert '_edi.schema_version' not in recovered + assert '_metadata.name demo' in recovered + + +def test_empty_body_round_trips_to_empty_string(): + text = section_to_edi('') + + assert edi_body_from_text(text) == '' + + +# ---------------------------------------------------------------------- +# Schema-marker validation +# ---------------------------------------------------------------------- + + +def test_missing_schema_version_marker_raises(): + text = '_metadata.name demo' + + with pytest.raises(ValueError, match='schema_version'): + edi_body_from_text(text) + + +def test_non_integer_schema_version_raises(): + text = '_edi.schema_version v1\n' + + with pytest.raises(ValueError, match='must start with an integer'): + edi_body_from_text(text) + + +def test_unsupported_major_schema_version_raises(): + text = '_edi.schema_version 2\n' + + with pytest.raises(ValueError, match='Unsupported Edi schema version'): + edi_body_from_text(text) + + +def test_minor_version_suffix_is_accepted(): + text = f'{_MARKER}.3\n\n_metadata.name demo' + + # Major version 1 is supported even with a minor suffix. + assert '_metadata.name demo' in edi_body_from_text(text) + + +# ---------------------------------------------------------------------- +# Background selector/body consistency +# ---------------------------------------------------------------------- + + +def _wrap(body: str) -> str: + return section_to_edi(f'data_expt\n{body}') + + +def test_background_fields_without_type_selector_raise(): + body = 'loop_\n_background.position\n_background.intensity\n10 0.5' + + with pytest.raises(ValueError, match=r'_background\.type selector'): + edi_body_from_text(_wrap(body)) + + +def test_unknown_background_type_raises(): + body = '_background.type bogus' + + with pytest.raises(ValueError, match=r'Unknown _background\.type'): + edi_body_from_text(_wrap(body)) + + +def test_line_segment_type_rejects_chebyshev_fields(): + body = '_background.type line-segment\nloop_\n_background.order\n_background.coef\n0 1.0' + + with pytest.raises(ValueError, match='line-segment background cannot contain'): + edi_body_from_text(_wrap(body)) + + +def test_chebyshev_type_rejects_line_segment_fields(): + body = '_background.type chebyshev\nloop_\n_background.position\n_background.intensity\n10 0.5' + + with pytest.raises(ValueError, match='chebyshev background cannot contain'): + edi_body_from_text(_wrap(body)) + + +def test_consistent_line_segment_background_validates(): + body = ( + '_background.type line-segment\nloop_\n_background.position\n_background.intensity\n10 0.5' + ) + + recovered = edi_body_from_text(_wrap(body)) + + assert '_background.position' in recovered + assert '_background.type line-segment' in recovered diff --git a/tests/unit/easydiffraction/io/test_ascii.py b/tests/unit/easydiffraction/io/test_ascii.py index 2da518b1b..5f183608d 100644 --- a/tests/unit/easydiffraction/io/test_ascii.py +++ b/tests/unit/easydiffraction/io/test_ascii.py @@ -77,27 +77,27 @@ class TestExtractProjectFromZip: """Tests for extract_project_from_zip.""" def test_extracts_project_dir(self, tmp_path): - """Returns path to the directory containing project.cif.""" + """Returns path to the directory containing project.edi.""" zip_path = tmp_path / 'proj.zip' with zipfile.ZipFile(zip_path, 'w') as zf: - zf.writestr('my_project/project.cif', 'data_project\n') + zf.writestr('my_project/project.edi', 'data_project\n') zf.writestr('my_project/structures/struct.cif', 'data_struct\n') result = extract_project_from_zip(zip_path, destination=tmp_path / 'out') assert result.endswith('my_project') - assert (tmp_path / 'out' / 'my_project' / 'project.cif').is_file() + assert (tmp_path / 'out' / 'my_project' / 'project.edi').is_file() def test_extracts_to_temp_dir_by_default(self, tmp_path): """Without destination, files go to a temp directory.""" zip_path = tmp_path / 'proj.zip' with zipfile.ZipFile(zip_path, 'w') as zf: - zf.writestr('myproj/project.cif', 'data_project\n') + zf.writestr('myproj/project.edi', 'data_project\n') result = extract_project_from_zip(zip_path) assert 'myproj' in result - assert 'project.cif' not in result # returns parent dir, not file + assert 'project.edi' not in result # returns parent dir, not file def test_raises_file_not_found(self, tmp_path): """Raises FileNotFoundError for missing ZIP path.""" @@ -105,12 +105,12 @@ def test_raises_file_not_found(self, tmp_path): extract_project_from_zip(tmp_path / 'missing.zip') def test_raises_value_error_no_project_cif(self, tmp_path): - """Raises ValueError when ZIP has no project.cif.""" + """Raises ValueError when ZIP has no project.edi.""" zip_path = tmp_path / 'bad.zip' with zipfile.ZipFile(zip_path, 'w') as zf: zf.writestr('data.dat', '1 2 3\n') - with pytest.raises(ValueError, match=r'No project\.cif found'): + with pytest.raises(ValueError, match=r'No project\.edi found'): extract_project_from_zip(zip_path) def test_destination_creates_directory(self, tmp_path): @@ -118,7 +118,7 @@ def test_destination_creates_directory(self, tmp_path): zip_path = tmp_path / 'proj.zip' dest = tmp_path / 'nested' / 'output' with zipfile.ZipFile(zip_path, 'w') as zf: - zf.writestr('proj/project.cif', 'data\n') + zf.writestr('proj/project.edi', 'data\n') result = extract_project_from_zip(zip_path, destination=dest) @@ -126,16 +126,16 @@ def test_destination_creates_directory(self, tmp_path): assert 'proj' in result def test_ignores_other_project_cif_in_destination(self, tmp_path): - """Only finds project.cif from the zip, not pre-existing ones.""" + """Only finds project.edi from the zip, not pre-existing ones.""" dest = tmp_path / 'data' # Pre-create another project directory in the destination - other_project = dest / 'aaa_other' / 'project.cif' + other_project = dest / 'aaa_other' / 'project.edi' other_project.parent.mkdir(parents=True) other_project.write_text('other\n') zip_path = tmp_path / 'proj.zip' with zipfile.ZipFile(zip_path, 'w') as zf: - zf.writestr('target_project/project.cif', 'correct\n') + zf.writestr('target_project/project.edi', 'correct\n') result = extract_project_from_zip(zip_path, destination=dest) diff --git a/tests/unit/easydiffraction/project/categories/info/test_default.py b/tests/unit/easydiffraction/project/categories/metadata/test_default.py similarity index 82% rename from tests/unit/easydiffraction/project/categories/info/test_default.py rename to tests/unit/easydiffraction/project/categories/metadata/test_default.py index b480ca16d..c4358777c 100644 --- a/tests/unit/easydiffraction/project/categories/info/test_default.py +++ b/tests/unit/easydiffraction/project/categories/metadata/test_default.py @@ -9,12 +9,12 @@ def test_project_info_defaults_and_identity(): - from easydiffraction.project.categories.info.default import ProjectInfo + from easydiffraction.project.categories.metadata.default import ProjectMetadata - info = ProjectInfo(name='beer', title='Beer title', description='Some description') + info = ProjectMetadata(name='beer', title='Beer title', description='Some description') assert info.type_info.tag == 'default' - assert info._identity.category_code == 'project' + assert info._identity.category_code == 'metadata' assert info.name == 'beer' assert info.title == 'Beer title' assert info.description == 'Some description' @@ -26,9 +26,9 @@ def test_project_info_defaults_and_identity(): def test_project_info_setters_and_from_cif_restore_fields(): - from easydiffraction.project.categories.info.default import ProjectInfo + from easydiffraction.project.categories.metadata.default import ProjectMetadata - info = ProjectInfo() + info = ProjectMetadata() info.description = 'Some spaced\n description' info.path = 'project-dir' diff --git a/tests/unit/easydiffraction/project/categories/info/test_factory.py b/tests/unit/easydiffraction/project/categories/metadata/test_factory.py similarity index 51% rename from tests/unit/easydiffraction/project/categories/info/test_factory.py rename to tests/unit/easydiffraction/project/categories/metadata/test_factory.py index 9010e2f85..5f59cff12 100644 --- a/tests/unit/easydiffraction/project/categories/info/test_factory.py +++ b/tests/unit/easydiffraction/project/categories/metadata/test_factory.py @@ -7,26 +7,26 @@ def test_project_info_factory_default_and_create(): - from easydiffraction.project.categories.info.default import ProjectInfo - from easydiffraction.project.categories.info.factory import ProjectInfoFactory + from easydiffraction.project.categories.metadata.default import ProjectMetadata + from easydiffraction.project.categories.metadata.factory import ProjectMetadataFactory - assert ProjectInfoFactory.default_tag() == 'default' - assert 'default' in ProjectInfoFactory.supported_tags() + assert ProjectMetadataFactory.default_tag() == 'default' + assert 'default' in ProjectMetadataFactory.supported_tags() - info = ProjectInfoFactory.create( + info = ProjectMetadataFactory.create( 'default', name='beer', title='Beer title', description='Some description', ) - assert isinstance(info, ProjectInfo) + assert isinstance(info, ProjectMetadata) assert info.name == 'beer' assert info.title == 'Beer title' def test_project_info_factory_rejects_unknown_tag(): - from easydiffraction.project.categories.info.factory import ProjectInfoFactory + from easydiffraction.project.categories.metadata.factory import ProjectMetadataFactory with pytest.raises(ValueError, match=r"Unsupported type: 'missing'"): - ProjectInfoFactory.create('missing') + ProjectMetadataFactory.create('missing') diff --git a/tests/unit/easydiffraction/project/categories/rendering_structure/test_default.py b/tests/unit/easydiffraction/project/categories/rendering_structure/test_default.py index 68152b9a1..89d141aa9 100644 --- a/tests/unit/easydiffraction/project/categories/rendering_structure/test_default.py +++ b/tests/unit/easydiffraction/project/categories/rendering_structure/test_default.py @@ -106,9 +106,9 @@ def test_viewer_engine_is_supported(self): rs = RenderingStructure() assert rs.viewer.engine in ViewerFactory.supported_engines() - def test_type_cif_handler_name(self): + def test_type_tags_name(self): rs = RenderingStructure() - assert rs._type._cif_handler.names == ['_rendering_structure.type'] + assert rs._type._tags.edi_names == ['_rendering_structure.type'] def test_parent_starts_detached(self): rs = RenderingStructure() diff --git a/tests/unit/easydiffraction/project/categories/structure_style/test_default.py b/tests/unit/easydiffraction/project/categories/structure_style/test_default.py index a2b7d8190..fd0176b4c 100644 --- a/tests/unit/easydiffraction/project/categories/structure_style/test_default.py +++ b/tests/unit/easydiffraction/project/categories/structure_style/test_default.py @@ -109,15 +109,15 @@ def test_parameters_collects_all_descriptors(self): # ---------------------------------------------------------------------- -class TestStructureStyleCifHandlerNames: - def test_cif_handler_names(self): +class TestStructureStyleTagSpecNames: + def test_tags_names(self): from easydiffraction.project.categories.structure_style.default import StructureStyle style = StructureStyle() - assert style.atom_view._cif_handler.names == ['_structure_style.atom_view'] - assert style.color_scheme._cif_handler.names == ['_structure_style.color_scheme'] - assert style.adp_probability._cif_handler.names == ['_structure_style.adp_probability'] - assert style.atom_scale._cif_handler.names == ['_structure_style.atom_scale'] + assert style.atom_view._tags.edi_names == ['_structure_style.atom_view'] + assert style.color_scheme._tags.edi_names == ['_structure_style.color_scheme'] + assert style.adp_probability._tags.edi_names == ['_structure_style.adp_probability'] + assert style.atom_scale._tags.edi_names == ['_structure_style.atom_scale'] # ---------------------------------------------------------------------- diff --git a/tests/unit/easydiffraction/project/categories/structure_style/test_factory.py b/tests/unit/easydiffraction/project/categories/structure_style/test_factory.py index 06b3195af..7246a7ac0 100644 --- a/tests/unit/easydiffraction/project/categories/structure_style/test_factory.py +++ b/tests/unit/easydiffraction/project/categories/structure_style/test_factory.py @@ -139,15 +139,15 @@ def test_created_instance_defaults(): assert structure_style.atom_scale.value == 0.3 -def test_cif_handler_names(): +def test_tags_names(): structure_style = _make_style() - assert structure_style.atom_view._cif_handler.names == ['_structure_style.atom_view'] - assert structure_style.color_scheme._cif_handler.names == ['_structure_style.color_scheme'] - assert structure_style.adp_probability._cif_handler.names == [ + assert structure_style.atom_view._tags.edi_names == ['_structure_style.atom_view'] + assert structure_style.color_scheme._tags.edi_names == ['_structure_style.color_scheme'] + assert structure_style.adp_probability._tags.edi_names == [ '_structure_style.adp_probability', ] - assert structure_style.atom_scale._cif_handler.names == ['_structure_style.atom_scale'] + assert structure_style.atom_scale._tags.edi_names == ['_structure_style.atom_scale'] # ---------------------------------------------------------------------- diff --git a/tests/unit/easydiffraction/project/categories/structure_view/test_default.py b/tests/unit/easydiffraction/project/categories/structure_view/test_default.py index c933efbba..230fa4962 100644 --- a/tests/unit/easydiffraction/project/categories/structure_view/test_default.py +++ b/tests/unit/easydiffraction/project/categories/structure_view/test_default.py @@ -120,12 +120,12 @@ def test_default_range_maximums(view): # ---------------------------------------------------------------------- -def test_boolean_cif_handler_names(view): - assert view.show_labels._cif_handler.names == ['_structure_view.show_labels'] - assert view.show_moments._cif_handler.names == ['_structure_view.show_moments'] +def test_boolean_tags_names(view): + assert view.show_labels._tags.edi_names == ['_structure_view.show_labels'] + assert view.show_moments._tags.edi_names == ['_structure_view.show_moments'] -def test_range_cif_handler_names(view): +def test_range_tags_names(view): expected = { 'range_a_min': ['_structure_view.range_a_min'], 'range_a_max': ['_structure_view.range_a_max'], @@ -135,7 +135,7 @@ def test_range_cif_handler_names(view): 'range_c_max': ['_structure_view.range_c_max'], } for attr, names in expected.items(): - assert getattr(view, attr)._cif_handler.names == names + assert getattr(view, attr)._tags.edi_names == names def test_descriptor_names_match_attribute(view): diff --git a/tests/unit/easydiffraction/project/test_display.py b/tests/unit/easydiffraction/project/test_display.py index e310cfaf9..9afe6f7a5 100644 --- a/tests/unit/easydiffraction/project/test_display.py +++ b/tests/unit/easydiffraction/project/test_display.py @@ -35,7 +35,9 @@ def _recorder(*args, **kwargs): fittable_params=record('fittable_params'), free_params=record('free_params'), how_to_access_parameters=record('how_to_access_parameters'), - parameter_cif_uids=record('parameter_cif_uids'), + parameter_uids=record('parameter_uids'), + parameter_edi_tags=record('parameter_edi_tags'), + parameter_cif_tags=record('parameter_cif_tags'), fit_results=record('fit_results'), ) plotter = SimpleNamespace( @@ -67,7 +69,7 @@ def _recorder(*args, **kwargs): _persisted_fit_state_sidecar={}, ), rendering_plot=SimpleNamespace(plotter=plotter), - experiments={'hrpt': SimpleNamespace(type=SimpleNamespace())}, + experiments={'hrpt': SimpleNamespace(experiment_type=SimpleNamespace())}, free_parameters=[], verbosity=SimpleNamespace(fit=SimpleNamespace(value='full')), ) @@ -173,14 +175,18 @@ def test_parameter_display_delegates_to_analysis_display(): display.parameters.fittable() display.parameters.free() display.parameters.access() - display.parameters.cif_uids() + display.parameters.uid() + display.parameters.edi() + display.parameters.cif() assert [name for name, _args, _kwargs in calls] == [ 'all_params', 'fittable_params', 'free_params', 'how_to_access_parameters', - 'parameter_cif_uids', + 'parameter_uids', + 'parameter_edi_tags', + 'parameter_cif_tags', ] @@ -322,7 +328,7 @@ def test_posterior_predictive_skips_processing_indicator_for_restored_cache(monk } }, ) - project.experiments = {'hrpt': SimpleNamespace(type=SimpleNamespace())} + project.experiments = {'hrpt': SimpleNamespace(experiment_type=SimpleNamespace())} project.rendering_plot.plotter.engine = 'plotly' project.rendering_plot.plotter._resolve_x_axis = lambda expt_type, x: ( 'two_theta', @@ -549,11 +555,11 @@ def test_pattern_option_statuses_ignore_placeholder_arrays_without_usable_state( ) experiment = SimpleNamespace( - type=SimpleNamespace( + experiment_type=SimpleNamespace( sample_form=SimpleNamespace(value=SampleFormEnum.POWDER.value), scattering_type=SimpleNamespace(value=ScatteringTypeEnum.BRAGG.value), ), - linked_phases=[], + linked_structures=[], background=[], refln=[], excluded_regions=[], @@ -598,11 +604,11 @@ def _recorder(*args, **kwargs): intensity_calc=[9.5, 11.5], ) experiment = SimpleNamespace( - type=SimpleNamespace( + experiment_type=SimpleNamespace( sample_form=SimpleNamespace(value=SampleFormEnum.SINGLE_CRYSTAL.value), scattering_type=SimpleNamespace(value=ScatteringTypeEnum.BRAGG.value), ), - linked_crystal=SimpleNamespace(id=SimpleNamespace(value='si')), + linked_structure=SimpleNamespace(structure_id=SimpleNamespace(value='si')), excluded_regions=[], _has_measured_data=lambda: True, ) diff --git a/tests/unit/easydiffraction/project/test_display_coverage.py b/tests/unit/easydiffraction/project/test_display_coverage.py index 24c02c786..d728f6854 100644 --- a/tests/unit/easydiffraction/project/test_display_coverage.py +++ b/tests/unit/easydiffraction/project/test_display_coverage.py @@ -214,7 +214,7 @@ def _predictive_project(*, sidecar, fit_results, engine='plotly'): fit_results=fit_results, _persisted_fit_state_sidecar=sidecar, ), - experiments={'hrpt': SimpleNamespace(type=SimpleNamespace())}, + experiments={'hrpt': SimpleNamespace(experiment_type=SimpleNamespace())}, rendering_plot=SimpleNamespace(plotter=plotter), ) @@ -701,35 +701,35 @@ def _linked_display(structure_names): return ProjectDisplay(project) -def test_linked_structure_matches_via_linked_phases(): +def test_linked_structure_matches_via_linked_structures(): display = _linked_display(['phase-a']) - linked_phase = SimpleNamespace( + linked_structure = SimpleNamespace( _identity=SimpleNamespace(category_entry_name='phase-a'), ) - experiment = SimpleNamespace(linked_phases=[linked_phase]) + experiment = SimpleNamespace(linked_structures=[linked_structure]) assert display._has_linked_structure_for_calculation(experiment) is True -def test_linked_structure_phases_present_but_no_match_falls_to_crystal(): +def test_linked_structure_structures_present_but_no_match_falls_to_single(): display = _linked_display(['phase-a']) - linked_phase = SimpleNamespace( + linked_structure = SimpleNamespace( _identity=SimpleNamespace(category_entry_name='other'), ) experiment = SimpleNamespace( - linked_phases=[linked_phase], - linked_crystal=SimpleNamespace(id=SimpleNamespace(value='phase-a')), + linked_structures=[linked_structure], + linked_structure=SimpleNamespace(structure_id=SimpleNamespace(value='phase-a')), ) - # No phase matched, but linked_crystal id does. + # No linked structure matched, but linked_structure id does. assert display._has_linked_structure_for_calculation(experiment) is True def test_linked_structure_no_match_anywhere(): display = _linked_display(['phase-a']) experiment = SimpleNamespace( - linked_phases=[], - linked_crystal=SimpleNamespace(id=SimpleNamespace(value='missing')), + linked_structures=[], + linked_structure=SimpleNamespace(structure_id=SimpleNamespace(value='missing')), ) assert display._has_linked_structure_for_calculation(experiment) is False diff --git a/tests/unit/easydiffraction/project/test_project.py b/tests/unit/easydiffraction/project/test_project.py index 137541b2d..04710011d 100644 --- a/tests/unit/easydiffraction/project/test_project.py +++ b/tests/unit/easydiffraction/project/test_project.py @@ -87,10 +87,10 @@ def test_apply_params_from_csv_resolves_relative_file_paths(tmp_path): from easydiffraction.project.project import Project project = Project() - project.info.path = tmp_path / 'project' - analysis_dir = project.info.path / 'analysis' + project.metadata.path = tmp_path / 'project' + analysis_dir = project.metadata.path / 'analysis' analysis_dir.mkdir(parents=True) - data_dir = project.info.path / 'experiments' / 'scan' + data_dir = project.metadata.path / 'experiments' / 'scan' data_dir.mkdir(parents=True) data_path = data_dir / 'scan_001.dat' data_path.write_text('1 2 3\n') @@ -141,13 +141,13 @@ def test_undo_fit_save_reload_preserves_fit_parameter_controls(tmp_path): parameter.uncertainty = 0.04 parameter.fit_min = 3.8 parameter.fit_max = 4.0 - parameter._set_fit_bounds_uncertainty_multiplier(4.0) + parameter._set_bounds_uncertainty_multiplier(4.0) project.analysis.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=parameter.fit_min, fit_max=parameter.fit_max, - fit_bounds_uncertainty_multiplier=4.0, + bounds_uncertainty_multiplier=4.0, start_value=3.87, start_uncertainty=0.02, ) @@ -172,13 +172,13 @@ def test_undo_fit_save_reload_preserves_fit_parameter_controls(tmp_path): assert loaded.analysis.fit_results is None assert loaded_row.fit_min.value == 3.8 assert loaded_row.fit_max.value == 4.0 - assert loaded_row.fit_bounds_uncertainty_multiplier.value == 4.0 + assert loaded_row.bounds_uncertainty_multiplier.value == 4.0 assert loaded_row.start_value.value == 3.87 assert loaded_row.start_uncertainty.value == 0.02 assert loaded_parameter.value == 3.87 assert loaded_parameter.fit_min == 3.8 assert loaded_parameter.fit_max == 4.0 - assert loaded_parameter.fit_bounds_uncertainty_multiplier == 4.0 + assert loaded_parameter.bounds_uncertainty_multiplier == 4.0 assert loaded_parameter._fit_start_value == 3.87 assert loaded_parameter._fit_start_uncertainty == 0.02 assert second_outcome.was_no_op is True diff --git a/tests/unit/easydiffraction/project/test_project_config.py b/tests/unit/easydiffraction/project/test_project_config.py index eba88ae2a..a849cb644 100644 --- a/tests/unit/easydiffraction/project/test_project_config.py +++ b/tests/unit/easydiffraction/project/test_project_config.py @@ -12,29 +12,29 @@ def test_project_config_exposes_project_info_chart_and_table_categories(): from easydiffraction.project.categories.rendering_table import RenderingTable from easydiffraction.project.categories.report import Report from easydiffraction.project.project_config import ProjectConfig - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata config = ProjectConfig(name='beer', title='Beer title', description='Some description') assert isinstance(config, CategoryOwner) - assert isinstance(config.info, ProjectInfo) + assert isinstance(config.metadata, ProjectMetadata) assert isinstance(config.rendering_plot, RenderingPlot) assert isinstance(config.report, Report) assert isinstance(config.rendering_table, RenderingTable) - assert config.info._parent is config + assert config.metadata._parent is config assert config.rendering_plot._parent is config assert config.report._parent is config assert config.rendering_table._parent is config - assert config.info.name == 'beer' - assert config.info.title == 'Beer title' - assert config.info.description == 'Some description' - assert config.info.path is None - assert isinstance(config.info.created, datetime.datetime) - assert isinstance(config.info.last_modified, datetime.datetime) + assert config.metadata.name == 'beer' + assert config.metadata.title == 'Beer title' + assert config.metadata.description == 'Some description' + assert config.metadata.path is None + assert isinstance(config.metadata.created, datetime.datetime) + assert isinstance(config.metadata.last_modified, datetime.datetime) assert config.verbosity._parent is config assert config.verbosity.fit.value == 'full' assert config.categories == [ - config.info, + config.metadata, config.rendering_plot, config.report, config.rendering_table, @@ -44,7 +44,7 @@ def test_project_config_exposes_project_info_chart_and_table_categories(): config.structure_style, ] assert config.parameters == ( - config.info.parameters + config.metadata.parameters + config.rendering_plot.parameters + config.report.parameters + config.rendering_table.parameters @@ -63,11 +63,11 @@ def test_project_config_as_cif_has_project_chart_and_table_sections_without_data cif_text = config.as_cif assert not cif_text.startswith('data_') - assert '_project.id beer' in cif_text - assert '_project.title' in cif_text - assert '_project.description' in cif_text - assert '_project.created' in cif_text - assert '_project.last_modified' in cif_text + assert '_metadata.name beer' in cif_text + assert '_metadata.title' in cif_text + assert '_metadata.description' in cif_text + assert '_metadata.created' in cif_text + assert '_metadata.last_modified' in cif_text assert '_rendering_plot.type' in cif_text assert '_report.cif' in cif_text assert '_report.html' in cif_text @@ -88,7 +88,7 @@ def test_project_save_and_load_use_auto_display_defaults_when_unset(tmp_path): project = Project(name='beer', title='Beer title', description='Some description') project.save_as(str(tmp_path / 'proj')) - project_cif = (tmp_path / 'proj' / 'project.cif').read_text() + project_cif = (tmp_path / 'proj' / 'project.edi').read_text() assert not project_cif.startswith('data_') assert '_rendering_plot.type auto' in project_cif @@ -113,20 +113,20 @@ def test_project_save_and_load_keep_project_config_section_format(tmp_path): project.rendering_table.type = 'rich' project.save_as(str(tmp_path / 'proj')) - project_cif = (tmp_path / 'proj' / 'project.cif').read_text() + project_cif = (tmp_path / 'proj' / 'project.edi').read_text() assert not project_cif.startswith('data_') - assert '_project.id beer' in project_cif + assert '_metadata.name beer' in project_cif assert '_rendering_plot.type asciichartpy' in project_cif assert '_report.cif false' in project_cif assert '_rendering_table.type rich' in project_cif assert '_verbosity.fit full' in project_cif loaded = Project.load(str(tmp_path / 'proj')) - assert loaded.info.name == 'beer' - assert loaded.info.title == 'Beer title' - assert loaded.info.description == 'Some description' - assert isinstance(loaded.info.created, datetime.datetime) - assert isinstance(loaded.info.last_modified, datetime.datetime) + assert loaded.metadata.name == 'beer' + assert loaded.metadata.title == 'Beer title' + assert loaded.metadata.description == 'Some description' + assert isinstance(loaded.metadata.created, datetime.datetime) + assert isinstance(loaded.metadata.last_modified, datetime.datetime) assert loaded.rendering_plot.type == 'asciichartpy' assert loaded.rendering_table.type == 'rich' assert loaded.verbosity.fit.value == 'full' @@ -143,14 +143,14 @@ def test_project_save_wraps_long_description_as_cif_text_field(tmp_path): project = Project(name='beer', title='Beer title', description=description) project.save_as(str(tmp_path / 'proj')) - project_cif = (tmp_path / 'proj' / 'project.cif').read_text() + project_cif = (tmp_path / 'proj' / 'project.edi').read_text() - assert '_project.description' in project_cif - description_tail = project_cif.split('_project.description', maxsplit=1)[1].lstrip(' ') + assert '_metadata.description' in project_cif + description_tail = project_cif.split('_metadata.description', maxsplit=1)[1].lstrip(' ') assert description_tail.startswith('\n;\n') - assert '\n;\n_project.created' in project_cif + assert '\n;\n_metadata.created' in project_cif description_block = description_tail.split('\n;\n', maxsplit=1)[1] - description_block = description_block.split('\n;\n_project.created', maxsplit=1)[0] + description_block = description_block.split('\n;\n_metadata.created', maxsplit=1)[0] description_lines = description_block.splitlines() assert len(description_lines) > 1 @@ -161,4 +161,4 @@ def test_project_save_wraps_long_description_as_cif_text_field(tmp_path): loaded = Project.load(str(tmp_path / 'proj')) - assert loaded.info.description == description + assert loaded.metadata.description == description diff --git a/tests/unit/easydiffraction/project/test_project_coverage.py b/tests/unit/easydiffraction/project/test_project_coverage.py index 7da497029..54ac5722f 100644 --- a/tests/unit/easydiffraction/project/test_project_coverage.py +++ b/tests/unit/easydiffraction/project/test_project_coverage.py @@ -22,11 +22,11 @@ from easydiffraction.project.project import Project from easydiffraction.project.project import _apply_csv_row_to_diffrn from easydiffraction.project.project import _apply_csv_row_to_params -from easydiffraction.project.project import _load_cif_directory +from easydiffraction.project.project import _load_edi_directory from easydiffraction.project.project import _load_project_analysis -from easydiffraction.project.project import _load_project_info +from easydiffraction.project.project import _load_project_metadata from easydiffraction.project.project import _resolve_data_path_from_results_csv -from easydiffraction.project.project import _resolved_analysis_cif_path +from easydiffraction.project.project import _resolved_analysis_path from easydiffraction.utils.logging import Logger @@ -163,48 +163,68 @@ def test_resolve_data_path_joins_relative_to_project(): # ---------------------------------------------------------------------- -# _load_cif_directory / _load_project_info / _resolved_analysis_cif_path +# _load_edi_directory / _load_project_metadata / _resolved_analysis_path # ---------------------------------------------------------------------- -def test_load_cif_directory_skips_missing_directory(tmp_path): +def test_load_edi_directory_skips_missing_directory(tmp_path): calls: list[str] = [] - _load_cif_directory(tmp_path / 'absent', calls.append) + _load_edi_directory( + tmp_path / 'absent', + calls.append, + replacement='structures/<structure>.edi', + ) assert calls == [] -def test_load_cif_directory_loads_sorted_cif_files(tmp_path): - cif_dir = tmp_path / 'structures' - cif_dir.mkdir() - (cif_dir / 'b.cif').write_text('b') - (cif_dir / 'a.cif').write_text('a') - (cif_dir / 'note.txt').write_text('ignored') +def test_load_edi_directory_loads_sorted_edi_files(tmp_path): + edi_dir = tmp_path / 'structures' + edi_dir.mkdir() + (edi_dir / 'b.edi').write_text('b') + (edi_dir / 'a.edi').write_text('a') + (edi_dir / 'note.txt').write_text('ignored') calls: list[str] = [] - _load_cif_directory(cif_dir, calls.append) + _load_edi_directory( + edi_dir, + calls.append, + replacement='structures/<structure>.edi', + ) - assert calls == [str(cif_dir / 'a.cif'), str(cif_dir / 'b.cif')] + assert calls == [str(edi_dir / 'a.edi'), str(edi_dir / 'b.edi')] -def test_load_project_info_no_cif_leaves_project_untouched(tmp_path): - project = Project(name='unchanged_info') +def test_load_edi_directory_rejects_legacy_cif(tmp_path): + edi_dir = tmp_path / 'structures' + edi_dir.mkdir() + (edi_dir / 'lbco.cif').write_text('legacy') - _load_project_info(project, tmp_path) + with pytest.raises(ValueError, match=r'structures/<structure>\.edi'): + _load_edi_directory( + edi_dir, + lambda _path: None, + replacement='structures/<structure>.edi', + ) + + +def test_load_project_metadata_no_edi_raises(tmp_path): + project = Project(name='unchanged_info') - assert project.name == 'unchanged_info' + with pytest.raises(FileNotFoundError, match=r'project\.edi'): + _load_project_metadata(project, tmp_path) -def test_resolved_analysis_cif_path_returns_none_when_absent(tmp_path): - assert _resolved_analysis_cif_path(tmp_path) is None +def test_resolved_analysis_path_returns_none_when_absent(tmp_path): + assert _resolved_analysis_path(tmp_path) is None -def test_resolved_analysis_cif_path_uses_root_fallback(tmp_path): - root_cif = tmp_path / 'analysis.cif' - root_cif.write_text('analysis') +def test_resolved_analysis_path_uses_root_fallback(tmp_path): + root_edi = tmp_path / 'analysis.edi' + root_edi.write_text('analysis') - assert _resolved_analysis_cif_path(tmp_path) == root_cif + assert _resolved_analysis_path(tmp_path) == root_edi def test_load_project_analysis_no_cif_is_noop(tmp_path): @@ -236,7 +256,7 @@ def test_current_project_path_none_when_unsaved(monkeypatch): def test_current_project_path_reports_saved_path(tmp_path): project = Project(name='tracked') expected = tmp_path / 'tracked-project' - project.info.path = expected + project.metadata.path = expected assert Project.current_project_path() == expected @@ -348,11 +368,11 @@ def test_resolve_alias_references_warns_on_unknown_parameter(monkeypatch): structure.cell.length_a = 4.0 project.analysis.aliases.create( - label='a_param', + id='a_param', param=structure.cell.length_a, ) alias = project.analysis.aliases['a_param'] - alias.param_unique_name.value = 'does.not.exist' + alias.parameter_unique_name.value = 'does.not.exist' warnings: list[str] = [] monkeypatch.setattr(project_module.log, 'warning', warnings.append) @@ -376,15 +396,15 @@ def test_save_without_path_logs_error_and_returns(monkeypatch): project.save() - assert project.info.path is None + assert project.metadata.path is None assert any('save_as()' in message for message in errors) -def test_save_writes_experiment_cif_files(tmp_path, monkeypatch): +def test_save_writes_experiment_edi_files(tmp_path, monkeypatch): from easydiffraction.analysis.analysis import Analysis - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - monkeypatch.setattr(ProjectInfo, 'as_cif', property(lambda self: 'info')) + monkeypatch.setattr(ProjectMetadata, 'as_cif', property(lambda self: 'info')) monkeypatch.setattr(Analysis, 'as_cif', property(lambda self: 'analysis')) project = Project(name='with_experiments') @@ -400,15 +420,18 @@ def values(): project._experiments = _Experiments(parameters=[]) project.save_as(str(tmp_path / 'proj')) - written = (tmp_path / 'proj' / 'experiments' / 'scan1.cif').read_text() - assert written == 'data_scan1' + # Experiments are persisted as Edi files carrying the schema + # marker; the original section header is preserved. + written = (tmp_path / 'proj' / 'experiments' / 'scan1.edi').read_text() + assert written.startswith('data_scan1') + assert '_edi.schema_version 1' in written def test_save_as_temporary_writes_under_system_tempdir(monkeypatch): from easydiffraction.analysis.analysis import Analysis - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - monkeypatch.setattr(ProjectInfo, 'as_cif', property(lambda self: 'info')) + monkeypatch.setattr(ProjectMetadata, 'as_cif', property(lambda self: 'info')) monkeypatch.setattr(Analysis, 'as_cif', property(lambda self: 'analysis')) project = Project(name='temp_save') @@ -418,8 +441,8 @@ def test_save_as_temporary_writes_under_system_tempdir(monkeypatch): try: project.save_as(unique_dir, temporary=True) expected = pathlib.Path(tempfile.gettempdir()) / unique_dir - assert (expected / 'project.cif').is_file() - assert project.info.path == expected + assert (expected / 'project.edi').is_file() + assert project.metadata.path == expected finally: import shutil @@ -430,9 +453,9 @@ def test_save_as_temporary_writes_under_system_tempdir(monkeypatch): def test_save_as_overwrite_clears_children_when_target_is_cwd(tmp_path, monkeypatch): from easydiffraction.analysis.analysis import Analysis - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - monkeypatch.setattr(ProjectInfo, 'as_cif', property(lambda self: 'info')) + monkeypatch.setattr(ProjectMetadata, 'as_cif', property(lambda self: 'info')) monkeypatch.setattr(Analysis, 'as_cif', property(lambda self: 'analysis')) target = tmp_path / 'cwd_project' @@ -453,7 +476,7 @@ def test_save_as_overwrite_clears_children_when_target_is_cwd(tmp_path, monkeypa # children are removed before the fresh project is written. assert not stale_file.exists() assert not stale_dir.exists() - assert (target / 'project.cif').is_file() + assert (target / 'project.edi').is_file() # ---------------------------------------------------------------------- @@ -470,8 +493,8 @@ def test_apply_params_from_csv_requires_saved_path(): def test_apply_params_from_csv_missing_results_csv(tmp_path): project = Project(name='missing_csv') - project.info.path = tmp_path / 'proj' - (project.info.path / 'analysis').mkdir(parents=True) + project.metadata.path = tmp_path / 'proj' + (project.metadata.path / 'analysis').mkdir(parents=True) with pytest.raises(FileNotFoundError, match='Results CSV not found'): project.apply_params_from_csv(0) @@ -520,8 +543,8 @@ def values(): def test_apply_params_from_csv_out_of_range_raises(tmp_path): project = Project(name='range_csv') - project.info.path = tmp_path / 'proj' - _write_results_csv(project.info.path / 'analysis', [{'file_path': ''}]) + project.metadata.path = tmp_path / 'proj' + _write_results_csv(project.metadata.path / 'analysis', [{'file_path': ''}]) _stub_project_collections(project) with pytest.raises(IndexError, match='out of range'): @@ -530,8 +553,8 @@ def test_apply_params_from_csv_out_of_range_raises(tmp_path): def test_apply_params_from_csv_negative_index_out_of_range_raises(tmp_path): project = Project(name='neg_range_csv') - project.info.path = tmp_path / 'proj' - _write_results_csv(project.info.path / 'analysis', [{'file_path': ''}]) + project.metadata.path = tmp_path / 'proj' + _write_results_csv(project.metadata.path / 'analysis', [{'file_path': ''}]) _stub_project_collections(project) with pytest.raises(IndexError, match='out of range'): @@ -542,12 +565,12 @@ def test_apply_params_from_csv_negative_index_skips_absent_data_file(tmp_path, m monkeypatch.setattr(Logger, '_reaction', Logger.Reaction.WARN, raising=True) project = Project(name='neg_csv') - project.info.path = tmp_path / 'proj' + project.metadata.path = tmp_path / 'proj' _write_results_csv( - project.info.path / 'analysis', + project.metadata.path / 'analysis', [ {'file_path': 'first.dat'}, - {'file_path': 'experiments/missing.dat'}, + {'file_path': 'expt-missing.dat'}, ], ) fakes = _stub_project_collections(project) diff --git a/tests/unit/easydiffraction/project/test_project_load.py b/tests/unit/easydiffraction/project/test_project_load.py index 2a5c1b4a0..bb7d4658a 100644 --- a/tests/unit/easydiffraction/project/test_project_load.py +++ b/tests/unit/easydiffraction/project/test_project_load.py @@ -24,9 +24,9 @@ def test_round_trips_empty_project(self, tmp_path): loaded = Project.load(str(tmp_path / 'proj')) assert loaded.name == 'empty' - assert loaded.info.title == 'Empty' - assert loaded.info.description == 'nothing' - assert loaded.info.path is not None + assert loaded.metadata.title == 'Empty' + assert loaded.metadata.description == 'nothing' + assert loaded.metadata.path is not None assert len(loaded.structures) == 0 assert len(loaded.experiments) == 0 @@ -41,7 +41,7 @@ def test_round_trips_structure(self, tmp_path): s.space_group.name_h_m = 'P m -3 m' s.cell.length_a = 3.88 s.atom_sites.create( - label='Co', + id='Co', type_symbol='Co', fract_x=0.0, fract_y=0.0, @@ -104,11 +104,11 @@ def test_round_trips_constraints(self, tmp_path): s.cell.length_b = 5.0 original.analysis.aliases.create( - label='a_param', + id='a_param', param=s.cell.length_a, ) original.analysis.aliases.create( - label='b_param', + id='b_param', param=s.cell.length_b, ) original.analysis.constraints.create(expression='b_param = a_param') @@ -117,8 +117,8 @@ def test_round_trips_constraints(self, tmp_path): loaded = Project.load(str(tmp_path / 'proj')) assert len(loaded.analysis.aliases) == 2 - assert loaded.analysis.aliases['a_param'].label.value == 'a_param' - assert loaded.analysis.aliases['b_param'].label.value == 'b_param' + assert loaded.analysis.aliases['a_param'].id.value == 'a_param' + assert loaded.analysis.aliases['b_param'].id.value == 'b_param' # Verify alias param references are resolved assert loaded.analysis.aliases['a_param'].param is not None assert loaded.analysis.aliases['b_param'].param is not None @@ -140,15 +140,15 @@ def test_round_trips_deterministic_fit_state_and_keeps_live_parameter_values(sel parameter.uncertainty = 0.07 parameter.fit_min = 3.8 parameter.fit_max = 3.9 - parameter._set_fit_bounds_uncertainty_multiplier(4.0) + parameter._set_bounds_uncertainty_multiplier(4.0) parameter._fit_start_value = 3.87 parameter._fit_start_uncertainty = 0.02 original.analysis.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=parameter.fit_min, fit_max=parameter.fit_max, - fit_bounds_uncertainty_multiplier=4.0, + bounds_uncertainty_multiplier=4.0, start_value=3.87, start_uncertainty=0.02, ) @@ -169,7 +169,7 @@ def test_round_trips_deterministic_fit_state_and_keeps_live_parameter_values(sel assert loaded_parameter.value == 3.88 assert loaded_parameter.fit_min == 3.8 assert loaded_parameter.fit_max == 3.9 - assert loaded_parameter.fit_bounds_uncertainty_multiplier == 4.0 + assert loaded_parameter.bounds_uncertainty_multiplier == 4.0 assert loaded_parameter._fit_start_value == 3.87 assert loaded_parameter._fit_start_uncertainty == 0.02 assert loaded_parameter.uncertainty == 0.07 @@ -197,14 +197,14 @@ def test_round_trips_persisted_deterministic_correlation_summary_for_reloaded_di parameter.uncertainty = 0.05 parameter.fit_min = 3.8 parameter.fit_max = 3.9 - parameter._set_fit_bounds_uncertainty_multiplier(4.0) + parameter._set_bounds_uncertainty_multiplier(4.0) parameter._fit_start_value = start_value parameter._fit_start_uncertainty = 0.02 original.analysis.fit_parameters.create( - param_unique_name=parameter.unique_name, + parameter_unique_name=parameter.unique_name, fit_min=parameter.fit_min, fit_max=parameter.fit_max, - fit_bounds_uncertainty_multiplier=4.0, + bounds_uncertainty_multiplier=4.0, start_value=start_value, start_uncertainty=0.02, ) @@ -225,8 +225,8 @@ def test_round_trips_persisted_deterministic_correlation_summary_for_reloaded_di original.analysis.fit_result._set_correlation_available(value=True) original.analysis.fit_parameter_correlations.create( source_kind='deterministic', - param_unique_name_i=parameter_b.unique_name, - param_unique_name_j=parameter_a.unique_name, + parameter_unique_name_i=parameter_b.unique_name, + parameter_unique_name_j=parameter_a.unique_name, correlation=0.42, ) original.analysis._set_has_persisted_fit_state(value=True) @@ -301,7 +301,7 @@ def test_loads_analysis_from_subdir(self, tmp_path): original.save_as(str(tmp_path / 'proj')) # Verify analysis.cif is in analysis/ subdirectory (current save layout) - assert (tmp_path / 'proj' / 'analysis' / 'analysis.cif').is_file() + assert (tmp_path / 'proj' / 'analysis' / 'analysis.edi').is_file() loaded = Project.load(str(tmp_path / 'proj')) assert loaded.analysis.minimizer.type == 'lmfit (leastsq)' @@ -314,7 +314,7 @@ def test_loads_analysis_from_root_fallback(self, tmp_path): # Move analysis.cif from analysis/ subdirectory to project root proj_dir = tmp_path / 'proj' analysis_dir = proj_dir / 'analysis' - (analysis_dir / 'analysis.cif').rename(proj_dir / 'analysis.cif') + (analysis_dir / 'analysis.edi').rename(proj_dir / 'analysis.edi') analysis_dir.rmdir() loaded = Project.load(str(proj_dir)) diff --git a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py index 6ba24a06b..cc0c6810f 100644 --- a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py +++ b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py @@ -20,6 +20,6 @@ def test_project_load_reads_project_info(tmp_path): loaded = Project.load(str(tmp_path / 'proj')) assert loaded.name == 'myproj' - assert loaded.info.title == 'My Title' - assert loaded.info.description == 'A description' - assert loaded.info.path is not None + assert loaded.metadata.title == 'My Title' + assert loaded.metadata.description == 'A description' + assert loaded.metadata.path is not None diff --git a/tests/unit/easydiffraction/project/test_project_info.py b/tests/unit/easydiffraction/project/test_project_metadata.py similarity index 65% rename from tests/unit/easydiffraction/project/test_project_info.py rename to tests/unit/easydiffraction/project/test_project_metadata.py index 8c3455ab5..cea01c7ab 100644 --- a/tests/unit/easydiffraction/project/test_project_info.py +++ b/tests/unit/easydiffraction/project/test_project_metadata.py @@ -3,8 +3,8 @@ def test_module_import(): - import easydiffraction.project.project_info as MUT + import easydiffraction.project.project_metadata as MUT - expected_module_name = 'easydiffraction.project.project_info' + expected_module_name = 'easydiffraction.project.project_metadata' actual_module_name = MUT.__name__ assert expected_module_name == actual_module_name diff --git a/tests/unit/easydiffraction/project/test_project_save.py b/tests/unit/easydiffraction/project/test_project_save.py index f061aa168..93d2e5f32 100644 --- a/tests/unit/easydiffraction/project/test_project_save.py +++ b/tests/unit/easydiffraction/project/test_project_save.py @@ -13,8 +13,8 @@ def test_project_save_uses_cwd_when_no_explicit_path(monkeypatch, tmp_path, caps out = capsys.readouterr().out # It should announce saving and create the three core files assert 'Saving project' in out - assert (tmp_path / 'project.cif').exists() - assert (tmp_path / 'analysis' / 'analysis.cif').exists() + assert (tmp_path / 'project.edi').exists() + assert (tmp_path / 'analysis' / 'analysis.edi').exists() assert not (tmp_path / 'summary.cif').exists() assert not (tmp_path / 'reports').exists() @@ -22,10 +22,10 @@ def test_project_save_uses_cwd_when_no_explicit_path(monkeypatch, tmp_path, caps def test_project_save_as_writes_core_files(tmp_path, monkeypatch): from easydiffraction.analysis.analysis import Analysis from easydiffraction.project.project import Project - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata # Monkeypatch as_cif producers to avoid heavy internals - monkeypatch.setattr(ProjectInfo, 'as_cif', property(lambda self: 'info')) + monkeypatch.setattr(ProjectMetadata, 'as_cif', property(lambda self: 'info')) monkeypatch.setattr(Analysis, 'as_cif', property(lambda self: 'analysis')) p = Project(name='p1') @@ -34,8 +34,8 @@ def test_project_save_as_writes_core_files(tmp_path, monkeypatch): p.save_as(str(target)) # Assert expected files/dirs exist - assert (target / 'project.cif').is_file() - assert (target / 'analysis' / 'analysis.cif').is_file() + assert (target / 'project.edi').is_file() + assert (target / 'analysis' / 'analysis.edi').is_file() assert not (target / 'summary.cif').exists() assert not (target / 'reports').exists() assert (target / 'structures').is_dir() @@ -45,9 +45,9 @@ def test_project_save_as_writes_core_files(tmp_path, monkeypatch): def test_project_save_lists_existing_analysis_results_csv(tmp_path, monkeypatch, capsys): from easydiffraction.analysis.analysis import Analysis from easydiffraction.project.project import Project - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - monkeypatch.setattr(ProjectInfo, 'as_cif', property(lambda self: 'info')) + monkeypatch.setattr(ProjectMetadata, 'as_cif', property(lambda self: 'info')) monkeypatch.setattr(Analysis, 'as_cif', property(lambda self: 'analysis')) target = tmp_path / 'proj_dir' @@ -56,20 +56,20 @@ def test_project_save_lists_existing_analysis_results_csv(tmp_path, monkeypatch, (analysis_dir / 'results.csv').write_text('file_path\nscan_001.xye\n') p = Project(name='p1') - p.info.path = target + p.metadata.path = target p.save() out = capsys.readouterr().out - assert 'analysis.cif' in out + assert 'analysis.edi' in out assert 'results.csv' in out def test_project_save_as_overwrites_existing_directory_by_default(tmp_path, monkeypatch): from easydiffraction.analysis.analysis import Analysis from easydiffraction.project.project import Project - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - monkeypatch.setattr(ProjectInfo, 'as_cif', property(lambda self: 'info')) + monkeypatch.setattr(ProjectMetadata, 'as_cif', property(lambda self: 'info')) monkeypatch.setattr(Analysis, 'as_cif', property(lambda self: 'analysis')) target = tmp_path / 'proj_dir' @@ -81,15 +81,15 @@ def test_project_save_as_overwrites_existing_directory_by_default(tmp_path, monk project.save_as(str(target)) assert not stale_file.exists() - assert (target / 'project.cif').is_file() + assert (target / 'project.edi').is_file() def test_project_save_as_preserves_existing_directory_when_disabled(tmp_path, monkeypatch): from easydiffraction.analysis.analysis import Analysis from easydiffraction.project.project import Project - from easydiffraction.project.project_info import ProjectInfo + from easydiffraction.project.project_metadata import ProjectMetadata - monkeypatch.setattr(ProjectInfo, 'as_cif', property(lambda self: 'info')) + monkeypatch.setattr(ProjectMetadata, 'as_cif', property(lambda self: 'info')) monkeypatch.setattr(Analysis, 'as_cif', property(lambda self: 'analysis')) target = tmp_path / 'proj_dir' @@ -104,7 +104,7 @@ def test_project_save_as_preserves_existing_directory_when_disabled(tmp_path, mo ) assert stale_file.exists() - assert (target / 'project.cif').is_file() + assert (target / 'project.edi').is_file() def test_project_save_omits_empty_fit_state_sections(tmp_path): @@ -113,7 +113,7 @@ def test_project_save_omits_empty_fit_state_sections(tmp_path): project = Project(name='no_fit_state') project.save_as(str(tmp_path / 'proj')) - analysis_cif = (tmp_path / 'proj' / 'analysis' / 'analysis.cif').read_text() + analysis_cif = (tmp_path / 'proj' / 'analysis' / 'analysis.edi').read_text() - assert '_fit_parameter.param_unique_name' not in analysis_cif + assert '_fit_parameter.parameter_unique_name' not in analysis_cif assert '_fit_result.result_kind' not in analysis_cif diff --git a/tests/unit/easydiffraction/report/test_data_context.py b/tests/unit/easydiffraction/report/test_data_context.py index 8eccd923a..0aa21a70f 100644 --- a/tests/unit/easydiffraction/report/test_data_context.py +++ b/tests/unit/easydiffraction/report/test_data_context.py @@ -47,12 +47,12 @@ def resolve_display_units(context): def _parameter(name, value, uncertainty): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec parameter = Parameter( name=name, value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=[f'_{name}']), + tags=TagSpec(edi_names=[f'_{name}']), ) parameter.value = value parameter.free = True @@ -77,7 +77,7 @@ def _structure() -> SimpleNamespace: ), atom_sites=[ SimpleNamespace( - label=_Descriptor('Si1'), + id=_Descriptor('Si1'), type_symbol=_Descriptor('Si'), fract_x=_parameter('fract_x', 11.98509310, 0.03069505), fract_y=_parameter('fract_y', 0.0, None), @@ -89,7 +89,7 @@ def _structure() -> SimpleNamespace: ], atom_site_aniso=[ SimpleNamespace( - label=_Descriptor('Si1'), + id=_Descriptor('Si1'), adp_11=_parameter('adp_11', 0.00658189, 0.00014), adp_22=_parameter('adp_22', 0.00488144, 0.00029), adp_33=_parameter('adp_33', 0.00488144, None), @@ -104,7 +104,7 @@ def _structure() -> SimpleNamespace: def _experiment() -> SimpleNamespace: return SimpleNamespace( name='heidi', - type=SimpleNamespace( + experiment_type=SimpleNamespace( sample_form=_Descriptor('single crystal'), beam_mode=_Descriptor('constant wavelength'), radiation_probe=_Descriptor('neutron'), @@ -133,7 +133,11 @@ def _experiment() -> SimpleNamespace: def _project() -> SimpleNamespace: return SimpleNamespace( name='demo', - info=SimpleNamespace(title=_Descriptor('Demo'), description=_Descriptor(None)), + metadata=SimpleNamespace( + title=_Descriptor('Demo'), + description=_Descriptor(None), + timestamp=None, + ), structures={'phase': _structure()}, experiments={'heidi': _experiment()}, analysis=SimpleNamespace( @@ -162,7 +166,7 @@ def test_report_data_context_builds_powder_bragg_tick_sets(): from easydiffraction.report.data_context import build_report_data_context experiment = _experiment() - experiment.type.sample_form = _Descriptor('powder') + experiment.experiment_type.sample_form = _Descriptor('powder') experiment.x_descriptor = _TwoThetaDescriptor() experiment.fit_data_arrays = lambda: { 'x': np.array([1.0, 2.0]), @@ -173,7 +177,7 @@ def test_report_data_context_builds_powder_bragg_tick_sets(): 'bkg': np.array([2.0, 2.5]), } experiment.refln = SimpleNamespace( - phase_id=np.array(['phase-a']), + structure_id=np.array(['phase-a']), two_theta=np.array([1.5]), index_h=np.array([1]), index_k=np.array([0]), @@ -187,7 +191,7 @@ def test_report_data_context_builds_powder_bragg_tick_sets(): context = build_report_data_context(project) tick_sets = context['experiments'][0]['fit_data']['bragg_tick_sets'] - assert [tick_set.phase_id for tick_set in tick_sets] == ['phase-a'] + assert [tick_set.structure_id for tick_set in tick_sets] == ['phase-a'] assert list(tick_sets[0].x) == [1.5] @@ -210,8 +214,8 @@ def test_report_category_context_keeps_numeric_string_ids_as_text(): from easydiffraction.report.data_context import _collection_category_context category = LineSegmentBackground() - category.create(id='10', x=10.0, y=2.0) - category.create(id='30', x=30.0, y=3.0) + category.create(id='10', position=10.0, intensity=2.0) + category.create(id='30', position=30.0, intensity=3.0) context = _collection_category_context(category) @@ -328,7 +332,7 @@ def test_report_powder_refln_columns_use_compact_labels(): category = PowderCwlReflnData() category._replace_from_records([ PowderReflnRecord( - phase_id='phase', + structure_id='phase', d_spacing=1.0, sin_theta_over_lambda=0.5, index_h=1, @@ -344,7 +348,7 @@ def test_report_powder_refln_columns_use_compact_labels(): assert [(column['latex_label'], column['html_label']) for column in context['columns']] == [ ('ID', 'ID'), - ('Phase', 'Phase'), + ('Structure', 'Structure'), (r'$d$', r'\(d\)'), (r'$\sin\theta/\lambda$', r'\(\sin\theta/\lambda\)'), (r'$h$', r'\(h\)'), @@ -362,13 +366,13 @@ def test_report_atom_site_adp_column_uses_active_b_u_labels(): structure = Structure(name='phase') structure.atom_sites.create( - label='Si1', + id='Si1', type_symbol='Si', adp_type='Biso', adp_iso=0.5, ) structure.atom_sites.create( - label='O1', + id='O1', type_symbol='O', adp_type='Uiso', adp_iso=0.006, @@ -388,7 +392,7 @@ def test_report_atom_site_aniso_adp_column_uses_active_b_label(): structure = Structure(name='phase') structure.atom_sites.create( - label='Si1', + id='Si1', type_symbol='Si', adp_iso=0.5, ) @@ -447,7 +451,7 @@ def test_report_descriptor_rows_normalize_angstrom_for_mathjax(): from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec from easydiffraction.report.data_context import _descriptor_rows parameter = Parameter( @@ -457,7 +461,7 @@ def test_report_descriptor_rows_normalize_angstrom_for_mathjax(): latex_name=r'$U_{\mathrm{iso}}$', latex_units=r'\AA$^2$', ), - cif_handler=CifHandler(names=['_atom_site.U_iso_or_equiv']), + tags=TagSpec(edi_names=['_atom_site.U_iso_or_equiv']), ) rows = _descriptor_rows([parameter]) @@ -470,7 +474,7 @@ def test_report_descriptor_rows_preserve_mixed_mathjax_label_text(): from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec from easydiffraction.report.data_context import _descriptor_rows parameter = Parameter( @@ -480,7 +484,7 @@ def test_report_descriptor_rows_preserve_mixed_mathjax_label_text(): latex_name=r'$2\theta$ offset', latex_units=r'$^\circ$', ), - cif_handler=CifHandler(names=['_instr.2theta_offset']), + tags=TagSpec(edi_names=['_instr.2theta_offset']), ) rows = _descriptor_rows([parameter]) @@ -502,7 +506,7 @@ def test_descriptor_units_unchanged_for_non_beta_parameters(): from easydiffraction.core.display_handler import DisplayHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec from easydiffraction.report.data_context import _descriptor_units # F2 regression: routing _descriptor_units through resolve_display_units @@ -512,7 +516,7 @@ def test_descriptor_units_unchanged_for_non_beta_parameters(): units='angstroms', display_handler=DisplayHandler(display_units='Å', latex_units=r'\AA'), value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_p']), + tags=TagSpec(edi_names=['_p']), ) assert _descriptor_units(with_handler, context='html') == 'Å' assert _descriptor_units(with_handler, context='latex') == r'\AA' @@ -522,7 +526,7 @@ def test_descriptor_units_unchanged_for_non_beta_parameters(): name='q', units='degrees', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_q']), + tags=TagSpec(edi_names=['_q']), ) # No display_handler -> resolves to the declared unit. assert _descriptor_units(fallback, context='html') == 'degrees' @@ -531,7 +535,7 @@ def test_descriptor_units_unchanged_for_non_beta_parameters(): def test_descriptor_units_resolves_none_unit_to_empty_string(): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec from easydiffraction.report.data_context import _descriptor_units # Intentional delta from routing through resolve_display_units: a @@ -541,7 +545,7 @@ def test_descriptor_units_resolves_none_unit_to_empty_string(): name='r', units='none', value_spec=AttributeSpec(default=0.0), - cif_handler=CifHandler(names=['_r']), + tags=TagSpec(edi_names=['_r']), ) assert _descriptor_units(param, context='html') == '' assert _descriptor_units(param, context='gui') == '' @@ -556,7 +560,7 @@ def test_descriptor_units_suppressed_for_beta_aniso_in_report(): structure.cell.length_a = 5.0 structure.cell.length_b = 6.0 structure.cell.length_c = 8.0 - structure.atom_sites.create(label='Fe', type_symbol='Fe', adp_iso=0.0) + structure.atom_sites.create(id='Fe', type_symbol='Fe', adp_iso=0.0) structure.atom_sites['Fe'].adp_type = 'beta' structure._sync_atom_site_aniso() aniso = structure.atom_site_aniso['Fe'] diff --git a/tests/unit/easydiffraction/report/test_data_context_coverage.py b/tests/unit/easydiffraction/report/test_data_context_coverage.py index fad7be1b2..d8a4e2bd0 100644 --- a/tests/unit/easydiffraction/report/test_data_context_coverage.py +++ b/tests/unit/easydiffraction/report/test_data_context_coverage.py @@ -17,13 +17,13 @@ def __init__(self, value): def _make_parameter(name, *, display_handler=None, cif_names=None): from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.variable import Parameter - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec return Parameter( name=name, value_spec=AttributeSpec(default=0.0), display_handler=display_handler, - cif_handler=CifHandler(names=cif_names or [f'_{name}']), + tags=TagSpec(edi_names=cif_names or [f'_{name}']), ) @@ -89,7 +89,7 @@ def test_first_cif_name_returns_none_without_handler(): def test_first_cif_name_returns_none_for_empty_names(): from easydiffraction.report.data_context import _first_cif_name - parameter = SimpleNamespace(_cif_handler=SimpleNamespace(names=())) + parameter = SimpleNamespace(_tags=SimpleNamespace(edi_names=())) assert _first_cif_name(parameter) is None @@ -116,24 +116,24 @@ def test_descriptor_is_numeric_classifies_descriptor_types(): from easydiffraction.core.variable import IntegerDescriptor from easydiffraction.core.variable import NumericDescriptor from easydiffraction.core.variable import StringDescriptor - from easydiffraction.io.cif.handler import CifHandler + from easydiffraction.io.cif.handler import TagSpec from easydiffraction.report.data_context import _descriptor_is_numeric - cif_handler = CifHandler(names=['_x.y']) + tags = TagSpec(edi_names=['_x.y']) integer = IntegerDescriptor( name='n', value_spec=AttributeSpec(default=0), - cif_handler=cif_handler, + tags=tags, ) numeric = NumericDescriptor( name='m', value_spec=AttributeSpec(default=0.0), - cif_handler=cif_handler, + tags=tags, ) string = StringDescriptor( name='s', value_spec=AttributeSpec(default=''), - cif_handler=cif_handler, + tags=tags, ) assert _descriptor_is_numeric(integer) is True @@ -424,7 +424,7 @@ def test_fit_data_axes_labels_falls_back_on_unknown_combination(): def _single_crystal_experiment(): return SimpleNamespace( name='heidi', - type=SimpleNamespace( + experiment_type=SimpleNamespace( sample_form=_Descriptor('single crystal'), scattering_type=_Descriptor('bragg'), ), @@ -543,7 +543,7 @@ def test_collection_category_context_truncates_long_loops(): category = LineSegmentBackground() for index in range(_REPORT_LOOP_DISPLAY_LIMIT + 6): - category.create(id=str(index), x=float(index), y=float(index) + 0.5) + category.create(id=str(index), position=float(index), intensity=float(index) + 0.5) context = _collection_category_context(category, truncate=True) @@ -559,8 +559,8 @@ def test_collection_category_context_keeps_short_loops_untruncated(): from easydiffraction.report.data_context import _collection_category_context category = LineSegmentBackground() - category.create(id='1', x=1.0, y=2.0) - category.create(id='2', x=2.0, y=3.0) + category.create(id='1', position=1.0, intensity=2.0) + category.create(id='2', position=2.0, intensity=3.0) context = _collection_category_context(category, truncate=True) diff --git a/tests/unit/easydiffraction/report/test_html_renderer.py b/tests/unit/easydiffraction/report/test_html_renderer.py index 296a5bfc4..bd4738e07 100644 --- a/tests/unit/easydiffraction/report/test_html_renderer.py +++ b/tests/unit/easydiffraction/report/test_html_renderer.py @@ -334,7 +334,7 @@ def test_render_html_report_uses_plotly_fit_style_order(): }, 'bragg_tick_sets': ( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5]), h=np.array([1]), k=np.array([0]), diff --git a/tests/unit/easydiffraction/report/test_html_renderer_coverage.py b/tests/unit/easydiffraction/report/test_html_renderer_coverage.py index 8daababb8..4d29c5b89 100644 --- a/tests/unit/easydiffraction/report/test_html_renderer_coverage.py +++ b/tests/unit/easydiffraction/report/test_html_renderer_coverage.py @@ -36,7 +36,7 @@ def test_html_report_path_builds_from_project_info(tmp_path): project = SimpleNamespace( name='demo', - info=SimpleNamespace(path=str(tmp_path)), + metadata=SimpleNamespace(path=str(tmp_path)), ) result = html_report_path(project) @@ -49,7 +49,7 @@ def test_html_report_path_defaults_project_name(tmp_path): # A project that exposes a saved path but no name falls back to # the 'project' default filename stem. - project = SimpleNamespace(info=SimpleNamespace(path=str(tmp_path))) + project = SimpleNamespace(metadata=SimpleNamespace(path=str(tmp_path))) result = html_report_path(project) @@ -59,7 +59,7 @@ def test_html_report_path_defaults_project_name(tmp_path): def test_html_report_path_raises_without_saved_path(): from easydiffraction.report.html_renderer import html_report_path - project = SimpleNamespace(name='demo', info=SimpleNamespace(path=None)) + project = SimpleNamespace(name='demo', metadata=SimpleNamespace(path=None)) with pytest.raises(FileNotFoundError, match='Save the project first'): html_report_path(project) @@ -155,7 +155,7 @@ def test_save_html_report_builds_path_from_project(tmp_path, monkeypatch): _stub_structure_figures(monkeypatch) project = SimpleNamespace( name='proj', - info=SimpleNamespace(path=str(tmp_path)), + metadata=SimpleNamespace(path=str(tmp_path)), ) result = save_html_report(project, _minimal_context()) diff --git a/tests/unit/easydiffraction/report/test_tex_renderer.py b/tests/unit/easydiffraction/report/test_tex_renderer.py index c9bec96ec..777d47d15 100644 --- a/tests/unit/easydiffraction/report/test_tex_renderer.py +++ b/tests/unit/easydiffraction/report/test_tex_renderer.py @@ -369,7 +369,7 @@ def test_save_tex_report_uses_composite_pgfplots_with_error_bars(tmp_path): }, 'bragg_tick_sets': ( BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5]), h=np.array([1]), k=np.array([0]), @@ -421,7 +421,7 @@ def test_save_tex_report_uses_composite_pgfplots_with_error_bars(tmp_path): '_pd_data.refinement_status' ) assert bragg_csv_header == ( - '_refln.id,_refln.phase_id,_refln.index_h,_refln.index_k,' + '_refln.id,_pd_refln.phase_id,_refln.index_h,_refln.index_k,' '_refln.index_l,_refln.f_calc,_refln.f_squared_calc,_refln.two_theta' ) @@ -444,20 +444,20 @@ def test_save_tex_report_removes_stale_managed_bundle_dirs(tmp_path): def test_save_tex_report_writes_structure_figure_png(tmp_path): - import easydiffraction as ed + import easydiffraction as edi from easydiffraction.report.tex_renderer import save_tex_report - project = ed.Project(name='struct_fig') + project = edi.Project(name='struct_fig') project.structures.create(name='nacl') structure = project.structures['nacl'] structure.cell.length_a = 5.64 structure.cell.length_b = 5.64 structure.cell.length_c = 5.64 structure.atom_sites.create( - label='Na', type_symbol='Na', fract_x=0, fract_y=0, fract_z=0, adp_iso=0.5, occupancy=1 + id='Na', type_symbol='Na', fract_x=0, fract_y=0, fract_z=0, adp_iso=0.5, occupancy=1 ) structure.atom_sites.create( - label='Cl', + id='Cl', type_symbol='Cl', fract_x=0.5, fract_y=0.5, diff --git a/tests/unit/easydiffraction/report/test_tex_renderer_coverage.py b/tests/unit/easydiffraction/report/test_tex_renderer_coverage.py index 131ca6a24..5eef819e5 100644 --- a/tests/unit/easydiffraction/report/test_tex_renderer_coverage.py +++ b/tests/unit/easydiffraction/report/test_tex_renderer_coverage.py @@ -40,8 +40,8 @@ def test_tex_report_path_returns_explicit_path(tmp_path): def test_tex_report_path_builds_from_project_info(tmp_path): from easydiffraction.report.tex_renderer import tex_report_path - info = types.SimpleNamespace(path=str(tmp_path)) - project = types.SimpleNamespace(info=info, name='myproj') + metadata = types.SimpleNamespace(path=str(tmp_path)) + project = types.SimpleNamespace(metadata=metadata, name='myproj') result = tex_report_path(project) @@ -51,8 +51,8 @@ def test_tex_report_path_builds_from_project_info(tmp_path): def test_tex_report_path_uses_default_name_when_missing(tmp_path): from easydiffraction.report.tex_renderer import tex_report_path - info = types.SimpleNamespace(path=str(tmp_path)) - project = types.SimpleNamespace(info=info) + metadata = types.SimpleNamespace(path=str(tmp_path)) + project = types.SimpleNamespace(metadata=metadata) result = tex_report_path(project) @@ -62,7 +62,7 @@ def test_tex_report_path_uses_default_name_when_missing(tmp_path): def test_tex_report_path_raises_when_project_unsaved(): from easydiffraction.report.tex_renderer import tex_report_path - project = types.SimpleNamespace(info=types.SimpleNamespace(path=None)) + project = types.SimpleNamespace(metadata=types.SimpleNamespace(path=None)) with pytest.raises(FileNotFoundError, match='Save the project first'): tex_report_path(project) @@ -475,21 +475,21 @@ def _refln_source_experiment(): """Return a live experiment exposing a refln loop category.""" items = [ _FakeItem([ - _FakeParameter('phase_id', 'phase-a'), + _FakeParameter('structure_id', 'phase-a'), _FakeParameter('two_theta', 12.0), _FakeParameter('index_h', 1), _FakeParameter('index_k', 0), _FakeParameter('index_l', 0), ]), _FakeItem([ - _FakeParameter('phase_id', ''), # empty -> skipped + _FakeParameter('structure_id', ''), # empty -> skipped _FakeParameter('two_theta', 13.0), _FakeParameter('index_h', 1), _FakeParameter('index_k', 1), _FakeParameter('index_l', 0), ]), _FakeItem([ - _FakeParameter('phase_id', 'phase-b'), + _FakeParameter('structure_id', 'phase-b'), _FakeParameter('two_theta', 14.0), _FakeParameter('index_h', 2), _FakeParameter('index_k', 0), @@ -517,17 +517,17 @@ def test_write_bragg_csvs_from_refln_category_splits_by_phase(tmp_path): phase_a_path = tmp_path / 'data' / csvs['phase-a']['filename'] rows = _read_csv(phase_a_path) header = rows[0] - assert '_refln.phase_id' in header + assert '_pd_refln.phase_id' in header assert '_refln.two_theta' in header # Only the single phase-a row is written. assert len(rows) == 2 assert rows[1][header.index('_refln.two_theta')] == '12.0' -def test_write_refln_category_csvs_empty_without_phase_id(tmp_path): +def test_write_refln_category_csvs_empty_without_structure_id(tmp_path): from easydiffraction.report.tex_renderer import _write_refln_category_csvs - values = {'two_theta': [10.0, 20.0]} # no phase_id + values = {'two_theta': [10.0, 20.0]} # no structure_id assert _write_refln_category_csvs('hrpt', values, tmp_path) == {} @@ -547,7 +547,7 @@ def test_write_bragg_csvs_falls_back_to_tick_sets(tmp_path): from easydiffraction.report.tex_renderer import _write_bragg_csvs tick_set = BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.5, 2.5]), h=np.array([1, 2]), k=np.array([0, 0]), @@ -584,7 +584,7 @@ def test_bragg_tick_sources_skips_phases_without_csv(): from easydiffraction.report.tex_renderer import _bragg_tick_sources tick_set = BraggTickSet( - phase_id='phase-a', + structure_id='phase-a', x=np.array([1.0]), h=np.array([1]), k=np.array([0]), @@ -603,7 +603,7 @@ def test_bragg_tick_sources_skips_phases_without_csv(): ) assert sources == [ { - 'phase_id': 'phase-a', + 'structure_id': 'phase-a', 'csv_filename': 'f.csv', 'x_column': '_refln.two_theta', } diff --git a/tests/unit/easydiffraction/test___init__.py b/tests/unit/easydiffraction/test___init__.py index 396840e43..933840ebf 100644 --- a/tests/unit/easydiffraction/test___init__.py +++ b/tests/unit/easydiffraction/test___init__.py @@ -8,43 +8,43 @@ def test_lazy_attributes_resolve_and_are_accessible(): - import easydiffraction as ed + import easydiffraction as edi # Access a few lazy attributes; just ensure they exist and are callable/class-like - assert hasattr(ed, 'Project') - assert hasattr(ed, 'ExperimentFactory') - assert hasattr(ed, 'StructureFactory') + assert hasattr(edi, 'Project') + assert hasattr(edi, 'ExperimentFactory') + assert hasattr(edi, 'StructureFactory') # Access utility functions from utils via lazy getattr - assert callable(ed.show_version) - assert callable(ed.extract_metadata) + assert callable(edi.show_version) + assert callable(edi.extract_metadata) # Import once to exercise __getattr__; subsequent access should be cached by Python - _ = ed.Project - _ = ed.ExperimentFactory + _ = edi.Project + _ = edi.ExperimentFactory def test___getattr__unknown_raises_attribute_error(): - ed = importlib.import_module('easydiffraction') + edi = importlib.import_module('easydiffraction') with pytest.raises(AttributeError): - ed.DefinitelyUnknownAttribute + edi.DefinitelyUnknownAttribute def test_lazy_functions_execute_with_monkeypatch(monkeypatch, capsys, tmp_path): - import easydiffraction as ed + import easydiffraction as edi from easydiffraction.utils import utils # 1) list_tutorials uses _fetch_tutorials_index → monkeypatch there fake_tutorial_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', 'description': 'A quick start tutorial', }, } monkeypatch.setattr(utils, '_fetch_tutorials_index', lambda: fake_tutorial_index) monkeypatch.setattr(utils, '_get_version_for_url', lambda: '0.8.0') - ed.list_tutorials() # calls into utils.list_tutorials + edi.list_tutorials() # calls into utils.list_tutorials out = capsys.readouterr().out assert 'Tutorials available for easydiffraction' in out @@ -52,8 +52,8 @@ def test_lazy_functions_execute_with_monkeypatch(monkeypatch, capsys, tmp_path): from easydiffraction.utils import utils fake_index = { - '12': { - 'path': 'data.xye', + 'meas-lbco-hrpt': { + 'path': 'meas-lbco-hrpt.xye', 'hash': 'sha256:...', 'description': 'Demo dataset', } @@ -70,6 +70,6 @@ def fake_retrieve(**kwargs): monkeypatch.setattr(utils.pooch, 'retrieve', fake_retrieve) - result = utils.download_data(id=12, destination=str(tmp_path), overwrite=True) + result = utils.download_data('meas-lbco-hrpt', destination=str(tmp_path), overwrite=True) assert Path(result).exists() - assert calls['kwargs']['url'] == utils._build_data_url('data.xye') + assert calls['kwargs']['url'] == utils._build_data_url('meas-lbco-hrpt.xye') diff --git a/tests/unit/easydiffraction/test___main__.py b/tests/unit/easydiffraction/test___main__.py index f02a47317..f9bff0bea 100644 --- a/tests/unit/easydiffraction/test___main__.py +++ b/tests/unit/easydiffraction/test___main__.py @@ -3,6 +3,8 @@ from typer.testing import CliRunner +import easydiffraction as edi + runner = CliRunner() @@ -15,7 +17,6 @@ def test_module_import(): def test_cli_version_invokes_show_version(monkeypatch, capsys): - import easydiffraction as ed import easydiffraction.__main__ as main_mod called = {'ok': False} @@ -24,7 +25,7 @@ def fake_show_version(): print('VERSION_OK') called['ok'] = True - monkeypatch.setattr(ed, 'show_version', fake_show_version) + monkeypatch.setattr(edi, 'show_version', fake_show_version) result = runner.invoke(main_mod.app, ['--version']) assert result.exit_code == 0 assert called['ok'] @@ -40,42 +41,82 @@ def test_cli_help_shows_and_exits_zero(): def test_cli_subcommands_call_utils(monkeypatch): - import easydiffraction as ed import easydiffraction.__main__ as main_mod logs = [] - monkeypatch.setattr(ed, 'list_data', lambda: logs.append('LIST_DATA')) + monkeypatch.setattr(edi, 'list_data', lambda: logs.append('LIST_DATA')) monkeypatch.setattr( - ed, + edi, 'download_data', - lambda id, destination='data', overwrite=False: logs.append( - f'DATA_{id}_{destination}_{overwrite}' + lambda name, destination='data', overwrite=False: logs.append( + f'DATA_{name}_{destination}_{overwrite}' ), ) - monkeypatch.setattr(ed, 'list_tutorials', lambda: logs.append('LIST')) + monkeypatch.setattr(edi, 'list_tutorials', lambda: logs.append('LIST')) monkeypatch.setattr( - ed, + edi, 'download_all_tutorials', lambda destination='tutorials', overwrite=False: logs.append('DOWNLOAD_ALL'), ) monkeypatch.setattr( - ed, + edi, 'download_tutorial', - lambda id, destination='tutorials', overwrite=False: logs.append(f'DOWNLOAD_{id}'), + lambda name, destination='tutorials', file_format='ipynb', overwrite=False: logs.append( + f'DOWNLOAD_{name}_{file_format}' + ), ) res0 = runner.invoke(main_mod.app, ['list-data']) - res1 = runner.invoke(main_mod.app, ['download-data', '30', '--destination', 'projects']) + res1 = runner.invoke( + main_mod.app, + ['download-data', 'proj-lbco-hrpt', '--destination', 'projects'], + ) res2 = runner.invoke(main_mod.app, ['list-tutorials']) res3 = runner.invoke(main_mod.app, ['download-all-tutorials']) - res4 = runner.invoke(main_mod.app, ['download-tutorial', '1']) + res4 = runner.invoke(main_mod.app, ['download-tutorial', 'refine-lbco-hrpt-from-cif']) assert res0.exit_code == 0 assert res1.exit_code == 0 assert res2.exit_code == 0 assert res3.exit_code == 0 assert res4.exit_code == 0 - assert logs == ['LIST_DATA', 'DATA_30_projects_False', 'LIST', 'DOWNLOAD_ALL', 'DOWNLOAD_1'] + assert logs == [ + 'LIST_DATA', + 'DATA_proj-lbco-hrpt_projects_False', + 'LIST', + 'DOWNLOAD_ALL', + 'DOWNLOAD_refine-lbco-hrpt-from-cif_ipynb', + ] + + +def test_cli_download_tutorial_format_flags(monkeypatch): + import easydiffraction as edi + import easydiffraction.__main__ as main_mod + + calls = [] + monkeypatch.setattr( + edi, + 'download_tutorial', + lambda name, destination='tutorials', file_format='ipynb', overwrite=False: calls.append( + file_format + ), + ) + + # Default (no flag) -> notebook only. + calls.clear() + assert runner.invoke(main_mod.app, ['download-tutorial', '3']).exit_code == 0 + assert calls == ['ipynb'] + + # --py alone -> script only. + calls.clear() + assert runner.invoke(main_mod.app, ['download-tutorial', '3', '--py']).exit_code == 0 + assert calls == ['py'] + + # Both flags -> notebook and script. + calls.clear() + res = runner.invoke(main_mod.app, ['download-tutorial', '3', '--ipynb', '--py']) + assert res.exit_code == 0 + assert calls == ['ipynb', 'py'] def test_cli_removed_report_commands_are_unknown(tmp_path): @@ -161,7 +202,7 @@ def pattern(expt_name, **kwargs): # Create a minimal project directory so load doesn't fail on path check proj_dir = tmp_path / 'proj' proj_dir.mkdir() - (proj_dir / 'project.cif').write_text('_project.id test\n') + (proj_dir / 'project.edi').write_text('_project.id test\n') monkeypatch.setattr(Project, 'load', staticmethod(lambda dir_path: fake_project)) @@ -221,7 +262,7 @@ def pattern(expt_name, **kwargs): proj_dir = tmp_path / 'proj' proj_dir.mkdir() - (proj_dir / 'project.cif').write_text('_project.id test\n') + (proj_dir / 'project.edi').write_text('_project.id test\n') monkeypatch.setattr(Project, 'load', staticmethod(lambda dir_path: fake_project)) @@ -241,7 +282,7 @@ class FakeExperiment: name = 'exp1' class FakeProject: - info = FakeInfo() + metadata = FakeInfo() experiments = [FakeExperiment()] class _analysis: @@ -278,13 +319,13 @@ def pattern(expt_name, **kwargs): proj_dir = tmp_path / 'proj' proj_dir.mkdir() - (proj_dir / 'project.cif').write_text('_project.id test\n') + (proj_dir / 'project.edi').write_text('_project.id test\n') monkeypatch.setattr(Project, 'load', staticmethod(lambda dir_path: fake_project)) result = runner.invoke(main_mod.app, ['fit', '--dry', str(proj_dir)]) assert result.exit_code == 0 - assert fake_project.info._path is None + assert fake_project.metadata._path is None def test_cli_undo_noop_exits_zero_and_does_not_save(monkeypatch, tmp_path): diff --git a/tests/unit/easydiffraction/utils/test_utils.py b/tests/unit/easydiffraction/utils/test_utils.py index 00f903b58..fc2809ade 100644 --- a/tests/unit/easydiffraction/utils/test_utils.py +++ b/tests/unit/easydiffraction/utils/test_utils.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors <https://github.com/easyscience> # SPDX-License-Identifier: BSD-3-Clause +import os + import numpy as np import pytest @@ -225,6 +227,80 @@ def test_get_version_for_url_released(monkeypatch): assert MUT._get_version_for_url() == '0.8.0.post1' +def test_parameter_docs_url_uses_grouped_docs_route(monkeypatch): + import easydiffraction.utils.utils as MUT + + monkeypatch.setattr(MUT, 'package_version', lambda name: '0.8.0.post1') + + url = MUT.parameter_docs_url('_cell.length_a') + + assert url == ( + 'https://easyscience.github.io/diffraction-lib/0.8.0.post1/' + 'user-guide/parameters/structure/cell/#cell-length-a' + ) + + +def test_parameter_docs_url_maps_category_page_alias(monkeypatch): + import easydiffraction.utils.utils as MUT + + monkeypatch.setattr(MUT, 'package_version', lambda name: '0.8.0.post1') + + url = MUT.parameter_docs_url('_excluded_regions.start') + + assert url == ( + 'https://easyscience.github.io/diffraction-lib/0.8.0.post1/' + 'user-guide/parameters/experiment/excluded_region/#excluded-region-start' + ) + + +def test_parameter_docs_url_maps_data_range_items(monkeypatch): + import easydiffraction.utils.utils as MUT + + monkeypatch.setattr(MUT, 'package_version', lambda name: '0.8.0.post1') + + twotheta_url = MUT.parameter_docs_url('_data_range.two_theta_min') + sthovl_url = MUT.parameter_docs_url('_data_range.sin_theta_over_lambda_max') + + assert twotheta_url == ( + 'https://easyscience.github.io/diffraction-lib/0.8.0.post1/' + 'user-guide/parameters/experiment/pd_meas/#pd-meas-2theta-range-min' + ) + assert sthovl_url == ( + 'https://easyscience.github.io/diffraction-lib/0.8.0.post1/' + 'user-guide/parameters/experiment/refln/#refln-sin-theta-over-lambda-range-max' + ) + + +def test_parameter_docs_blocks_match_docs_tree(): + """Guard the owner-grouped route map against docs-tree drift.""" + import pathlib + + import easydiffraction.utils.utils as MUT + + repo_root = pathlib.Path(__file__).resolve().parents[4] + params_dir = repo_root / 'docs' / 'docs' / 'user-guide' / 'parameters' + + docs_pages = { + (owner_dir.name, page.stem) + for owner_dir in params_dir.iterdir() + if owner_dir.is_dir() + for page in owner_dir.glob('*.md') + } + mapped_pages = { + (block, category) + for block, categories in MUT._PARAMETER_DOCS_BLOCKS.items() + for category in categories + } + + # Every grouped route must resolve to an existing reference page and + # vice versa, so runtime parameter URLs cannot drift from the docs. + assert mapped_pages == docs_pages + + # Each mapped category resolves to its own owner/category route. + for block, category in mapped_pages: + assert MUT._parameter_docs_page(category) == f'{block}/{category}' + + @pytest.mark.filterwarnings('ignore:Failed to fetch tutorials index:UserWarning') def test_fetch_tutorials_index_returns_empty_on_error(monkeypatch): import easydiffraction.utils.utils as MUT @@ -256,19 +332,19 @@ def test_list_tutorials_with_data(monkeypatch, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', 'description': 'A quick start tutorial', }, - '2': { - 'url': 'https://example.com/{version}/tutorials/ed-2/ed-2.ipynb', + 'advanced': { + 'url': 'https://example.com/{version}/tutorials/advanced.ipynb', 'title': 'Advanced', 'description': 'An advanced tutorial', }, } monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: fake_index) - monkeypatch.setattr(MUT, '_get_version_for_url', lambda: '0.8.0') + monkeypatch.setattr(MUT, 'package_version', lambda name: '0.8.0') MUT.list_tutorials() out = capsys.readouterr().out assert 'Tutorials available for easydiffraction v0.8.0' in out @@ -276,20 +352,37 @@ def test_list_tutorials_with_data(monkeypatch, capsys): assert 'Advanced' in out +@pytest.mark.parametrize( + ('terminal_columns', 'expected_width'), + [(200, 100), (72, 72)], +) +def test_list_table_width_caps_at_max(monkeypatch, terminal_columns, expected_width): + import easydiffraction.utils.utils as MUT + + # Accept arbitrary args so pytest's own ``get_terminal_size(fallback=...)`` + # keeps working while this patch is active. + monkeypatch.setattr( + MUT.shutil, + 'get_terminal_size', + lambda *args, **kwargs: os.terminal_size((terminal_columns, 24)), + ) + assert MUT._list_table_width() == expected_width + + def test_download_tutorial_unknown_id(monkeypatch): import easydiffraction.utils.utils as MUT - monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: {'1': {}}) - with pytest.raises(KeyError, match='Unknown tutorial id=99'): - MUT.download_tutorial(id=99) + monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: {'quick-start': {}}) + with pytest.raises(KeyError, match="Unknown tutorial 'missing-tutorial'"): + MUT.download_tutorial('missing-tutorial') def test_download_tutorial_success(monkeypatch, tmp_path): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', 'description': 'A quick start tutorial', }, @@ -309,17 +402,63 @@ def __exit__(self, *args): monkeypatch.setattr(MUT, '_safe_urlopen', lambda url: DummyResp()) - result = MUT.download_tutorial(id=1, destination=str(tmp_path)) - assert result == str(tmp_path / 'ed-1.ipynb') - assert (tmp_path / 'ed-1.ipynb').exists() + result = MUT.download_tutorial('quick-start', destination=str(tmp_path)) + assert result == str(tmp_path / 'quick-start.ipynb') + assert (tmp_path / 'quick-start.ipynb').exists() + + +def test_download_tutorial_py_format_swaps_extension(monkeypatch, tmp_path): + import easydiffraction.utils.utils as MUT + + fake_index = { + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start/quick-start.ipynb', + 'title': 'Quick Start', + }, + } + monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: fake_index) + monkeypatch.setattr(MUT, '_get_version_for_url', lambda: '0.8.0') + + requested_urls = [] + + class DummyResp: + def read(self): + return b'# quick-start script' + + def __enter__(self): + return self + + def __exit__(self, *args): + return False + + def fake_urlopen(url): + requested_urls.append(url) + return DummyResp() + + monkeypatch.setattr(MUT, '_safe_urlopen', fake_urlopen) + + result = MUT.download_tutorial('quick-start', destination=str(tmp_path), file_format='py') + assert result == str(tmp_path / 'quick-start.py') + assert (tmp_path / 'quick-start.py').exists() + # The notebook lives nested (tutorials/<name>/<name>.ipynb) but the + # .py source is published flat at tutorials/<name>.py. + assert requested_urls == ['https://example.com/0.8.0/tutorials/quick-start.py'] + + +def test_download_tutorial_unknown_format(monkeypatch): + import easydiffraction.utils.utils as MUT + + monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: {'quick-start': {}}) + with pytest.raises(ValueError, match="Unknown tutorial format 'txt'"): + MUT.download_tutorial('quick-start', file_format='txt') def test_download_tutorial_uses_artifact_root(monkeypatch, tmp_path): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', }, } @@ -340,9 +479,9 @@ def __exit__(self, *args): monkeypatch.setattr(MUT, '_safe_urlopen', lambda url: DummyResp()) - result = MUT.download_tutorial(id=1, destination='tutorials') + result = MUT.download_tutorial('quick-start', destination='tutorials') - expected_path = artifact_root / 'tutorials' / 'ed-1.ipynb' + expected_path = artifact_root / 'tutorials' / 'quick-start.ipynb' assert result == str(expected_path) assert expected_path.exists() @@ -351,8 +490,8 @@ def test_download_tutorial_already_exists_no_overwrite(monkeypatch, tmp_path, ca import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', }, } @@ -360,14 +499,14 @@ def test_download_tutorial_already_exists_no_overwrite(monkeypatch, tmp_path, ca monkeypatch.setattr(MUT, '_get_version_for_url', lambda: '0.8.0') # Create existing file - (tmp_path / 'ed-1.ipynb').write_text('existing content') + (tmp_path / 'quick-start.ipynb').write_text('existing content') - result = MUT.download_tutorial(id=1, destination=str(tmp_path), overwrite=False) - assert result == str(tmp_path / 'ed-1.ipynb') + result = MUT.download_tutorial('quick-start', destination=str(tmp_path), overwrite=False) + assert result == str(tmp_path / 'quick-start.ipynb') out = capsys.readouterr().out assert 'already present' in out # Content should not be changed - assert (tmp_path / 'ed-1.ipynb').read_text() == 'existing content' + assert (tmp_path / 'quick-start.ipynb').read_text() == 'existing content' def test_show_version_prints(capsys, monkeypatch): @@ -393,12 +532,12 @@ def test_download_all_tutorials_success(monkeypatch, tmp_path, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', }, - '2': { - 'url': 'https://example.com/{version}/tutorials/ed-2/ed-2.ipynb', + 'advanced': { + 'url': 'https://example.com/{version}/tutorials/advanced.ipynb', 'title': 'Advanced', }, } @@ -419,8 +558,8 @@ def __exit__(self, *args): result = MUT.download_all_tutorials(destination=str(tmp_path)) assert len(result) == 2 - assert (tmp_path / 'ed-1.ipynb').exists() - assert (tmp_path / 'ed-2.ipynb').exists() + assert (tmp_path / 'quick-start.ipynb').exists() + assert (tmp_path / 'advanced.ipynb').exists() def test_download_all_tutorials_reports_resolved_artifact_root( @@ -431,8 +570,8 @@ def test_download_all_tutorials_reports_resolved_artifact_root( import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', }, } @@ -456,8 +595,8 @@ def __exit__(self, *args): result = MUT.download_all_tutorials(destination='tutorials') expected_dir = artifact_root / 'tutorials' - assert result == [str(expected_dir / 'ed-1.ipynb')] - assert (expected_dir / 'ed-1.ipynb').exists() + assert result == [str(expected_dir / 'quick-start.ipynb')] + assert (expected_dir / 'quick-start.ipynb').exists() out = capsys.readouterr().out assert 'Downloaded 1 tutorials' in out normalized_out = out.replace('\\', '/').replace('\n', '') diff --git a/tests/unit/easydiffraction/utils/test_utils_coverage.py b/tests/unit/easydiffraction/utils/test_utils_coverage.py index dfd12aa19..e18671532 100644 --- a/tests/unit/easydiffraction/utils/test_utils_coverage.py +++ b/tests/unit/easydiffraction/utils/test_utils_coverage.py @@ -25,28 +25,28 @@ def test_validate_url_accepts_https(): MUT._validate_url('https://example.com/file.cif') -# --- _filename_for_id_from_path ----------------------------------------------- +# --- _local_filename ---------------------------------------------------------- -def test_filename_for_id_from_path_with_extension(): +def test_local_filename_with_extension(): import easydiffraction.utils.utils as MUT - result = MUT._filename_for_id_from_path(12, 'file.xye') - assert result == 'ed-12.xye' + result = MUT._local_filename('meas-lbco-hrpt', 'measured/lbco-hrpt.xye') + assert result == 'meas-lbco-hrpt.xye' -def test_filename_for_id_from_path_cif_extension(): +def test_local_filename_cif_extension(): import easydiffraction.utils.utils as MUT - result = MUT._filename_for_id_from_path('3', 'path/model.cif') - assert result == 'ed-3.cif' + result = MUT._local_filename('struct-lbco', 'structures/lbco.cif') + assert result == 'struct-lbco.cif' -def test_filename_for_id_from_path_no_extension(): +def test_local_filename_no_extension(): import easydiffraction.utils.utils as MUT - result = MUT._filename_for_id_from_path(7, 'path/noext') - assert result == 'ed-7' + result = MUT._local_filename('proj-x', 'projects/x') + assert result == 'proj-x' def test_record_path_raises_for_missing_path_key(): @@ -333,40 +333,40 @@ def test_tof_to_d_linear_negative_tof_minus_offset_gives_nan(): def test_download_data_unknown_id(monkeypatch): import easydiffraction.utils.utils as MUT - fake_index = {'1': {'path': 'data.xye', 'hash': None}} + fake_index = {'struct-lbco': {'path': 'struct-lbco.cif', 'hash': None}} monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) - with pytest.raises(KeyError, match='Unknown dataset id=999'): - MUT.download_data(id=999) + with pytest.raises(KeyError, match="Unknown dataset 'struct-missing'"): + MUT.download_data('struct-missing') def test_download_data_already_exists_no_overwrite(monkeypatch, tmp_path, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'path': 'data.xye', + 'meas-lbco-hrpt': { + 'path': 'meas-lbco-hrpt.xye', 'hash': None, 'description': 'Test data', } } monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) - # Create existing file - (tmp_path / 'ed-1.xye').write_text('existing data') + # Create existing file (named after the dataset id, not the slug). + (tmp_path / 'meas-lbco-hrpt.xye').write_text('existing data') - result = MUT.download_data(id=1, destination=str(tmp_path), overwrite=False) - assert result == str(tmp_path / 'ed-1.xye') + result = MUT.download_data('meas-lbco-hrpt', destination=str(tmp_path), overwrite=False) + assert result == str(tmp_path / 'meas-lbco-hrpt.xye') out = capsys.readouterr().out assert 'already present' in out - assert (tmp_path / 'ed-1.xye').read_text() == 'existing data' + assert (tmp_path / 'meas-lbco-hrpt.xye').read_text() == 'existing data' def test_download_data_success(monkeypatch, tmp_path, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'path': 'data.xye', + 'meas-lbco-hrpt': { + 'path': 'meas-lbco-hrpt.xye', 'hash': None, 'description': 'Test data', } @@ -382,9 +382,9 @@ def fake_retrieve(url, known_hash, fname, path): monkeypatch.setattr(MUT.pooch, 'retrieve', fake_retrieve) - result = MUT.download_data(id=1, destination=str(tmp_path)) - assert result == str(tmp_path / 'ed-1.xye') - assert (tmp_path / 'ed-1.xye').exists() + result = MUT.download_data('meas-lbco-hrpt', destination=str(tmp_path)) + assert result == str(tmp_path / 'meas-lbco-hrpt.xye') + assert (tmp_path / 'meas-lbco-hrpt.xye').exists() out = capsys.readouterr().out assert 'downloaded' in out @@ -393,16 +393,16 @@ def test_download_data_overwrite_existing(monkeypatch, tmp_path, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'path': 'data.xye', + 'meas-lbco-hrpt': { + 'path': 'meas-lbco-hrpt.xye', 'hash': None, 'description': 'Test data', } } monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) - # Create existing file - (tmp_path / 'ed-1.xye').write_text('old data') + # Create existing file (named after the dataset id). + (tmp_path / 'meas-lbco-hrpt.xye').write_text('old data') def fake_retrieve(url, known_hash, fname, path): import pathlib @@ -412,29 +412,29 @@ def fake_retrieve(url, known_hash, fname, path): monkeypatch.setattr(MUT.pooch, 'retrieve', fake_retrieve) - result = MUT.download_data(id=1, destination=str(tmp_path), overwrite=True) - assert result == str(tmp_path / 'ed-1.xye') - assert (tmp_path / 'ed-1.xye').read_text() == 'new data' + result = MUT.download_data('meas-lbco-hrpt', destination=str(tmp_path), overwrite=True) + assert result == str(tmp_path / 'meas-lbco-hrpt.xye') + assert (tmp_path / 'meas-lbco-hrpt.xye').read_text() == 'new data' def test_download_data_no_description(monkeypatch, tmp_path, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'path': 'data.xye', + 'struct-lbco': { + 'path': 'struct-lbco.cif', 'hash': 'sha256:...', } } monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) # Create existing file so we hit the no-overwrite short-circuit - (tmp_path / 'ed-1.xye').write_text('existing') + (tmp_path / 'struct-lbco.cif').write_text('existing') - result = MUT.download_data(id=1, destination=str(tmp_path)) - assert result == str(tmp_path / 'ed-1.xye') + result = MUT.download_data('struct-lbco', destination=str(tmp_path)) + assert result == str(tmp_path / 'struct-lbco.cif') out = capsys.readouterr().out - assert 'Data #1' in out + assert "Data 'struct-lbco'" in out def test_download_data_uses_tutorial_artifact_root_fallback(monkeypatch, tmp_path): @@ -446,8 +446,8 @@ def test_download_data_uses_tutorial_artifact_root_fallback(monkeypatch, tmp_pat tutorials_dir.mkdir(parents=True) fake_index = { - '1': { - 'path': 'data.xye', + 'meas-lbco-hrpt': { + 'path': 'meas-lbco-hrpt.xye', 'hash': None, 'description': 'Test data', } @@ -466,9 +466,9 @@ def fake_retrieve(url, known_hash, fname, path): monkeypatch.setattr(MUT.pooch, 'retrieve', fake_retrieve) - result = MUT.download_data(id=1, destination='data') + result = MUT.download_data('meas-lbco-hrpt', destination='data') - expected_path = repo_root / 'tmp' / 'tutorials' / 'data' / 'ed-1.xye' + expected_path = repo_root / 'tmp' / 'tutorials' / 'data' / 'meas-lbco-hrpt.xye' assert result == str(expected_path) assert expected_path.exists() @@ -480,8 +480,8 @@ def test_download_tutorial_overwrite(monkeypatch, tmp_path, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', }, } @@ -489,7 +489,7 @@ def test_download_tutorial_overwrite(monkeypatch, tmp_path, capsys): monkeypatch.setattr(MUT, '_get_version_for_url', lambda: '0.8.0') # Create existing file - (tmp_path / 'ed-1.ipynb').write_text('old content') + (tmp_path / 'quick-start.ipynb').write_text('old content') class DummyResp: def read(self): @@ -503,9 +503,9 @@ def __exit__(self, *args): monkeypatch.setattr(MUT, '_safe_urlopen', lambda url: DummyResp()) - result = MUT.download_tutorial(id=1, destination=str(tmp_path), overwrite=True) - assert result == str(tmp_path / 'ed-1.ipynb') - assert 'new' in (tmp_path / 'ed-1.ipynb').read_text() + result = MUT.download_tutorial('quick-start', destination=str(tmp_path), overwrite=True) + assert result == str(tmp_path / 'quick-start.ipynb') + assert 'new' in (tmp_path / 'quick-start.ipynb').read_text() # --- display_path ------------------------------------------------------------- @@ -656,7 +656,7 @@ def test_existing_project_dir_returns_parent(tmp_path): project_dir = tmp_path / 'myproject' project_dir.mkdir() - (project_dir / 'project.cif').write_text('data_block') + (project_dir / 'project.edi').write_text('data_block') result = MUT._existing_project_dir(tmp_path) assert result == project_dir.resolve() @@ -670,16 +670,16 @@ def test_list_data_empty_index(monkeypatch, capsys): monkeypatch.setattr(MUT, '_fetch_data_index', dict) MUT.list_data() out = capsys.readouterr().out - assert 'No example data available' in out + assert 'No datasets available' in out def test_list_data_renders_rows(monkeypatch): import easydiffraction.utils.utils as MUT fake_index = { - '2': {'path': 'sub/two.cif', 'kind': 'project', 'description': 'Second'}, - '10': {'path': 'ten.xye', 'kind': 'pattern', 'description': 'Tenth'}, - '1': {'path': 'one.xye'}, + 'struct-lbco': {'path': 'struct-lbco.cif', 'description': 'Structure'}, + 'meas-lbco-hrpt': {'path': 'meas-lbco-hrpt.xye', 'description': 'Pattern'}, + 'expt-lbco-hrpt': {'path': 'expt-lbco-hrpt.cif'}, } monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) @@ -687,14 +687,22 @@ def test_list_data_renders_rows(monkeypatch): monkeypatch.setattr(MUT, 'render_table', lambda **kwargs: captured.update(kwargs)) MUT.list_data() + # Columns are the dataset name, the file format (bare extension), and + # the description; the renderer adds its own row number, and the + # removed 'kind'/'#' columns are no longer present. + assert captured['columns_headers'] == ['name', 'format', 'description'] + rows = captured['columns_data'] - # Numeric ids sort numerically: 1, 2, 10. - assert [row[0] for row in rows] == ['1', '2', '10'] - # File column uses the basename of the record path. - assert rows[1][1] == 'two.cif' - # Missing kind/description default to empty strings. + # Names sort alphabetically: expt-…, meas-…, struct-…. + assert [row[0] for row in rows] == [ + 'expt-lbco-hrpt', + 'meas-lbco-hrpt', + 'struct-lbco', + ] + # Format column is the record-path extension without the leading dot. + assert rows[2][1] == 'cif' + # Missing description defaults to an empty string. assert rows[0][2] == '' - assert rows[0][3] == '' # --- download_data project-archive branches ----------------------------------- @@ -704,22 +712,22 @@ def test_download_data_project_archive_already_extracted(monkeypatch, tmp_path, import easydiffraction.utils.utils as MUT fake_index = { - '5': { - 'path': 'proj.zip', - 'kind': 'project', + 'proj-lbco-hrpt': { + 'path': 'proj-lbco-hrpt.zip', 'hash': None, 'description': 'Project archive', } } monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) - # Pre-create an extracted project directory matching fname stem 'ed-5'. - extraction_dir = tmp_path / 'ed-5' + # Pre-create an extracted project directory matching the fname stem. + # With no record hash the extraction dir is the bare id stem (no tag). + extraction_dir = tmp_path / 'proj-lbco-hrpt' project_dir = extraction_dir / 'inner' project_dir.mkdir(parents=True) - (project_dir / 'project.cif').write_text('data_block') + (project_dir / 'project.edi').write_text('data_block') - result = MUT.download_data(id=5, destination=str(tmp_path)) + result = MUT.download_data('proj-lbco-hrpt', destination=str(tmp_path)) assert result == str(project_dir.resolve()) out = capsys.readouterr().out assert 'already extracted' in out @@ -729,9 +737,8 @@ def test_download_data_project_archive_zip_present_extracts(monkeypatch, tmp_pat import easydiffraction.utils.utils as MUT fake_index = { - '6': { - 'path': 'proj.zip', - 'kind': 'project', + 'proj-lbco-hrpt': { + 'path': 'proj-lbco-hrpt.zip', 'hash': None, 'description': 'Project archive', } @@ -739,10 +746,10 @@ def test_download_data_project_archive_zip_present_extracts(monkeypatch, tmp_pat monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) # The zip file exists but no extraction dir yet. - zip_path = tmp_path / 'ed-6.zip' + zip_path = tmp_path / 'proj-lbco-hrpt.zip' zip_path.write_text('zip bytes') - extracted = tmp_path / 'ed-6' / 'project' + extracted = tmp_path / 'proj-lbco-hrpt' / 'project' extracted.mkdir(parents=True) def fake_extract(file_path, destination): @@ -751,7 +758,7 @@ def fake_extract(file_path, destination): monkeypatch.setattr(MUT, 'extract_project_from_zip', fake_extract) - result = MUT.download_data(id=6, destination=str(tmp_path)) + result = MUT.download_data('proj-lbco-hrpt', destination=str(tmp_path)) assert result == str(extracted) # The zip is removed after extraction. assert not zip_path.exists() @@ -763,16 +770,15 @@ def test_download_data_project_archive_downloads_and_extracts(monkeypatch, tmp_p import easydiffraction.utils.utils as MUT fake_index = { - '7': { - 'path': 'proj.zip', - 'kind': 'project', + 'proj-lbco-hrpt': { + 'path': 'proj-lbco-hrpt.zip', 'hash': None, 'description': 'Project archive', } } monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) - zip_path = tmp_path / 'ed-7.zip' + zip_path = tmp_path / 'proj-lbco-hrpt.zip' def fake_retrieve(url, known_hash, fname, path): import pathlib @@ -783,7 +789,7 @@ def fake_retrieve(url, known_hash, fname, path): monkeypatch.setattr(MUT.pooch, 'retrieve', fake_retrieve) - extracted = tmp_path / 'ed-7' / 'project' + extracted = tmp_path / 'proj-lbco-hrpt' / 'project' extracted.mkdir(parents=True) def fake_extract(file_path, destination): @@ -791,7 +797,7 @@ def fake_extract(file_path, destination): monkeypatch.setattr(MUT, 'extract_project_from_zip', fake_extract) - result = MUT.download_data(id=7, destination=str(tmp_path)) + result = MUT.download_data('proj-lbco-hrpt', destination=str(tmp_path)) assert result == str(extracted) # Downloaded zip is cleaned up after extraction. assert not zip_path.exists() @@ -802,10 +808,16 @@ def fake_extract(file_path, destination): def test_download_data_overwrite_logs_debug_and_redownloads(monkeypatch, tmp_path): import easydiffraction.utils.utils as MUT - fake_index = {'1': {'path': 'data.xye', 'hash': None, 'description': 'Test data'}} + fake_index = { + 'meas-lbco-hrpt': { + 'path': 'meas-lbco-hrpt.xye', + 'hash': None, + 'description': 'Test data', + } + } monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) - existing = tmp_path / 'ed-1.xye' + existing = tmp_path / 'meas-lbco-hrpt.xye' existing.write_text('old') debug_messages = [] @@ -819,7 +831,7 @@ def fake_retrieve(url, known_hash, fname, path): monkeypatch.setattr(MUT.pooch, 'retrieve', fake_retrieve) - result = MUT.download_data(id=1, destination=str(tmp_path), overwrite=True) + result = MUT.download_data('meas-lbco-hrpt', destination=str(tmp_path), overwrite=True) assert result == str(existing) assert existing.read_text() == 'fresh' # The overwrite path emits a debug log before unlinking. @@ -830,9 +842,8 @@ def test_download_data_project_archive_overwrite_removes_extraction(monkeypatch, import easydiffraction.utils.utils as MUT fake_index = { - '8': { - 'path': 'proj.zip', - 'kind': 'project', + 'proj-lbco-hrpt': { + 'path': 'proj-lbco-hrpt.zip', 'hash': None, 'description': 'Project archive', } @@ -840,7 +851,7 @@ def test_download_data_project_archive_overwrite_removes_extraction(monkeypatch, monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) # Pre-existing extraction dir that should be wiped on overwrite. - extraction_dir = tmp_path / 'ed-8' + extraction_dir = tmp_path / 'proj-lbco-hrpt' stale = extraction_dir / 'stale' stale.mkdir(parents=True) (stale / 'old.cif').write_text('old') @@ -853,7 +864,7 @@ def fake_retrieve(url, known_hash, fname, path): monkeypatch.setattr(MUT.pooch, 'retrieve', fake_retrieve) - extracted = tmp_path / 'ed-8' / 'project' + extracted = tmp_path / 'proj-lbco-hrpt' / 'project' def fake_extract(file_path, destination): extracted.mkdir(parents=True, exist_ok=True) @@ -861,7 +872,7 @@ def fake_extract(file_path, destination): monkeypatch.setattr(MUT, 'extract_project_from_zip', fake_extract) - result = MUT.download_data(id=8, destination=str(tmp_path), overwrite=True) + result = MUT.download_data('proj-lbco-hrpt', destination=str(tmp_path), overwrite=True) assert result == str(extracted) # The stale extracted content was removed before re-extraction. assert not (extraction_dir / 'stale').exists() @@ -874,13 +885,13 @@ def test_list_tutorials_terminal_markup_branch(monkeypatch): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', 'description': 'A quick start tutorial', }, - '2': { - 'url': 'https://example.com/{version}/tutorials/ed-2/ed-2.ipynb', + 'no-description': { + 'url': 'https://example.com/{version}/tutorials/no-description.ipynb', 'title': 'No Description', }, } @@ -893,21 +904,27 @@ def test_list_tutorials_terminal_markup_branch(monkeypatch): monkeypatch.setattr(MUT, 'render_table', lambda **kwargs: captured.update(kwargs)) MUT.list_tutorials() - rows = captured['columns_data'] - # Row with a description carries the dimmed second line. - assert '[dim]' in rows[0][2] - assert 'Quick Start' in rows[0][2] - # Row without a description has only the styled title (no [dim]). - assert '[dim]' not in rows[1][2] - assert 'No Description' in rows[1][2] + # One column; each cell stacks name / title / (dimmed) description. + assert captured['columns_headers'] == ['tutorial'] + cells = [row[0] for row in captured['columns_data']] + quick = next(c for c in cells if c.startswith('quick-start')) + nodesc = next(c for c in cells if c.startswith('no-description')) + # First line is the name in default color (no markup). + assert quick.splitlines()[0] == 'quick-start' + assert 'Quick Start' in quick + assert '[dim]' in quick # description rendered dim + # Row without a description has only name + styled title (no [dim]). + assert nodesc.splitlines()[0] == 'no-description' + assert 'No Description' in nodesc + assert '[dim]' not in nodesc def test_list_tutorials_jupyter_plain_title_branch(monkeypatch): import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'quick-start': { + 'url': 'https://example.com/{version}/tutorials/quick-start.ipynb', 'title': 'Quick Start', 'description': 'A quick start tutorial', }, @@ -921,10 +938,13 @@ def test_list_tutorials_jupyter_plain_title_branch(monkeypatch): monkeypatch.setattr(MUT, 'render_table', lambda **kwargs: captured.update(kwargs)) MUT.list_tutorials() - rows = captured['columns_data'] - # Jupyter shows the plain title with no Rich markup. - assert rows[0][2] == 'Quick Start' - assert '[dim]' not in rows[0][2] + # Jupyter cell uses plain lines: name, title, description (no markup). + cell = captured['columns_data'][0][0] + lines = cell.split('\n') + assert lines[0] == 'quick-start' + assert lines[1] == 'Quick Start' + assert lines[2] == 'A quick start tutorial' + assert '[dim]' not in cell # --- download_tutorial title-less message branch ------------------------------ @@ -934,7 +954,7 @@ def test_download_tutorial_no_title_message(monkeypatch, tmp_path, capsys): import easydiffraction.utils.utils as MUT fake_index = { - '1': {'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb'}, + 'quick-start': {'url': 'https://example.com/{version}/tutorials/quick-start.ipynb'}, } monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: fake_index) monkeypatch.setattr(MUT, '_get_version_for_url', lambda: '0.8.0') @@ -951,11 +971,11 @@ def __exit__(self, *args): monkeypatch.setattr(MUT, '_safe_urlopen', lambda url: DummyResp()) - result = MUT.download_tutorial(id=1, destination=str(tmp_path)) - assert result == str(tmp_path / 'ed-1.ipynb') + result = MUT.download_tutorial('quick-start', destination=str(tmp_path)) + assert result == str(tmp_path / 'quick-start.ipynb') out = capsys.readouterr().out - # Without a title the message is just 'Tutorial #1' (no ': <title>'). - assert 'Tutorial #1' in out + # Without a title the message is just "Tutorial 'quick-start'" (no ': <title>'). + assert "Tutorial 'quick-start'" in out # --- download_all_tutorials error handling ------------------------------------ @@ -965,23 +985,23 @@ def test_download_all_tutorials_logs_failure_and_continues(monkeypatch, tmp_path import easydiffraction.utils.utils as MUT fake_index = { - '1': { - 'url': 'https://example.com/{version}/tutorials/ed-1/ed-1.ipynb', + 'good-tutorial': { + 'url': 'https://example.com/{version}/tutorials/good-tutorial.ipynb', 'title': 'Good', }, - '2': { - 'url': 'https://example.com/{version}/tutorials/ed-2/ed-2.ipynb', + 'zzz-bad-tutorial': { + 'url': 'https://example.com/{version}/tutorials/zzz-bad-tutorial.ipynb', 'title': 'Bad', }, } monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: fake_index) monkeypatch.setattr(MUT, '_get_version_for_url', lambda: '0.8.0') - def flaky_download(id, destination, overwrite): - if str(id) == '2': + def flaky_download(name, destination, overwrite): + if name == 'zzz-bad-tutorial': msg = 'boom' raise OSError(msg) - return str(tmp_path / f'ed-{id}.ipynb') + return str(tmp_path / f'{name}.ipynb') monkeypatch.setattr(MUT, 'download_tutorial', flaky_download) @@ -990,8 +1010,8 @@ def flaky_download(id, destination, overwrite): result = MUT.download_all_tutorials(destination=str(tmp_path)) # The failing tutorial is skipped; the good one is returned. - assert result == [str(tmp_path / 'ed-1.ipynb')] - assert any('Failed to download tutorial #2' in m for m in warnings) + assert result == [str(tmp_path / 'good-tutorial.ipynb')] + assert any("Failed to download tutorial 'zzz-bad-tutorial'" in m for m in warnings) # --- build_table_renderable --------------------------------------------------- @@ -1152,3 +1172,178 @@ def record(text): MUT.render_object_help(PropsOnly()) # Only the Properties section renders when there are no public methods. assert headings == ['Properties'] + + +# --- name validation, resolution, and ref pinning ---------------------------- + + +@pytest.mark.parametrize( + 'name', + ['struct-lbco', 'meas-lbco-hrpt', 'proj-lbco-hrpt', 'expt-lbco-hrpt'], +) +def test_validate_dataset_id_accepts_valid_names(name): + import easydiffraction.utils.utils as MUT + + MUT._validate_dataset_id(name) # must not raise + + +@pytest.mark.parametrize( + 'name', + [ + 'struct-LBCO', # uppercase + 'expt-lbco-hrpt.edi', # extension in name + 'nope-lbco', # unknown category prefix + 'lbco', # missing category prefix + 'meas/lbco', # path form with slash + 'meas--lbco', # doubled dash + 'meas-', # empty name after prefix + ], +) +def test_validate_dataset_id_rejects_invalid_names(name): + import easydiffraction.utils.utils as MUT + + with pytest.raises(ValueError, match='Invalid'): + MUT._validate_dataset_id(name) + + +def test_validate_tutorial_id_rejects_slash(): + import easydiffraction.utils.utils as MUT + + MUT._validate_tutorial_id('refine-lbco-hrpt-from-cif') # must not raise + with pytest.raises(ValueError, match='Invalid tutorial name'): + MUT._validate_tutorial_id('meas/lbco') + + +def test_is_project_id_only_projects_namespace(): + import easydiffraction.utils.utils as MUT + + assert MUT._is_project_id('proj-lbco-hrpt') is True + assert MUT._is_project_id('meas-lbco-hrpt') is False + + +def test_resolve_positional_one_based_against_listing_order(): + import easydiffraction.utils.utils as MUT + + keys = ['struct-lbco', 'meas-lbco-hrpt', 'proj-lbco-hrpt'] + assert MUT._resolve_positional(1, keys, kind='dataset') == 'struct-lbco' + assert MUT._resolve_positional(3, keys, kind='dataset') == 'proj-lbco-hrpt' + with pytest.raises(IndexError): + MUT._resolve_positional(0, keys, kind='dataset') + with pytest.raises(IndexError): + MUT._resolve_positional(4, keys, kind='dataset') + + +def test_data_index_ref_rejects_malformed(monkeypatch): + import easydiffraction.utils.utils as MUT + + class _Res: + def __init__(self, text): + self._text = text + + def joinpath(self, _name): + return self + + def read_text(self, encoding='utf-8'): + return self._text + + MUT._data_index_ref.cache_clear() + monkeypatch.setattr(MUT.importlib.resources, 'files', lambda _pkg: _Res('not-a-sha')) + with pytest.raises(ValueError, match='Invalid data index ref'): + MUT._data_index_ref() + MUT._data_index_ref.cache_clear() + + +def test_data_index_ref_accepts_full_sha(monkeypatch): + import easydiffraction.utils.utils as MUT + + sha = 'a' * 40 + + class _Res: + def joinpath(self, _name): + return self + + def read_text(self, encoding='utf-8'): + return sha + '\n' + + MUT._data_index_ref.cache_clear() + monkeypatch.setattr(MUT.importlib.resources, 'files', lambda _pkg: _Res()) + assert MUT._data_index_ref() == sha + MUT._data_index_ref.cache_clear() + + +def test_download_data_project_archive_stale_zip_revalidated(monkeypatch, tmp_path): + """A stale local project ZIP is re-downloaded, never extracted as-is.""" + import pathlib + + import easydiffraction.utils.utils as MUT + + fake_index = { + 'proj-lbco-hrpt': { + 'path': 'proj-lbco-hrpt.zip', + 'hash': 'sha256:' + 'a' * 64, # will not match the stale local zip + 'description': 'Project archive', + } + } + monkeypatch.setattr(MUT, '_fetch_data_index', lambda: fake_index) + + # A stale project ZIP from an older pinned commit is already present. + zip_path = tmp_path / 'proj-lbco-hrpt.zip' + zip_path.write_text('stale zip bytes') + + calls = {'retrieve': 0} + + def fake_retrieve(url, known_hash, fname, path): + calls['retrieve'] += 1 + target = pathlib.Path(path, fname) + target.write_text('fresh zip bytes', encoding='utf-8') + return str(target) + + monkeypatch.setattr(MUT.pooch, 'retrieve', fake_retrieve) + + extracted = tmp_path / 'fresh' / 'project' + extracted.mkdir(parents=True) + seen = {} + + def fake_extract(file_path, destination): + seen['bytes'] = pathlib.Path(file_path).read_text(encoding='utf-8') + return extracted + + monkeypatch.setattr(MUT, 'extract_project_from_zip', fake_extract) + + result = MUT.download_data('proj-lbco-hrpt', destination=str(tmp_path)) + assert result == str(extracted) + # The stale ZIP was re-downloaded before extraction, not served as-is. + assert calls['retrieve'] == 1 + assert seen['bytes'] == 'fresh zip bytes' + + +def test_ordered_keys_honors_explicit_order_field(): + """Records with an `order` field sort by it; others stay alphabetical.""" + import easydiffraction.utils.utils as MUT + + tutorials = { + 'zzz-intro': {'order': 1, 'title': 'Intro'}, + 'aaa-advanced': {'order': 2, 'title': 'Advanced'}, + } + # Learning order wins over lexicographic slug order. + assert MUT._ordered_keys(tutorials) == ['zzz-intro', 'aaa-advanced'] + + datasets = { + 'struct-lbco': {'path': 'struct-lbco.cif'}, + 'meas-lbco-hrpt': {'path': 'meas-lbco-hrpt.xye'}, + } + # No order field -> alphabetical by slug. + assert MUT._ordered_keys(datasets) == ['meas-lbco-hrpt', 'struct-lbco'] + + +def test_resolve_tutorial_positional_follows_learning_order(monkeypatch): + """Row number 1 resolves to the first learning-order tutorial.""" + import easydiffraction.utils.utils as MUT + + index = { + 'second': {'order': 2, 'title': 'Second'}, + 'first': {'order': 1, 'title': 'First'}, + } + monkeypatch.setattr(MUT, '_fetch_tutorials_index', lambda: index) + assert MUT._resolve_tutorial_id(1, index) == 'first' + assert MUT._resolve_tutorial_id(2, index) == 'second' diff --git a/tools/clean_tutorial_projects.py b/tools/clean_tutorial_projects.py index 047cc418c..bba2861a3 100644 --- a/tools/clean_tutorial_projects.py +++ b/tools/clean_tutorial_projects.py @@ -3,14 +3,17 @@ """Remove saved tutorial output projects before regenerating them. The tutorials save their projects under ``<artifact-root>/projects/`` -with names matching ``ed_<n>_<name>``. Removing them before the -tutorial tests guarantees the tutorial-output checks see only freshly -written projects, so a stale artifact cannot mask a tutorial that no -longer saves its project. - -Only the ``ed_*`` output directories are removed; downloaded input -projects (e.g. ``ed-36`` from ``download_data``) keep their hyphenated -names and are preserved so they need not be re-downloaded. +with slug-derived names such as ``refine-lbco-hrpt-from-cif``. Removing +them before the tutorial tests guarantees the tutorial-output checks see +only freshly written projects, so a stale artifact cannot mask a +tutorial that no longer saves its project. + +Only the tutorial-saved directories are removed; downloaded project +archives keep their ``proj-`` data-category names (e.g. +``proj-lbco-hrpt`` from ``download_data``) and are preserved so they +need not be re-downloaded. This proj-exclusion mirrors the +``generate_baseline.py`` collection rule, so any stale non-``proj-`` +directory (including old ``ed_*`` artifacts) is cleared. """ from __future__ import annotations @@ -23,15 +26,16 @@ def main() -> None: - """Remove every ``projects/ed_*`` directory under the artifact root.""" + """Remove every non-``proj-`` project directory under the artifact root.""" configured = os.environ.get('EASYDIFFRACTION_ARTIFACT_ROOT') root = Path(configured) if configured else _DEFAULT_ARTIFACT_ROOT projects_dir = root / 'projects' removed = 0 if projects_dir.is_dir(): - for path in sorted(projects_dir.glob('ed_*')): - if path.is_dir(): + for path in sorted(projects_dir.iterdir()): + # Keep downloaded ``proj-`` archives; remove tutorial-saved ones. + if path.is_dir() and not path.name.startswith('proj-'): shutil.rmtree(path) removed += 1 diff --git a/tools/edi_handler_inventory.py b/tools/edi_handler_inventory.py new file mode 100644 index 000000000..200375c61 --- /dev/null +++ b/tools/edi_handler_inventory.py @@ -0,0 +1,376 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors <https://github.com/easyscience> +# SPDX-License-Identifier: BSD-3-Clause +"""Generate an Edi persistence-handler inventory.""" + +from __future__ import annotations + +import argparse +import inspect +import json +import sys +from dataclasses import asdict +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +REPO_ROOT = Path(__file__).resolve().parents[1] +SRC_ROOT = REPO_ROOT / 'src' +DEFAULT_OUTPUT = ( + REPO_ROOT + / 'docs' + / 'dev' + / 'adrs' + / 'accepted' + / 'edstar-project-persistence' + / 'handler-inventory.json' +) + +if str(SRC_ROOT) not in sys.path: + sys.path.insert(0, str(SRC_ROOT)) + + +@dataclass(frozen=True) +class InventoryEntry: + """One descriptor declared with a ``CifHandler``.""" + + context: str + descriptor_path: str + owner_class: str + descriptor_class: str + descriptor_name: str + unique_name: str + category_code: str | None + category_entry_name: str | None + edi_name: str + edi_names: list[str] + cif_names: list[str] + read_names: list[str] + cif_name: str + docs_page: str + docs_anchor: str + + +def _import_registration_modules() -> None: + """Import packages whose ``__init__`` files register classes.""" + import easydiffraction.analysis.categories.aliases # noqa: F401 + import easydiffraction.analysis.categories.constraints # noqa: F401 + import easydiffraction.analysis.categories.fit_parameter_correlations # noqa: F401 + import easydiffraction.analysis.categories.fit_parameters # noqa: F401 + import easydiffraction.analysis.categories.fit_result # noqa: F401 + import easydiffraction.analysis.categories.fitting_mode # noqa: F401 + import easydiffraction.analysis.categories.joint_fit # noqa: F401 + import easydiffraction.analysis.categories.minimizer # noqa: F401 + import easydiffraction.analysis.categories.sequential_fit # noqa: F401 + import easydiffraction.analysis.categories.sequential_fit_extract # noqa: F401 + import easydiffraction.analysis.categories.software # noqa: F401 + import easydiffraction.datablocks.experiment.categories.background # noqa: F401 + import easydiffraction.datablocks.experiment.categories.calculator # noqa: F401 + import easydiffraction.datablocks.experiment.categories.data # noqa: F401 + import easydiffraction.datablocks.experiment.categories.data_range # noqa: F401 + import easydiffraction.datablocks.experiment.categories.diffrn # noqa: F401 + import easydiffraction.datablocks.experiment.categories.excluded_regions # noqa: F401 + import easydiffraction.datablocks.experiment.categories.experiment_type # noqa: F401 + import easydiffraction.datablocks.experiment.categories.extinction # noqa: F401 + import easydiffraction.datablocks.experiment.categories.instrument # noqa: F401 + import easydiffraction.datablocks.experiment.categories.linked_structure # noqa: F401 + import easydiffraction.datablocks.experiment.categories.linked_structures # noqa: F401 + import easydiffraction.datablocks.experiment.categories.peak # noqa: F401 + import easydiffraction.datablocks.experiment.categories.pref_orient # noqa: F401 + import easydiffraction.datablocks.experiment.categories.refln # noqa: F401 + import easydiffraction.datablocks.experiment.item # noqa: F401 + import easydiffraction.datablocks.structure.categories.atom_site_aniso # noqa: F401 + import easydiffraction.datablocks.structure.categories.atom_sites # noqa: F401 + import easydiffraction.datablocks.structure.categories.cell # noqa: F401 + import easydiffraction.datablocks.structure.categories.geom # noqa: F401 + import easydiffraction.datablocks.structure.categories.space_group # noqa: F401 + import easydiffraction.datablocks.structure.categories.space_group_wyckoff # noqa: F401 + import easydiffraction.project.categories.metadata # noqa: F401 + import easydiffraction.project.categories.rendering_plot # noqa: F401 + import easydiffraction.project.categories.rendering_structure # noqa: F401 + import easydiffraction.project.categories.rendering_table # noqa: F401 + import easydiffraction.project.categories.report # noqa: F401 + import easydiffraction.project.categories.structure_style # noqa: F401 + import easydiffraction.project.categories.structure_view # noqa: F401 + import easydiffraction.project.categories.verbosity # noqa: F401 + + +def _representative_roots() -> list[tuple[str, object]]: + """Create representative owners whose context shapes descriptors.""" + from easydiffraction.analysis.analysis import Analysis + from easydiffraction.datablocks.experiment.item.factory import ExperimentFactory + from easydiffraction.datablocks.structure.item.factory import StructureFactory + from easydiffraction.project.project_config import ProjectConfig + + roots: list[tuple[str, object]] = [ + ('project', ProjectConfig()), + ('analysis', Analysis(project=None)), + ('structure', StructureFactory.from_scratch(name='inventory_structure')), + ] + experiment_cases = [ + ( + 'experiment.bragg_pd_cwl', + 'powder', + 'constant wavelength', + 'bragg', + ), + ('experiment.bragg_pd_tof', 'powder', 'time-of-flight', 'bragg'), + ('experiment.total_pd_cwl', 'powder', 'constant wavelength', 'total'), + ('experiment.total_pd_tof', 'powder', 'time-of-flight', 'total'), + ( + 'experiment.bragg_sc_cwl', + 'single crystal', + 'constant wavelength', + 'bragg', + ), + ( + 'experiment.bragg_sc_tof', + 'single crystal', + 'time-of-flight', + 'bragg', + ), + ] + for context, sample_form, beam_mode, scattering_type in experiment_cases: + roots.append( + ( + context, + ExperimentFactory.from_scratch( + name='inventory_experiment', + sample_form=sample_form, + beam_mode=beam_mode, + scattering_type=scattering_type, + ), + ) + ) + return roots + + +def _factory_roots() -> list[tuple[str, object]]: + """Create registered category implementations where practical.""" + from easydiffraction.core.category import CategoryCollection + from easydiffraction.core.category import CategoryItem + from easydiffraction.core.factory import FactoryBase + + roots: list[tuple[str, object]] = [] + for factory in _factory_subclasses(FactoryBase): + for tag, cls in sorted(factory._supported_map().items()): + if not issubclass(cls, (CategoryItem, CategoryCollection)): + continue + instance = _instantiate_registered_class(cls) + if instance is None: + continue + roots.append((f'factory.{factory.__name__}.{tag}', instance)) + return roots + + +def _factory_subclasses(cls: type) -> list[type]: + """Return recursive subclasses sorted by class name.""" + result: list[type] = [] + for subclass in cls.__subclasses__(): + result.append(subclass) + result.extend(_factory_subclasses(subclass)) + return sorted(result, key=lambda item: item.__name__) + + +def _instantiate_registered_class(cls: type) -> object | None: + """Instantiate a registered category class when arguments are known.""" + if inspect.isabstract(cls): + return None + + signature = inspect.signature(cls) + constructor_args: dict[str, object] = {} + for parameter in signature.parameters.values(): + if parameter.default is not inspect.Parameter.empty: + continue + if parameter.kind in { + inspect.Parameter.VAR_POSITIONAL, + inspect.Parameter.VAR_KEYWORD, + }: + continue + if parameter.name == 'type': + constructor_args['type'] = 'cryspy' + continue + if parameter.name == 'name': + constructor_args['name'] = 'inventory' + continue + return None + + return cls(**constructor_args) + + +def collect_inventory() -> list[InventoryEntry]: + """Collect the deterministic handler inventory.""" + _import_registration_modules() + entries: list[InventoryEntry] = [] + for context, root in [*_representative_roots(), *_factory_roots()]: + _walk_object(root, context, context, entries, set()) + return sorted( + _deduplicate_entries(entries), + key=lambda entry: ( + entry.context, + entry.descriptor_path, + entry.edi_name, + entry.cif_name, + ), + ) + + +def _walk_object( + obj: object, + context: str, + path: str, + entries: list[InventoryEntry], + visited: set[int], +) -> None: + """Walk an object graph and collect handler descriptors.""" + from easydiffraction.core.category import CategoryCollection + from easydiffraction.core.guard import GuardedBase + from easydiffraction.core.variable import GenericDescriptorBase + + if id(obj) in visited: + return + visited.add(id(obj)) + + if isinstance(obj, GenericDescriptorBase): + entries.append(_entry_for_descriptor(context, path, obj)) + return + + if isinstance(obj, CategoryCollection): + _walk_collection_item_type(obj, context, path, entries, visited) + + if not isinstance(obj, GuardedBase): + return + + for attr_name, value in sorted(vars(obj).items()): + if attr_name in {'_identity', '_parent'}: + continue + child_path = f'{path}.{_path_segment(attr_name)}' + if isinstance(value, GenericDescriptorBase): + entries.append(_entry_for_descriptor(context, child_path, value)) + continue + if isinstance(value, GuardedBase): + _walk_object(value, context, child_path, entries, visited) + + +def _walk_collection_item_type( + obj: object, + context: str, + path: str, + entries: list[InventoryEntry], + visited: set[int], +) -> None: + """Collect descriptors from a collection's item prototype.""" + item_type = getattr(obj, '_item_type', None) + if item_type is None: + return + try: + item = item_type() + except TypeError: + return + _walk_object(item, context, f'{path}[]', entries, visited) + + +def _path_segment(attr_name: str) -> str: + """Return a stable path segment for a private storage name.""" + if attr_name == '_calculator_category': + return 'calculator' + return attr_name.removeprefix('_') + + +def _entry_for_descriptor( + context: str, + path: str, + descriptor: object, +) -> InventoryEntry: + """Build one inventory entry from a descriptor.""" + handler = descriptor._tags + identity = descriptor._identity + return InventoryEntry( + context=context, + descriptor_path=path, + owner_class=type(getattr(descriptor, '_parent', None)).__name__, + descriptor_class=type(descriptor).__name__, + descriptor_name=descriptor.name, + unique_name=descriptor.unique_name, + category_code=identity.category_code, + category_entry_name=identity.category_entry_name, + edi_name=handler.edi_name, + edi_names=list(handler.edi_names), + cif_names=list(handler.cif_names), + read_names=list(handler.read_names), + cif_name=handler.cif_name, + docs_page=handler.docs_page, + docs_anchor=handler.docs_anchor, + ) + + +def _deduplicate_entries(entries: list[InventoryEntry]) -> list[InventoryEntry]: + """Remove exact duplicate paths produced by overlapping roots.""" + unique_entries: dict[tuple[str, str, str, str], InventoryEntry] = {} + for entry in entries: + key = ( + entry.context, + entry.descriptor_path, + entry.edi_name, + entry.cif_name, + ) + unique_entries[key] = entry + return list(unique_entries.values()) + + +def _inventory_payload(entries: list[InventoryEntry]) -> dict[str, Any]: + """Render the inventory JSON payload.""" + return { + 'schema': 'edi-handler-inventory-v2', + 'generated_by': 'tools/edi_handler_inventory.py', + 'entries': [asdict(entry) for entry in entries], + } + + +def _serialized_payload(entries: list[InventoryEntry]) -> str: + """Return canonical inventory JSON text.""" + return json.dumps(_inventory_payload(entries), indent=2, sort_keys=True) + '\n' + + +def write_inventory(path: Path) -> None: + """Write the handler inventory to *path*.""" + entries = collect_inventory() + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(_serialized_payload(entries), encoding='utf-8') + print(f'Wrote {len(entries)} entries to {path}') + + +def check_inventory(path: Path) -> int: + """Return 0 when *path* matches the generated inventory.""" + expected = _serialized_payload(collect_inventory()) + actual = path.read_text(encoding='utf-8') if path.exists() else '' + if actual == expected: + print(f'{path} is up to date') + return 0 + print(f'{path} is out of date') + return 1 + + +def main() -> int: + """CLI entry point.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--output', + type=Path, + default=DEFAULT_OUTPUT, + help='Inventory JSON path to write or check.', + ) + parser.add_argument( + '--check', + action='store_true', + help='Check instead of rewriting the inventory file.', + ) + args = parser.parse_args() + + if args.check: + return check_inventory(args.output) + write_inventory(args.output) + return 0 + + +if __name__ == '__main__': + raise SystemExit(main())