Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
52 changes: 34 additions & 18 deletions docs/dev/adr_display-ux.md → docs/dev/ADRs/adr_display-ux.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

## Status

Accepted.
Accepted and implemented.

## Context

The current user-facing display API mixes presentation actions, analysis
reports, and renderer configuration:
The previous user-facing display API mixed presentation actions,
analysis reports, and renderer configuration:

```python
project.display.plotter.plot_meas(expt_name='hrpt')
Expand All @@ -32,7 +32,7 @@ This has several UX problems:
- `plot_meas`, `plot_calc`, and `plot_meas_vs_calc` force users to
choose a plot state that the project can often infer.
- Bayesian and deterministic chart names are not systematic.
- The existing `project.display` category is serialized to CIF, so it
- The previous `project.display` category was serialized to CIF, so it
should not also become a broad transient display facade.

EasyDiffraction is aimed at scientists, often non-programmers, so the
Expand All @@ -55,7 +55,7 @@ project.rendering.show_table_engines()
project.rendering.show_config()
```

Suggested CIF names:
CIF names:

- `_rendering.chart_engine`
- `_rendering.table_engine`
Expand Down Expand Up @@ -87,7 +87,8 @@ project.display.show_pattern_options(expt_name='hrpt')
```

`project.analysis.display` is removed from the primary public API. Its
current responsibilities move to clearer homes:
current responsibilities move to clearer homes, while the implementation
may keep the existing helpers as internal delegation targets:

| Current method | New home |
| ---------------------------- | -------------------------------------------------------------- |
Expand All @@ -100,7 +101,7 @@ current responsibilities move to clearer homes:
| `constraints()` | `project.analysis.constraints.show()` |
| `as_cif()` | `project.analysis.as_cif` and `project.analysis.show_as_cif()` |

`project.analysis` and `project.info` should follow the same CIF display
`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.
Expand All @@ -119,13 +120,17 @@ By default, `pattern()` uses `include='auto'` and displays as much
useful information as the project state supports:

- measured data if present
- calculated data if a model/calculation is available
- background if defined and relevant
- Bragg ticks if phases/reflections are available
- calculated data if linked structure state and calculated intensities
are available
- background if powder Bragg measured and calculated data plus defined
background points are available
- Bragg ticks if powder Bragg measured and calculated data plus
reflection rows are available
- residual if both measured and calculated data are available and the
experiment type supports a residual panel
- excluded regions if available
- uncertainty bands where posterior predictive data exists
- excluded regions if available on the experiment
- uncertainty bands where posterior predictive data exists and the chart
engine supports them

Specific subsets are selected with `include`:

Expand Down Expand Up @@ -156,11 +161,11 @@ Add discovery for supported pattern content:
project.display.show_pattern_options(expt_name='hrpt')
```

The table should show option name, description, availability for the
The table shows option name, description, availability for the
experiment, whether `include='auto'` includes it, and the reason an
option is unavailable.

Initial option names:
Pattern option names:

- `auto`
- `measured`
Expand All @@ -171,9 +176,17 @@ Initial option names:
- `excluded`
- `uncertainty`

`uncertainty` should be implemented immediately where posterior
predictive data exists. It should be unavailable, with a clear reason,
when no posterior predictive data is present.
`uncertainty` is available where posterior predictive data exists for a
supported experiment and the active chart engine can render bands. It is
unavailable, with a clear reason, when no posterior predictive data is
present.

Explicit combinations are validated against the same project state used
by `include='auto'`. `background`, `bragg`, and `residual` require both
measured and calculated data in the same view. `excluded` requires
measured, calculated, or uncertainty content in the same view, and
excluded-region overlays currently require the experiment's default
x-axis.

## Deterministic And Bayesian Consistency

Expand Down Expand Up @@ -223,5 +236,8 @@ they duplicate `pattern(..., include=...)`.
- Constraints remain owned by the analysis constraints category.
- There is no legacy CIF compatibility path for `_display.plotter_type`
or `_display.tabler_type`.
- `project.analysis` and `project.info` need CIF access cleanup for
- `project.analysis` and `project.info` CIF access is standardized for
consistency with structure and experiment objects.
- Pattern option availability is computed from live project state,
linked structures, calculated intensities, and experiment-specific
content instead of placeholder arrays alone.
65 changes: 65 additions & 0 deletions docs/dev/ADRs/adr_help-discoverability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# ADR: Help Method Discoverability

## Status

Accepted and implemented.

## Context

EasyDiffraction is used by scientists who often explore the API in
notebooks. The main object graph already exposes many focused objects:
projects, project metadata, structures, experiments, categories,
parameters, analysis helpers, summaries, and display facades. Users need
a consistent way to discover the next useful operation from any of these
objects without reading source code.

Most model objects inherit `GuardedBase`, `CategoryItem`,
`CategoryCollection`, `DatablockItem`, or `DatablockCollection`, which
already provide `help()` output. Plain facade classes such as display
namespaces and summaries do not inherit those base classes, so they need
the same discovery behavior explicitly.

## Decision

Every primary public object should provide a `help()` method. This
includes:

- parameters and descriptors
- category items and category collections
- datablock items and datablock collections
- project-level objects such as `Project`, `ProjectInfo`, `Analysis`,
`Summary`, and `Rendering`
- display facades such as `project.display`,
`project.display.parameters`, `project.display.fit`,
`project.display.posterior`, and `analysis.display`

`help()` output uses the existing console/table presentation style. It
lists public properties and methods discovered from the class MRO, uses
the first docstring paragraph as the description, and skips private
names. Specialized containers can append domain-specific tables, such as
collection items or datablock categories, after the generic section.

Plain helper and facade classes use `render_object_help()` so their
output stays consistent with `GuardedBase.help()` without forcing those
classes into the guarded object hierarchy.

## Consequences

Users can call `help()` while navigating through the object graph:

```python
project.help()
project.display.help()
project.display.parameters.help()
project.analysis.display.help()
project.summary.help()
project.experiments.help()
project.experiments['hrpt'].help()
project.experiments['hrpt'].background.help()
project.experiments['hrpt'].background['1'].help()
```

New user-facing objects should either inherit an existing help-capable
base class or define `help()` by delegating to `render_object_help()`.
When a class represents a collection or owner, its help output should
guide users to the next object level where practical.
12 changes: 12 additions & 0 deletions docs/dev/issues_closed.md → docs/dev/Issues/issues_closed.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ Issues that have been fully resolved. Kept for historical reference.

---

## 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 `docs/dev/ADRs/adr_help-discoverability.md`.

---

## Restore Minimiser Variant Support

Used thin subclasses (approach A) to restore lmfit algorithm variants.
Expand Down
31 changes: 5 additions & 26 deletions docs/dev/issues_open.md → docs/dev/Issues/issues_open.md
Original file line number Diff line number Diff line change
Expand Up @@ -689,12 +689,13 @@ the archived planning notes left two follow-up questions open:

---

## 40. 🟢 Implement Resetting `.constrained` to `False`
## 40. 🟢 Implement Resetting `.user_constrained` to `False`

**Type:** Feature

`ConstraintsHandler` has a TODO to implement changing the `.constrained`
attribute back to `False` when constraints are removed.
`ConstraintsHandler` has a TODO to implement changing the
`.user_constrained` attribute back to `False` when constraints are
removed.

**TODOs:**

Expand Down Expand Up @@ -1263,27 +1264,6 @@ deviate: e.g. `show_minimizer_types()` instead of

---

## 77. 🟡 Add `help()` to `Project` and Enrich Existing `help()` Methods

**Type:** API discoverability

`help()` exists on `CategoryItem`, `CollectionBase`, `DatablockItem`,
and `Analysis`, but **not on `Project`**. The user's primary entry point
lacks discoverability. Additionally, each `help()` level should guide
the user to the next level:

1. `project.help()` → attributes: info, experiments, structures,
analysis, summary.
2. `project.experiments.help()` → list experiments and how to select.
3. `project.experiments['name'].help()` → list categories.
4. `experiment.peak.help()` → list public attributes.
5. `experiment.background.help()` → list items + array accessors.
6. `experiment.background['id'].help()` → list attributes.

**Depends on:** nothing.

---

## 79. 🟢 Verify Completeness of Analysis CIF Serialisation

**Type:** Correctness
Expand Down Expand Up @@ -1518,7 +1498,7 @@ operation is possible (e.g. in automated pipelines or tests).
| 37 | Rename experiment `.type` property | 🟢 Low | Naming |
| 38 | Fix `@typechecked`/gemmi in factories | 🟡 Med | Bug |
| 39 | Improve `_update_priority` handling | 🟢 Low | Design |
| 40 | Implement resetting `.constrained` to `False` | 🟢 Low | Feature |
| 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 |
Expand Down Expand Up @@ -1555,7 +1535,6 @@ operation is possible (e.g. in automated pipelines or tests).
| 74 | Sync property type hints + custom lint rules | 🟡 Med | Tooling |
| 75 | `show_supported_calculators()` on Analysis | 🟢 Low | API completeness |
| 76 | Consistent `_type` suffix in switchable APIs | 🟡 Med | Naming |
| 77 | Add `help()` to Project + enrich existing | 🟡 Med | Discoverability |
| 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 |
Expand Down
44 changes: 24 additions & 20 deletions docs/dev/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,16 +187,16 @@ execution order within a datablock (e.g. background before data).

### 2.4 DatablockItem and DatablockCollection

| Aspect | `DatablockItem` | `DatablockCollection` |
| ------------------ | ------------------------------------------- | ------------------------------ |
| CIF analogy | A single `data_` block | Collection of data blocks |
| Examples | Structure, BraggPdExperiment | Structures, Experiments |
| Category discovery | Scans `vars(self)` for categories | N/A |
| Update cascade | `_update_categories()` — sorted by priority | N/A |
| Parameters | Aggregated from all categories | Aggregated from all datablocks |
| Fittable params | N/A | Non-constrained `Parameter`s |
| Free params | N/A | Fittable + `free == True` |
| Dirty flag | `_need_categories_update` | N/A |
| Aspect | `DatablockItem` | `DatablockCollection` |
| ------------------ | ------------------------------------------- | -------------------------------------------------------- |
| CIF analogy | A single `data_` block | Collection of data blocks |
| Examples | Structure, BraggPdExperiment | Structures, Experiments |
| Category discovery | Scans `vars(self)` for categories | N/A |
| Update cascade | `_update_categories()` — sorted by priority | N/A |
| Parameters | Aggregated from all categories | Aggregated from all datablocks |
| Fittable params | N/A | `Parameter`s not blocked by user or symmetry constraints |
| Free params | N/A | Fittable + `free == True` |
| Dirty flag | `_need_categories_update` | N/A |

When any `Parameter.value` is set, it propagates
`_need_categories_update = True` up to the owning `DatablockItem`.
Expand All @@ -210,7 +210,7 @@ GuardedBase
└── GenericDescriptorBase # name, value (validated via AttributeSpec), description
├── GenericStringDescriptor # _value_type = DataTypes.STRING
└── GenericNumericDescriptor # _value_type = DataTypes.NUMERIC, + units
└── GenericParameter # + free, uncertainty, fit_min, fit_max, constrained, symmetry_fixed
└── GenericParameter # + free, uncertainty, fit_min, fit_max, user_constrained, symmetry_constrained
```

CIF-bound concrete classes add a `CifHandler` for serialisation:
Expand Down Expand Up @@ -325,14 +325,15 @@ via the `crystallography` module during `_update_categories()`.

Parameters that are fully determined by symmetry (e.g. `lattice_b` in
cubic, `fract_y` of an atom on a 4-fold axis, off-diagonal ADPs forced
to zero by site symmetry) are flagged as `symmetry_fixed = True` on the
`Parameter`. This forces `free = False`; any subsequent attempt to set
`free = True` on such a parameter is ignored with a warning. Flags are
recomputed on every `_update_categories()` so that changing the space
group, Wyckoff letter, or ADP type re-evaluates which parameters are
fixed. Surface helpers `cell_symmetry_fixed_flags(...)` and
`atom_site_symmetry_fixed_flags(...)` in `crystallography` expose the
per-key flags.
to zero by site symmetry) are flagged as `symmetry_constrained = True`
on the `Parameter`. This forces `free = False`; any subsequent attempt
to set `free = True` on such a parameter is ignored with a warning.
Flags are recomputed on every `_update_categories()` so that changing
the space group, Wyckoff letter, or ADP type re-evaluates which
parameters are symmetry constrained. Surface helpers
`cell_symmetry_constrained_flags(...)` and
`atom_site_symmetry_constrained_flags(...)` in `crystallography` expose
the per-key flags.

### 4.2 Atomic Displacement Parameters (ADP)

Expand Down Expand Up @@ -830,7 +831,10 @@ workflow:
object. This is `FitResults` for deterministic fits and
`BayesianFitResults` for Bayesian DREAM runs.
- Parameter tables: `show_all_params()`, `show_fittable_params()`,
`show_free_params()`, `how_to_access_parameters()`
`show_free_params()`, `how_to_access_parameters()` Compact
summary-style parameter displays intentionally hide the large
loop-backed experiment categories `pd_data`, `total_data`, and `refln`
in `all()`, `access()`, and `cif_uids()` so the output stays readable.
- Fitting: `fit()` dispatches single/joint through the callable `fit`
category; `fit_sequential()` handles sequential mode (sets `fit.mode`
to `'sequential'` internally). `fit()` accepts optional `random_seed`
Expand Down
Loading
Loading