Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b530409
Add tutorial benchmark results for Darwin ARM64 Py3.14
AndrewSazonov Jun 18, 2026
6e6e746
Correct absorption wrapping notes in ADR
AndrewSazonov Jun 18, 2026
a57c9e0
Add three new open issues and refine absorption note
AndrewSazonov Jun 18, 2026
84491b9
Merge origin/develop into cfl-api-2
AndrewSazonov Jun 18, 2026
f00a627
Update CrysFML CFL X-ray support
AndrewSazonov Jun 18, 2026
d740122
Add LiF X-ray verification cases
AndrewSazonov Jun 18, 2026
65819f6
Add PbSO4 X-ray verification diagnostics
AndrewSazonov Jun 18, 2026
cbe783b
Remove unused verification constants
AndrewSazonov Jun 18, 2026
572189b
Remove PbSO4 X-ray verification data
AndrewSazonov Jun 18, 2026
0148042
Standardize verification examples and use CrysFML 0.7.0
AndrewSazonov Jun 18, 2026
51c192c
Apply formatting
AndrewSazonov Jun 18, 2026
d578d13
Fix CFL API review findings
AndrewSazonov Jun 18, 2026
c747a91
Move pattern corrections to analysis/corrections package
AndrewSazonov Jun 18, 2026
d513898
Add CrysFML Python API request evidence scripts
AndrewSazonov Jun 18, 2026
32a2f2c
Document upstream request evidence workflow
AndrewSazonov Jun 18, 2026
d2b0fbd
Prefix verification links with experiment types
AndrewSazonov Jun 18, 2026
75ea182
Focus LaB6 verification examples on one feature
AndrewSazonov Jun 18, 2026
caf4c40
Add request evidence comparison plots
AndrewSazonov Jun 18, 2026
8091633
Stabilize CFL backend verification checks
AndrewSazonov Jun 18, 2026
dc819a5
Add CrysFML request evidence and PDF verification
AndrewSazonov Jun 18, 2026
b74f5ff
Update CrysFML Python API request evidence
AndrewSazonov Jun 18, 2026
593b449
Update verification examples and request scripts
AndrewSazonov Jun 19, 2026
2e312fa
Remove unsupported crysfml verification sections
AndrewSazonov Jun 19, 2026
8e91c9c
Update Fe and Tb2Ti2O7 verification examples
AndrewSazonov Jun 19, 2026
935c637
Add comprehensive features documentation for user interface component…
AndrewSazonov Jun 19, 2026
5901dac
Publish features matrix in user docs
AndrewSazonov Jun 19, 2026
80e9de2
Rename verification scripts for total scattering profiles
AndrewSazonov Jun 19, 2026
3127c43
Refactor verification documentation for LaB6 and add new profiles for…
AndrewSazonov Jun 19, 2026
13cfab4
Add features section to documentation overview
AndrewSazonov Jun 19, 2026
c144cb4
Enhance features documentation
AndrewSazonov Jun 19, 2026
ce3204b
Refine verification notebooks and feature documentation
AndrewSazonov Jun 19, 2026
62b42b0
Remove fort.77, ignore Fortran scratch files, format docs
AndrewSazonov Jun 19, 2026
96a7ad6
Add missing verification notebooks to docs nav
AndrewSazonov Jun 19, 2026
b285c25
Refresh PDF scale baselines after external scale refactor
AndrewSazonov Jun 19, 2026
e970fb3
Refine Features page layout and per-engine status icons
AndrewSazonov Jun 19, 2026
cb8f769
Apply prettier formatting to Features and Introduction
AndrewSazonov Jun 19, 2026
32971cf
Restore mobile content gutter on docs pages
AndrewSazonov Jun 19, 2026
eb91b9f
Reword Features page intro for a user audience
AndrewSazonov Jun 19, 2026
5eba19e
Clarify column legend and render its example icons
AndrewSazonov Jun 19, 2026
713421d
Fix SyCos/SySin cross-refs for displacement and transparency
AndrewSazonov Jun 19, 2026
181acbc
Split structural twinning and add magnetic domains row
AndrewSazonov Jun 19, 2026
6d13d23
Align minimizer and sampler names with backend (method) tags
AndrewSazonov Jun 19, 2026
59540f2
List pdffit2 as planned for space group IT number
AndrewSazonov Jun 19, 2026
4a57eff
Mark pdffit2 not supporting IT coordinate system code
AndrewSazonov Jun 19, 2026
07c019c
Clarify auto vs explicit background estimation methods
AndrewSazonov Jun 19, 2026
0ab1bdc
Update preferred-orientation, second-wavelength, displacement rows
AndrewSazonov Jun 19, 2026
d3f77cb
Mark cryspy TOF peak-shape profiles as implemented
AndrewSazonov Jun 19, 2026
eca2f4e
Fix extinction cross-ref and pdffit2 no-data simulation status
AndrewSazonov Jun 19, 2026
2aadff3
Swap not-applicable icon and document unknown-status icon
AndrewSazonov Jun 19, 2026
4975cc4
Add report formats and posterior plots to features
AndrewSazonov Jun 19, 2026
c380007
Split polarized neutron diffraction into 2.3 and 2.4
AndrewSazonov Jun 19, 2026
2c7cbee
Expand and complexity-sort backlog; tag tutorials Jupyter
AndrewSazonov Jun 19, 2026
e4a061e
Remove redundant TOC list and plainer engine wording
AndrewSazonov Jun 19, 2026
5a3bbff
Drop already-implemented fit-quality-metrics backlog item
AndrewSazonov Jun 19, 2026
91d9784
Spell out Ikeda-Carpenter sigma and gamma parameters
AndrewSazonov Jun 19, 2026
cb6ec7c
Unify CLI column icons and explain CLI workflow in a note
AndrewSazonov Jun 19, 2026
173d866
Clarify structure names in verification documentation
AndrewSazonov Jun 19, 2026
9eef4b4
Condense engine list with links to the Analysis page
AndrewSazonov Jun 19, 2026
74beb0e
Move column and Feature-cell legends into note blocks
AndrewSazonov Jun 19, 2026
771f472
Make the How-to-read legend notes collapsible
AndrewSazonov Jun 20, 2026
eb1ae30
Refactor documentation for clarity and formatting improvements
AndrewSazonov Jun 20, 2026
0848c65
Fix grammar in epics note
AndrewSazonov Jun 20, 2026
2301ee8
Fix column logic, cross-ref icon, and heading consistency
AndrewSazonov Jun 20, 2026
21a5e3a
Tighten matrix status columns and cell padding
AndrewSazonov Jun 20, 2026
f4576c0
Balance matrix cell padding and narrow status columns
AndrewSazonov Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ CLAUDE.md
/data/
/projects/
/tmp/

# FullProf/CrysFML Fortran scratch unit files
fort.*
16 changes: 8 additions & 8 deletions docs/dev/adrs/accepted/development-docs-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ docs/dev/
|-- package-structure/
| |-- full.md
| `-- short.md
|-- plans/
`-- roadmap/
`-- ROADMAP.md
`-- plans/
```

Use lowercase directory names for new development-doc folders. Keep
`ROADMAP.md` uppercase because it may later be copied into published
user documentation where the conventional filename is useful.
Use lowercase directory names for new development-doc folders. The
feature/roadmap matrix is published directly in the user documentation
at `docs/docs/features/index.md` (single source of truth for both
current capabilities and planned work); there is no separate
`docs/dev/roadmap/` copy.

Use `docs/dev/adrs/index.md` as the architecture and decision navigation
surface. Do not keep a separate architecture overview that duplicates
Expand All @@ -62,5 +62,5 @@ ADR content.
- ADRs have one entry point for architecture navigation and separate
accepted and proposed areas.
- Package-structure snapshots have stable, script-friendly paths.
- Roadmap publication can later be implemented as a build-time copy from
`docs/dev/roadmap/ROADMAP.md` into `docs/docs`.
- The roadmap is published directly as a user-facing page at
`docs/docs/features/index.md`, kept in sync with the codebase.
33 changes: 17 additions & 16 deletions docs/dev/adrs/accepted/model-sample-absorption.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ factor that attenuates low-angle peaks more than high-angle peaks;
omitting it leaves an angle-dependent intensity residual that scales
with the sample's μR (linear absorption coefficient × radius).

This is not hypothetical. The 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. See
This is not hypothetical. The FullProf reference directory
`pd-neut-cwl_lab6` was refined with **μR = 0.7**; the unmodelled
correction is the _entire_ intensity residual on the companion
`pd-neut-cwl_LaB6_absorption` page (≈5 % profile difference), while the
μR = 0 page passes to corr 0.9999. See
[issue #119](../../issues/open/highest_model-sample-absorption-debye-scherrer-r.md).

### What the three reference sources provide
Expand All @@ -55,13 +55,14 @@ implements the CW formulas in Fortran:
and `cabs ∈ {HEWAT, LOBANOV}`.
- `Powder_Lorentz_IntegInt_CW(…, muR, …)` — the bare Hewat form.

**However**, these routines are **not wrapped** in CrysFML's
`PythonAPI/`, and the high-level entry our backend actually calls
(`cw_powder_pattern_from_dict`) computes a plain Lorentz factor
`0.5/(sin²θ·cosθ)` with no absorption term. So the issue's claim that
absorption is "reachable via `Lorentz_abs_CW` through pycrysfml" is
**not true today** — it would require upstream wrapping or an upstream
call-site change we do not control.
**However**, the standalone absorption routines are **not wrapped** in
CrysFML's `PythonAPI/`. The high-level CFL entry our backend calls
(`patterns_simulation`) prepares the reflection corrections inside
CrysFML and does not expose a model-level μR input through our binding.
So the issue's claim that absorption is "reachable via `Lorentz_abs_CW`
through pycrysfml" is **not true today** — using it as an internal
correction would require an upstream wrapper or a broader upstream
call-site binding we do not control.

**cryspy** has **no absorption code at all** (only Debye–Waller and
sphere _extinction_, which are different physics). CW intensity is
Expand Down Expand Up @@ -169,7 +170,7 @@ y_corrected(2θ_i) = A(θ_i) · y_calc(2θ_i)
```

This is a single shared helper
(`analysis/calculators/absorption.py::factor(two_theta, params)`) called
(`analysis/corrections/absorption.py::factor(two_theta, params)`) called
from the post-calculation step of both `cryspy.py` and `crysfml.py`. The
two backends thus stay bit-for-bit consistent on the absorption term,
and the helper is unit-testable in isolation against FullProf output
Expand Down Expand Up @@ -368,9 +369,9 @@ backend round-trip.
## Consequences

- **Closes the LaB₆ absorption residual.** The
`pd-neut-cwl_tch-fcj_abs_lab6` verification page becomes the
acceptance test: with `cylinder-hewat`, `mu_r = 0.7` it should reach
the same corr as the μR = 0 page.
`pd-neut-cwl_LaB6_absorption` verification page becomes the acceptance
test: with `cylinder-hewat`, `mu_r = 0.7` it should reach the same
corr as the μR = 0 page.
- **Calculator-consistent by construction.** Both backends call the same
helper, so the absorption term can never drift between `cryspy` and
`crysfml`. New backends inherit it for free.
Expand Down
95 changes: 95 additions & 0 deletions docs/dev/adrs/accepted/post-calculation-corrections-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ADR: Post-Calculation Corrections Package

## Status

Accepted.

## Date

2026-06-18.

## Group

Analysis and fitting.

> This ADR follows [`AGENTS.md`](../../../../AGENTS.md). It defines
> where calculator-independent corrections live, separate from the
> calculation backends. It does not change any public API or any
> correction's behaviour; it is a placement and naming decision.

## Context

`analysis/calculators/` holds the diffraction calculation backends —
`crysfml`, `cryspy`, `pdffit` — each a `CalculatorBase` subclass that
computes a pattern from a structure and experiment. Its own package
docstring describes it as the home of the calculation _backends_.

Two modules accumulated in that same package that are **not** backends:

- `absorption.py` — Debye–Scherrer `μR` intensity factor (issue 119).
- `polarization.py` — X-ray CW Lorentz–polarization factor.

Both _adjust an already-calculated pattern_ rather than computing one.
Each exposes a module-level `apply(y, experiment) -> y` (plus internal
`factor(...)` helpers) and is called by `crysfml.py` and `cryspy.py`
**after** the backend returns its convolved profile, so the two backends
stay bit-for-bit consistent on the correction term. Neither is exported
from any `__init__.py`; they are imported by full module path.

Mixing post-calculation corrections into the backends package conflates
two roles (compute vs. adjust) and gives no obvious home for the further
point-wise corrections the
[in-house calculation engine ADR](../suggestions/in-house-calculation-engine.md)
anticipates (per-reflection `SyCos`/`SySin`, preferred orientation).

## Decision

1. **Move corrections to a sibling package `analysis/corrections/`.**
`absorption.py` and `polarization.py` move out of
`analysis/calculators/` into `analysis/corrections/`. `calculators/`
stays purely about engines; `corrections/` holds adjustments applied
on top of an engine's output. The unit tests mirror the move to
`tests/unit/easydiffraction/analysis/corrections/`.

2. **Keep the informal `apply(y, experiment)` contract; no base class
yet.** Each correction module exposes `apply(y, experiment) -> y`,
returning `y` unchanged when the correction does not apply. Two plain
functions sharing a signature do not justify a `CorrectionBase`
abstraction; one is introduced only when a third correction shows a
concrete shared need (per [`AGENTS.md`](../../../../AGENTS.md)
§Architecture).

3. **No public-API or behaviour change.** The modules were never
re-exported, so the move is import-path-only for the two backend
consumers and the tests. Each correction's math is unchanged.

4. **Corrections stay backend-agnostic; they are not relocated into a
future native engine.** External backends still need the point-wise
forms, so `analysis/corrections/` is their permanent home. The native
engine reimplements the _exact per-reflection_ forms on its own code
path rather than moving these modules (see the in-house engine ADR).

## Consequences

### Positive

- `calculators/` is unambiguously the engines package; `corrections/`
names the post-calculation layer and gives future point-wise
corrections an obvious, scalable home.
- The shared `apply(y, experiment)` contract is discoverable in one
place.
- Contained change: no public API, no behaviour, no framework churn.

### Negative / cost

- A one-time import-path update for the two backends and the moved
tests, plus refreshed path references in two existing ADRs.

## Alternatives Considered

| # | Alternative | Verdict |
| --- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| A | **`analysis/corrections/` sibling package** (this ADR). | **Chosen.** Clean split between compute and adjust; scalable home for the growing correction family. |
| B | **Nested `analysis/calculators/corrections/`.** | Rejected. Keeps corrections under the `calculators` name they do not belong to; the conceptual conflation remains. |
| C | **Keep them in `analysis/calculators/`.** | Rejected. Mixes non-engine modules into the backends package; no home for further corrections. |
| D | **Introduce a `CorrectionBase` class now.** | Deferred. Premature abstraction for two plain `apply()` functions; revisit when a third correction lands. |
27 changes: 14 additions & 13 deletions docs/dev/adrs/accepted/preferred-orientation-category.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Three independent sources confirm the same simple, widely used model —
direction); it is **not** the verification reference. Both shipped
FullProf PO examples are simulated-annealing demos on _calculated_
data, so the verification case is constructed separately (see the
§Verification section, which builds on `pd-neut-cwl_pv_lbco`).
§Verification section, which builds on `pd-neut-cwl_LBCO_basic`).

The three models agree on the **category shape** — a single scalar March
coefficient plus an integer direction (and an optional random fraction)
Expand Down Expand Up @@ -487,7 +487,7 @@ preferred orientation, so PO stays CrysPy-only for now.)
small, discoverable API and standards-aligned CIF.
- One new category package plus experiment wiring and a CrysPy
serialization branch; no changes to the structure model.
- A new cross-engine verification case (on the `pd-neut-cwl_pv_lbco`
- A new cross-engine verification case (on the `pd-neut-cwl_LBCO_basic`
base, two-parameter March–Dollase) extends the existing FullProf suite
(consistent with the cross-engine work in commits #195–#199): refining
`march_r`, `march_random_fract`, and scale recovers FullProf's
Expand Down Expand Up @@ -552,24 +552,25 @@ against measured data — unsuitable as a verification reference.

The verification notebook is a **positive cross-engine agreement check**
that also exercises both March–Dollase parameters. It is built on the
existing **`pd-neut-cwl_pv_lbco`** case (La₀.₅Ba₀.₅CoO₃, neutron CW,
existing **`pd-neut-cwl_LBCO_basic`** case (La₀.₅Ba₀.₅CoO₃, neutron CW,
pseudo-Voigt — chosen as the base on request):

1. Copy `docs/docs/verification/fullprof/pd-neut-cwl_pv_lbco/lbco.pcr`,
1. Copy
`docs/docs/verification/fullprof/pd-neut-cwl_lbco_basic/lbco.pcr`,
enable the March–Dollase model (`Nor=1`) along the phase
`Pr1 Pr2 Pr3` direction with a non-zero `Pref1` **and** `Pref2`, and
re-run FullProf locally (`~/Applications/fullprof`) to regenerate
`.prf`/`.bac`/`.sum` (the reference uses `Pref1=1.2`, `Pref2=0.3`,
axis `[0 0 1]`).
2. Add `pd-neut-cwl_pv-march_lbco` (paired `.py`/`.ipynb`) that builds
the same LBCO model, sets `expt.preferred_orientation` with `r=Pref1`
and `fraction=Pref2`, then **refines `march_r`, `march_random_fract`,
and scale**. ed-cryspy recovers `r≈Pref1` and `fraction≈Pref2`, and
`verify.assert_patterns_agree` passes (Profile diff ≈ 0.7%, area and
shape within tolerance). The as-calculated step shows the constant
scale offset from CrysPy's non-normalisation (Decision 6), reconciled
by the fit. PO is CrysPy-only, so the case compares CrysPy vs
FullProf only (no CrysFML column).
2. Add `pd-neut-cwl_LBCO_preferred-orientation` (paired `.py`/`.ipynb`)
that builds the same LBCO model, sets `expt.preferred_orientation`
with `r=Pref1` and `fraction=Pref2`, then **refines `march_r`,
`march_random_fract`, and scale**. ed-cryspy recovers `r≈Pref1` and
`fraction≈Pref2`, and `verify.assert_patterns_agree` passes (Profile
diff ≈ 0.7%, area and shape within tolerance). The as-calculated step
shows the constant scale offset from CrysPy's non-normalisation
(Decision 6), reconciled by the fit. PO is CrysPy-only, so the case
compares CrysPy vs FullProf only (no CrysFML column).

This verification notebook is built **before any user tutorial**, so the
tutorial can cite a validated, well-understood workflow. Producing the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ The engine is passed **explicitly as a tag** (`'cryspy'`, `'crysfml'`),
deliberate contract choice: a verification page computes one engine's
curve, stores it, **switches the calculator**, computes the next, then
compares the **stored** arrays together (e.g.
`docs/docs/verification/pd-neut-cwl_pv_lbco.py` calculates with
`docs/docs/verification/pd-neut-cwl_LBCO_basic.py` calculates with
`cryspy`, switches to `crysfml`, then renders both stored results). A
helper that read the _active_ engine at render time would label a stored
`cryspy` result with whichever engine happened to be active later —
Expand Down
5 changes: 2 additions & 3 deletions docs/dev/adrs/accepted/xray-cw-polarization-optics.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ the model this ADR follows for declaration style.

### Diagnostic evidence

`docs/docs/verification/pd-xray-pbso4-single-polarized-wdt48.py` adds a
focused diagnostic reference generated from
`pbsox_single_polarized_wdt48.pcr`:
A focused PbSO₄ diagnostic reference was generated from
`pbsox_single_polarized_wdt48.pcr` during the polarization design work:

- `Lambda1 = Lambda2 = 1.540560`
- `Ratio = 0`
Expand Down
Loading
Loading