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
203 changes: 99 additions & 104 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

- Python library for crystallographic diffraction analysis (refining
structural models against experimental data).
- Axes: `sample_form` (powder, single crystal), `beam_mode` (time-of-
flight, constant wavelength), `radiation_probe` (neutron, x-ray),
`scattering_type` (bragg, total).
- Domain axes: `sample_form` (powder, single crystal), `beam_mode`
(time-of-flight, constant wavelength), `radiation_probe` (neutron,
x-ray), `scattering_type` (bragg, total).
- Calculation backends: `cryspy` and `crysfml` (Bragg), `pdffit2` (total
scattering).
- Follow CIF naming conventions; deviate only for clearly better API.
- CIF concepts map to: `DatablockItem` / `DatablockCollection` and
`CategoryItem` / `CategoryCollection` (loops).
- CIF maps to `DatablockItem`/`DatablockCollection` and
`CategoryItem`/`CategoryCollection` (loops). Follow CIF naming;
deviate only for a clearly better API.
- Metadata via frozen dataclasses: `TypeInfo`, `Compatibility`,
`CalculatorSupport`.
- Audience: scientists, often non-programmers. Prioritize
discoverability, clear errors, safe defaults over developer
- Audience is scientists, often non-programmers: prioritize
discoverability, clear errors, and safe defaults over developer
ergonomics.
- Critical-software rigor: every code path tested, edge cases handled
explicitly, no silent failures.
Expand All @@ -24,40 +24,39 @@

- snake_case (functions/vars), PascalCase (classes), UPPER_SNAKE_CASE
(constants).
- `from __future__ import annotations` in every module.
- Type-annotate all public signatures.
- Numpy-style docstrings on all public classes/methods, with Parameters
/ Returns / Raises where applicable. Summary is one line ≤72 chars
- `from __future__ import annotations` in every module. Type-annotate
all public signatures.
- Numpy-style docstrings on all public classes/methods (Parameters /
Returns / Raises where applicable). Summary is one line ≤72 chars
(`max-doc-length`); shorten wording rather than wrap.
- Flat over nested, explicit over clever. No defensive checks for
unlikely edge cases. Composition over deep inheritance.
- Flat over nested, explicit over clever, composition over deep
inheritance. No defensive checks for unlikely edge cases.
- One class per file when substantial; group small related classes.
- No `**kwargs` — use explicit keyword arguments.
- No string-based dispatch (e.g. `getattr(self, f'_{name}')`); write
explicit named methods (`_set_sample_form`, `_set_beam_mode`).
named methods (`_set_sample_form`, `_set_beam_mode`).
- Public attrs are either editable (getter+setter property) or read-only
(getter only). For internal mutation of read-only props, add a private
(getter only). For internal mutation of read-only props, use a private
`_set_<name>` method, not a public setter.
- Lint complexity thresholds in `pyproject.toml` (`max-args`,
`max-branches`, `max-statements`, `max-locals`, `max-nested-blocks`,
…) are guardrails. A violation means refactor (extract helpers,
parameter objects, flatten). Do not raise thresholds, add `# noqa`, or
otherwise silence them. For complex refactors touching many lines or
public API, propose a plan and wait for approval.
parameter objects, flatten) — do not raise thresholds, add `# noqa`,
or otherwise silence them. For complex refactors touching many lines
or public API, propose a plan and wait for approval.

## Architecture

- Eager top-of-module imports by default. Lazy imports only to break
circular deps or keep `core/` free of heavy imports on rarely-called
paths (e.g. `help()`).
- No `pkgutil` / `importlib` auto-discovery, no background threads, no
circular deps or to keep `core/` free of heavy imports on rarely-
called paths (e.g. `help()`).
- No `pkgutil`/`importlib` auto-discovery, no background threads, no
monkey-patching or runtime class mutation.
- No `__all__`; control public API via explicit `__init__.py` imports.
- No redundant `import X as X` aliases — use plain
`from module import X`.
No redundant `import X as X` aliases.
- Concrete classes use `@Factory.register`. Each package's `__init__.py`
must explicitly import every concrete class to trigger registration.
Always add new concrete classes to the corresponding `__init__.py`.
must explicitly import every concrete class to trigger registration
always update it when adding a class.
- Switchable categories (factory-swappable at runtime) follow this fixed
API on the owner (experiment / structure / analysis): `<category>`
(read-only), `<category>_type` (getter+setter),
Expand All @@ -69,17 +68,11 @@
as a child of another category of a different type; cross-reference
via IDs instead.
- Every finite, closed set of values (factory tags, axes, enumerated
descriptors) uses a `(str, Enum)` class; compare against members, not
raw strings.
descriptors) is a `(str, Enum)`; compare against members, not raw
strings.
- Keep `core/` free of domain logic (base classes and utilities only).
- Don't introduce abstractions before a concrete second use case.
- Don't add dependencies without asking.

## Tutorials

- Notebooks in `docs/docs/tutorials/*.ipynb` are generated artifacts.
Edit only the corresponding `*.py`, then run
`pixi run notebook-prepare`.
- Don't introduce abstractions before a concrete second use case. Don't
add dependencies without asking.

## Testing

Expand All @@ -88,98 +81,100 @@
- Unit tests mirror the source tree:
`src/easydiffraction/<pkg>/<mod>.py` →
`tests/unit/easydiffraction/<pkg>/test_<mod>.py`. Verify with
`pixi run test-structure-check`.
- Category packages with only `default.py`/`factory.py` may use one
parent-level `test_<package>.py`.
- Supplementary tests: `test_<mod>_coverage.py`.
`pixi run test-structure-check`. Supplementary tests:
`test_<mod>_coverage.py`. Category packages with only
`default.py`/`factory.py` may use one parent-level
`test_<package>.py`.
- Tests expecting `log.error()` to raise must `monkeypatch` Logger to
RAISE mode (another test may have leaked WARN mode).
- `@typechecked` setters raise `typeguard.TypeCheckError`, not
`TypeError`.
- No test-ordering dependence, no network, no sleeping, no real
calculation engines in unit tests.

## Changes
## Tutorials

- Notebooks in `docs/docs/tutorials/*.ipynb` are generated artifacts.
Edit only the corresponding `*.py`, then run
`pixi run notebook-prepare`.

## Change Discipline

- Before any structural/design change (new categories, factories,
switchable-category wiring, datablocks, CIF serialisation), read
`docs/architecture/architecture.md` and follow documented patterns.
Localised bug fixes or test updates need only this file.
- Project is in beta: no legacy shims, no deprecation warnings — update
tests and tutorials to current API.
- Minimal diffs; don't reformat working code.
tests and tutorials to the current API.
- Minimal diffs; don't reformat working code. Fix only what's asked;
flag adjacent issues as comments. Don't add features or refactor
unless asked. Don't remove TODOs or comments unless the change fully
resolves them.
- Never remove or replace existing functionality without explicit
confirmation. Highlight every removal and wait for approval.
- Fix only what's asked; flag adjacent issues as comments.
- Don't add features or refactor unless asked. Don't remove TODOs or
comments unless the change fully resolves them.
confirmation — highlight every removal and wait for approval.
- When renaming, grep the entire project (code, tests, tutorials, docs).
- Each change is atomic, single-commit-sized. Make one change, suggest
the commit message, then stop and wait for confirmation.
- Each change is atomic and single-commit-sized: make one change,
suggest the commit message, then stop and wait for confirmation.
- When in doubt, ask.

## Workflow

### Planning Workflow
## Commits

- When asked to create a plan, first gather enough repository context to
make the plan concrete. Ask all ambiguous, potentially ambiguous, or
unclear questions in one concise batch, and record unresolved
questions in the plan if the user wants the plan saved before
answering them.
- Save plans as Markdown files in `docs/dev` with the filename pattern
`plan_<feature-name>.md`. The `<feature-name>` part uses lowercase
words separated by dashes, for example
`docs/dev/plan_background-refactor.md`.
- Use the same `<feature-name>` to create the implementation branch,
normally `feature/<feature-name>`. Do not push the branch unless the
user explicitly asks.
- Each plan must include a status checklist with `[ ]` items. Mark each
item as `[x]` as it is completed during implementation.
- Plans for non-trivial work must separate the work into two phases:
- **Phase 1 — Implementation:** the agent works independently through
all implementation steps, updating the plan checklist as it goes. Do
not create tests or run tests in this phase unless the user
explicitly asks. When Phase 1 is complete, stop and ask the user to
review the implementation.
- **Phase 2 — Verification:** after user approval, add/update tests,
run formatting, linting, unit tests, integration tests, and script
or notebook checks requested by the plan.
- Every completed implementation step must end with a local commit.
Stage only the files modified for that step, using explicit paths
where practical. Do not include data files, project files, CIF files,
or other generated artifacts created by integration tests, script
tests, or notebook execution unless the user explicitly asked to
update those artifacts.
- Keep commits atomic, single-purpose, and aligned with plan steps. Use
imperative commit messages, no type prefix, and keep the subject line
at or below 72 characters.
- Suggest a commit message after each change: code block, ≤72 chars,
imperative mood, no type prefix, no `Co-authored-by: Copilot`.
Examples:
- Add ChebyshevPolynomialBackground class
- Implement background_type setter on Experiment
- Standardize switchable-category naming convention
- Stage only the files modified for the step, using explicit paths where
practical. Do not include data, project, CIF, or other generated
artifacts produced by integration/script/notebook tests unless the
user explicitly asked to update them.
- Before each commit, inspect the worktree and avoid staging unrelated
user changes. If unrelated dirty files exist, leave them untouched and
mention them only when relevant.
- The plan should be easy to maintain while working: include concrete
files likely to change, decisions already made, open questions,
verification commands for Phase 2, and a short suggested commit
message or branch name when useful.

### General Workflow
## Workflow

Non-trivial changes use a two-phase workflow:

- **Phase 1 — Implementation.** Code, docs, and architecture updates
only. Do not create or run tests unless the user explicitly asks. When
done, present for review and iterate until approved.
- **Phase 2 — Verification.** Add/update tests, then run `pixi run fix`,
`pixi run check`, `pixi run unit-tests`, `pixi run integration-tests`,
`pixi run script-tests`.

- Two-phase workflow for non-trivial changes:
- **Phase 1 — Implementation:** code, docs, architecture updates. Do
not create new tests or run existing tests. Present for review and
iterate until approved.
- **Phase 2 — Verification:** add/update tests, then run
`pixi run fix`, `pixi run check`, `pixi run unit-tests`,
`pixi run integration-tests`, `pixi run script-tests`.
Notes:

- `pixi run fix` regenerates `docs/architecture/package-structure-*.md`
automatically — never edit those by hand. Don't review auto-fixes;
accept and move on. Then `pixi run check` until clean.
- Open issues / design questions / planned improvements live in
`docs/architecture/issues_open.md` (priority-ordered). On resolution,
move to `docs/architecture/issues_closed.md` and update
`architecture.md` if affected.
- `pixi run fix` regenerates `docs/architecture/package-structure-*.md`
automatically — never edit those by hand. Don't review auto-fixes;
accept and move on. Then `pixi run check` until clean.
- Suggest a commit message (code block, ≤72 chars, imperative mood, no
type prefix) after each change. E.g.:
- Add ChebyshevPolynomialBackground class
- Implement background_type setter on Experiment
- Standardize switchable-category naming convention

### Planning

When asked to create a plan:

- First gather enough repository context to make the plan concrete. Ask
all ambiguous or unclear questions in one concise batch; record
unresolved questions in the plan if the user wants it saved before
answering them.
- Save plans as `docs/dev/plan_<feature-name>.md` (lowercase,
dash-separated, e.g. `plan_background-refactor.md`). Use the same
`<feature-name>` for the implementation branch
(`feature/<feature-name>`). Do not push the branch unless asked.
- Include a status checklist with `[ ]` items; mark `[x]` as completed
during implementation.
- Apply the two-phase workflow (Phase 1 implementation, Phase 2
verification) to non-trivial plans. Stop after Phase 1 and ask the
user to review before starting Phase 2.
- Every completed implementation step ends with a local commit following
the rules in **Commits**. Keep commits atomic, single-purpose, and
aligned with plan steps.
- The plan should be easy to maintain while working: include concrete
files likely to change, decisions already made, open questions,
verification commands for Phase 2, and a short suggested commit
message or branch name when useful.
1 change: 1 addition & 0 deletions docs/architecture/package-structure-full.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
│ ├── 📄 plotting.py
│ │ ├── 🏷️ class PlotterEngineEnum
│ │ ├── 🏷️ class _MeasVsCalcPlotOptions
│ │ ├── 🏷️ class _PowderMeasVsCalcSeries
│ │ ├── 🏷️ class Plotter
│ │ └── 🏷️ class PlotterFactory
│ ├── 📄 tables.py
Expand Down
11 changes: 8 additions & 3 deletions src/easydiffraction/display/plotters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ class PowderMeasVsCalcSpec:
"""
Specification for one composite powder plot.

The plotting facade assembles the measured, calculated, residual,
and Bragg-tick data into this display-specific object before
delegating to a backend.
The plotting facade assembles the measured, background, calculated,
residual, and Bragg-tick data into this display-specific object
before delegating to a backend.
"""

x: np.ndarray
Expand All @@ -59,6 +59,7 @@ class PowderMeasVsCalcSpec:
residual_height_fraction: float
bragg_peaks_height_fraction: float
height: int | None = None
y_bkg: np.ndarray | None = None


class XAxisType(StrEnum):
Expand Down Expand Up @@ -184,6 +185,10 @@ class XAxisType(StrEnum):
'mode': 'lines',
'name': 'Total calculated (Icalc)',
},
'bkg': {
'mode': 'lines',
'name': 'Background (Ibkg)',
},
'meas': {
'mode': 'lines+markers',
'name': 'Measured (Imeas)',
Expand Down
Loading
Loading