Skip to content

Commit 25e84b7

Browse files
Restructure datablocks, unify factory system, and standardize switchable-category API (#128)
* Refactor validation imports and remove unused DataTypes references * Refactor value specification to use 'data_type' instead of 'type_' in AttributeSpec * Refactor content_validator to validator in AttributeSpec * Refactor Cell class constructor to use default parameters and simplify attribute setting * Refactor AtomSite and SpaceGroup initializers to remove parameters and improve clarity * Refactor constructors in multiple classes to remove parameters and use property setters * Refactor add method to accept only keyword arguments for improved clarity * Refactor initialization of descriptors in multiple classes for consistency * Remove value initialization from validation class constructor * Refactor parameter value specification to remove DataTypes dependency * Refactor parameter initialization and property methods in mixin files * Refactor: streamline CIF handler initialization and add public property comments * Refactor ExperimentType initialization to use property setters * Refactor ExperimentType initialization to use property setters * Add temporary test script * Initialize parent class in guard.py constructor * Refactor peak-profile mixins to remove unused broadening and asymmetry methods * Refactor broadening parameter comments and remove unused initializations in TofPeak * Refactor parameter definitions to streamline unit assignments in various modules * Refactor CIF handler initialization for clarity and consistency * Refactor constructors in PdDataPoint mixins to remove kwargs * Refactor parameters module to variable module; update imports across multiple files * Refactor import statements for consistency and clarity * Refactor sample models to structures in project and related files * Refactor terminology from "sample" to "structure" in documentation and code comments * More refactoring from sample models to structures * Refactor factory methods, etc. * Refactor method names from `add_from_scratch` to `create` for consistency across collections * Rebuild classes structure * Refactor validation module by removing unused type checking decorator * Refactor PeakProfileTypeEnum to consider auto-extraction of peak profile info * Add revised design for all factories * Improve revised design for all factories * Add copilot instructions for EasyDiffraction project * Update all init files * Refactor metadata handling * Refactor type hints for optional parameters in collection.py * Add per-file ignores for __init__.py in Ruff configuration * Add TOF instrument and peak profile classes with compatibility and calculator support * Clarify usage of keyword arguments in copilot instructions * Add metadata dataclasses for factory-created classes * Refactor setter methods for improved readability and consistency * Refactor formatting of beam_mode and calculators in Chebyshev class * Update copilot instructions to clarify beta project guidelines * Update copilot instructions to clarify refactoring guidelines * Add architecture documentation for EasyDiffraction library * Refactor calculator management for joint and sequential fitting in experiments * Refactor code blocks in architecture.md to specify shell syntax highlighting * Document current and potential architectural issues in the codebase * Document current and potential issues in architecture * Document limitations of `FactoryBase` regarding constructor-variant registrations * Refactor calculator and minimizer factory methods for consistency and clarity * Refactor architecture documentation for improved clarity and consistency * Refactor logging and docstring formatting in analysis and factory modules * Refactor calculator and minimizer factory to use tag strings; update tests accordingly * Refactor core factory structure to eliminate duplication and unify metadata handling * Add initial experiment setup and analysis workflow for hrpt project * Update easydiffraction version and SHA-256 hash in pixi.lock * Refactor architecture documentation to clarify factory structures and display methods * Remove unused exports from __init__.py to streamline module interface * Refactor string quotes in test.py for consistency and readability * unify CollectionBase key resolution, add __contains__ and remove() * Make ExperimentType immutable after creation * Make peak and background read-only public properties * Document editable vs read-only property convention with _set_ private methods * Standardize switchable-category naming convention * Apply formatting * Refactor Analysis class to instantiate calculator in __init__ and remove class-level attribute * Remove duplicated symmetry methods from Structure * Route constraint updates through validated setter * Override _key_for in CategoryCollection and DatablockCollection * Warn when switching background or peak profile type * Document why minimisers bypass the value setter * Replace 'lmfit (leastsq)' with 'lmfit' in tests, tutorials, and docs * Consolidate revised-design-v5.md into architecture.md * Apply formatting * Update copilot instructions for linting and testing workflow * Enable dirty-flag guard via _set_value_from_minimizer * Add factory.py and metadata.py to package structure documentation * Consolidate all issues into issues.md and update copilot instructions * Move calculator from global Analysis to per-experiment * Add Bragg+PDF joint tutorial, multi-experiment docs section, and test * Temporarily disable Bragg+PDF joint fit until test is improved * Add universal factories for Extinction and LinkedCrystal * Add universal factories for all remaining categories * Update tutorial #14 * Add switchable-category API to all factory-created categories * Add switchable-category API for instrument and data on experiments * Filter show_supported_instrument_types by experiment context * Add target-audience and reliability instructions to copilot config * Convert fit_mode to CategoryItem * Convert fit_mode to factory-based category with enum comparison * Add flat category structure rule to architecture.md * Consolidate architecture docs and fix stale references * Restructure help() to show Parameters, Properties, and Methods tables * Auto-populate Analysis.help() from class introspection * Refine eager-imports rule to document lazy-import exceptions * Update tutorial structure in mkdocs.yml for clarity and consistency * Update issues list * Add instruction to run tutorial tests after changes * Refactor background type naming for consistency * Delete test.py * Fix tutorial failures and stale calculator cache with excluded regions * Update issues * Update tutorial names and copyright year
1 parent 6a52ea9 commit 25e84b7

265 files changed

Lines changed: 13467 additions & 6365 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Copilot Instructions for EasyDiffraction
2+
3+
## Project Context
4+
5+
- Python library for crystallographic diffraction analysis, such as refinement
6+
of the structural model against experimental data.
7+
- Support for
8+
- sample_form: powder and single crystal
9+
- beam_mode: time-of-flight and constant wavelength
10+
- radiation_probe: neutron and x-ray
11+
- scattering_type: bragg and total scattering
12+
- Calculations are done using external calculation libraries:
13+
- `cryspy` for Bragg diffraction
14+
- `crysfml` for Bragg diffraction
15+
- `pdffit2` for Total scattering
16+
- Follow CIF naming conventions where possible. In some places, we deviate for
17+
better API design, but we try to keep the spirit of the CIF names.
18+
- Reusing the concept of datablocks and categories from CIF. We have
19+
`DatablockItem` (structure or experiment) and `DatablockCollection`
20+
(collection of structures or experiments), as well as `CategoryItem` (single
21+
categories in CIF) and `CategoryCollection` (loop categories in CIF).
22+
- Metadata via frozen dataclasses: `TypeInfo`, `Compatibility`,
23+
`CalculatorSupport`.
24+
- The API is designed for scientists who use EasyDiffraction as a final product
25+
in a user-friendly, intuitive way. The target users are not software
26+
developers and may have little or no Python experience. The design is not
27+
oriented toward developers building their own tooling on top of the library,
28+
although experienced developers will find their own way. Prioritize
29+
discoverability, clear error messages, and safe defaults so that
30+
non-programmers are not stuck by standard API conventions.
31+
- This project must be developed to be as error-free as possible, with the same
32+
rigour applied to critical software (e.g. nuclear-plant control systems).
33+
Every code path must be tested, edge cases must be handled explicitly, and
34+
silent failures are not acceptable.
35+
36+
## Code Style
37+
38+
- Use snake_case for functions and variables, PascalCase for classes, and
39+
UPPER_SNAKE_CASE for constants.
40+
- Use `from __future__ import annotations` in every module.
41+
- Type-annotate all public function signatures.
42+
- Docstrings on all public classes and methods (Google style).
43+
- Prefer flat over nested, explicit over clever.
44+
- Write straightforward code; do not add defensive checks for unlikely edge
45+
cases.
46+
- Prefer composition over deep inheritance.
47+
- One class per file when the class is substantial; group small related classes.
48+
- Avoid `**kwargs`; use explicit keyword arguments for clarity, autocomplete,
49+
and typo detection.
50+
- Do not use string-based dispatch (e.g. `getattr(self, f'_{name}')`) to route
51+
to attributes or methods. Instead, write explicit named methods (e.g.
52+
`_set_sample_form`, `_set_beam_mode`). This keeps the code greppable,
53+
autocomplete-friendly, and type-safe.
54+
- Public parameters and descriptors are either **editable** (property with both
55+
getter and setter) or **read-only** (property with getter only). If internal
56+
code needs to mutate a read-only property, add a private `_set_<name>` method
57+
instead of exposing a public setter.
58+
59+
## Architecture
60+
61+
- Eager imports at the top of the module by default. Use lazy imports (inside a
62+
method body) only when necessary to break circular dependencies or to keep
63+
`core/` free of heavy utility imports on rarely-called paths (e.g. `help()`).
64+
- No `pkgutil` / `importlib` auto-discovery patterns.
65+
- No background/daemon threads.
66+
- No monkey-patching or runtime class mutation.
67+
- Do not use `__all__` in modules; instead, rely on explicit imports in
68+
`__init__.py` to control the public API.
69+
- Do not use redundant `import X as X` aliases in `__init__.py`. Use plain
70+
`from module import X`.
71+
- Concrete classes use `@Factory.register` decorators. To trigger registration,
72+
each package's `__init__.py` must explicitly import every concrete class (e.g.
73+
`from .chebyshev import ChebyshevPolynomialBackground`). When adding a new
74+
concrete class, always add its import to the corresponding `__init__.py`.
75+
- Switchable categories (those whose implementation can be swapped at runtime
76+
via a factory) follow a fixed naming convention on the owner (experiment,
77+
structure, or analysis): `<category>` (read-only property), `<category>_type`
78+
(getter + setter), `show_supported_<category>_types()`,
79+
`show_current_<category>_type()`. The owner class owns the type setter and the
80+
show methods; the show methods delegate to `Factory.show_supported(...)`
81+
passing context. Every factory-created category must have this full API, even
82+
if only one implementation exists today.
83+
- Categories are flat siblings within their owner (datablock or analysis). A
84+
category must never be a child of another category of a different type.
85+
Categories can reference each other via IDs, but not via parent-child nesting.
86+
- Every finite, closed set of values (factory tags, experiment axes, category
87+
descriptors with enumerated choices) must use a `(str, Enum)` class. Internal
88+
code compares against enum members, never raw strings.
89+
- Keep `core/` free of domain logic — only base classes and utilities.
90+
- Don't introduce a new abstraction until there is a concrete second use case.
91+
- Don't add dependencies without asking.
92+
93+
## Changes
94+
95+
- Before implementing any structural or design change (new categories, new
96+
factories, switchable-category wiring, new datablocks, CIF serialisation
97+
changes), read `docs/architecture/architecture.md` to understand the current
98+
design choices and conventions. Follow the documented patterns (factory
99+
registration, switchable-category naming, metadata classification, etc.) to
100+
stay consistent with the rest of the codebase. For localised bug fixes or test
101+
updates, the rules in this file are sufficient.
102+
- The project is in beta; do not keep legacy code or add deprecation warnings.
103+
Instead, update tests and tutorials to follow the current API.
104+
- Minimal diffs: don't rewrite working code just to reformat it.
105+
- Never remove or replace existing functionality as part of a new change without
106+
explicit confirmation. If a refactor would drop features, options, or
107+
configurations, highlight every removal and wait for approval.
108+
- Fix only what's asked; flag adjacent issues as comments, don't fix them
109+
silently.
110+
- Don't add new features or refactor existing code unless explicitly asked.
111+
- Do not remove TODOs or comments unless the change fully resolves them.
112+
- When renaming, grep the entire project (code, tests, tutorials, docs).
113+
- Every change should be atomic and self-contained, small enough to be described
114+
by a single commit message. Make one change, suggest the commit message, then
115+
stop and wait for confirmation before starting the next change.
116+
- When in doubt, ask for clarification before making changes.
117+
118+
## Workflow
119+
120+
- All open issues, design questions, and planned improvements are tracked in
121+
`docs/architecture/issues_open.md`, ordered by priority. When an issue is
122+
fully implemented, move it from that file to
123+
`docs/architecture/issues_closed.md`. When the resolution affects the
124+
architecture, update the relevant sections of
125+
`docs/architecture/architecture.md`.
126+
- After changes, run linting and formatting fixes with `pixi run fix`. Do not
127+
check what was auto-fixed, just accept the fixes and move on.
128+
- After changes, run unit tests with `pixi run unit-tests`.
129+
- After changes, run integration tests with `pixi run integration-tests`.
130+
- After changes, run tutorial tests with `pixi run script-tests`.
131+
- Suggest a concise commit message (as a code block) after each change (less
132+
than 72 characters, imperative mood, without prefixing with the type of
133+
change). E.g.:
134+
- Add ChebyshevPolynomialBackground class
135+
- Implement background_type setter on Experiment
136+
- Standardize switchable-category naming convention
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: easydiffraction.datablocks.experiment
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: easydiffraction.datablocks.structure

docs/api-reference/experiments.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/api-reference/index.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ available in EasyDiffraction:
1313
space groups, and symmetry operations.
1414
- [utils](utils.md) – Miscellaneous utility functions for formatting,
1515
decorators, and general helpers.
16+
- datablocks
17+
- [experiments](datablocks/experiment.md) – Manages experimental setups and
18+
instrument parameters, as well as the associated diffraction data.
19+
- [structures](datablocks/structure.md) – Defines structures, such as
20+
crystallographic structures, and manages their properties.
1621
- [display](display.md) – Tools for plotting data and rendering tables.
1722
- [project](project.md) – Defines the project and manages its state.
18-
- [sample_models](sample_models.md) – Defines sample models, such as
19-
crystallographic structures, and manages their properties.
20-
- [experiments](experiments.md) – Manages experimental setups and instrument
21-
parameters, as well as the associated diffraction data.
2223
- [analysis](analysis.md) – Provides tools for analyzing diffraction data,
2324
including fitting and minimization.
2425
- [summary](summary.md) – Provides a summary of the project.

docs/api-reference/sample_models.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)