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
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ contributors). Tool-specific files (`CLAUDE.md`) only point here. This file
links the page that owns each topic instead of restating it.

PowderLine runs powder diffraction refinements from a **recipe** (structured
data: a dict, serialised as JSON). A recipe names one engine and one workflow
data: a dict, serialized as JSON). A recipe names one engine and one workflow
in `schema_name` (`gsasii.rietveld`, `gsasii.spf`, `topas.rietveld`,
`topas.spf`, `easydiffraction.rietveld`) and is written in that engine's own
terms and units. `powderline.run()` and the `powderline` CLI hand it to the
Expand All @@ -20,7 +20,7 @@ statistics. Nothing is translated between engines.
- `docs/engines/*.md` + `docs/catalog/engine_behaviour.yaml` — what each
engine does and PowderLine's policy (`EB-NN`).
- `docs/reference/known-issues.md` + `docs/catalog/known_issues.yaml` —
known, deliberately deferred behaviours (`KI-NN`). Check before "fixing" a
known, deliberately deferred behaviors (`KI-NN`). Check before "fixing" a
quirk.
- The code under `src/` and `pixi.toml`: where a page and the code disagree,
the code wins; report the discrepancy.
Expand Down Expand Up @@ -94,12 +94,12 @@ Detail: `docs/development/contributing.md`.
`src/powderline/gateways/gsasii/{helpers,project,setters,executors,extractors,constraints}.py`
do GSAS-II's project-dictionary work. Rules (`docs/development/policies.md#frozen-gsas-ii-code`):

- A behaviour-changing edit needs the maintainers' explicit consent. An agent
- A behavior-changing edit needs the maintainers' explicit consent. An agent
never makes one on its own: propose it, with its reason and proof.
- Comment and docstring edits need no consent. Don't leave outdated comments.
- After **any** edit, `pixi run update-code-hash` (the hash covers raw bytes;
`tests/test_code_hash.py` guards it).
- New behaviour goes into the non-frozen modules (`fit_report`, `sites`,
- New behavior goes into the non-frozen modules (`fit_report`, `sites`,
`routing`, `paths`, `gateway`).
- Replace the GSAS-II server after any edit it runs (a stale server is
detected: skipped in `auto` mode, refused in `server` mode):
Expand Down Expand Up @@ -139,7 +139,7 @@ do GSAS-II's project-dictionary work. Rules (`docs/development/policies.md#froze
`docs/concepts/results.md`.
- Every failure raises `EngineExecutionError` or `ExecutionBackendError`; a
gateway never returns a failed result — `docs/development/adding-an-engine.md`.
- Every intentional behaviour change is deliberate; references move only with
- Every intentional behavior change is deliberate; references move only with
an equivalence check — `docs/development/policies.md`.
- Docs follow the writing rules in
`docs/development/contributing.md#writing-documentation`.
Expand Down
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ limitations are listed in the [known-issues register](docs/reference/known-issue
- Examples: per-engine recipes under `examples/<case>/{topas,easydiffraction}/`,
`example_LaB6_temperature_series` (sequential refinement), `example_LaB6_ionic`
and `example_LaB6_textured`, with easydiffraction references.
- Documentation, organised by reader: Get started, Concepts, How-to guides,
- Documentation, organized by reader: Get started, Concepts, How-to guides,
Examples, Engines, Reference and Development, on pydata-sphinx-theme with
diagrams and per-engine tabs ([docs](https://nsls2.github.io/PowderLine/)).
Among its pages: [outputs](docs/reference/outputs.md),
[fields](docs/reference/fields.md), [validation](docs/concepts/validation.md),
[engine behaviour](docs/engines/index.md),
[engine behavior](docs/engines/index.md),
[cross-engine comparison](docs/concepts/comparing-engines.md) and
[policies](docs/development/policies.md).
- Every engine behaviour (`EB-NN`) and known issue (`KI-NN`) has its own
- Every engine behavior (`EB-NN`) and known issue (`KI-NN`) has its own
entry and link, listed in the [ID index](docs/reference/ids.md). Known
issues KI-19 to KI-21 cover the GSAS-II server's lifetime, its state
directory and a race in `gsas-server stop`
Expand Down Expand Up @@ -283,7 +283,7 @@ The pre-refactor baseline; unified recipe schema 0.26.0.

### Fixed
- GSAS-II's refinement failure message is surfaced instead of a silent
no-Rwp result; `run()` normalisation no longer crashes on null tables from
no-Rwp result; `run()` normalization no longer crashes on null tables from
server failure responses (#3).

## [0.1.0] - 2026-08-17
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pixi run test # the engine-free suites
when they are stale).
- The frozen GSAS-II modules
(`src/powderline/gateways/gsasii/{helpers,project,setters,executors,extractors,constraints}.py`)
change behaviour only with the maintainers' explicit consent. Comment and
change behavior only with the maintainers' explicit consent. Comment and
docstring edits are fine. After any edit, run `pixi run update-code-hash`.
- `examples/**/output/` is tracked on purpose (regression references). It
moves only as a deliberate, reviewed change.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ walks through them.
| Engine | Workflows | Runs on |
|---|---|---|
| GSAS-II 5.7.9 | Rietveld, single-peak fitting | Linux, Windows, macOS\* |
| TOPAS 6 | Rietveld, single-peak fitting | Windows with a TOPAS licence (input files are written anywhere) |
| TOPAS 6 | Rietveld, single-peak fitting | Windows with a TOPAS license (input files are written anywhere) |
| easydiffraction 0.21.1 | Rietveld | Linux, Windows, macOS\* |

\* macOS (Apple Silicon) is expected to work but is not routinely tested.
Expand All @@ -106,7 +106,7 @@ Full references, including the calculators and gemmi, are on
[Citing PowderLine](https://nsls2.github.io/PowderLine/about/citing.html).
Machine-readable metadata is in [`CITATION.cff`](CITATION.cff).

## Contributing and licence
## Contributing and license

See the [developer guide](https://nsls2.github.io/PowderLine/development/).
BSD 3-Clause licence, see [LICENSE](LICENSE).
BSD 3-Clause license, see [LICENSE](LICENSE).
8 changes: 4 additions & 4 deletions docs/_generated/cross_engine_comparison.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@
"value": 4.156826,
"esd": 4e-05,
"label": "NIST SRM 660c",
"note": "certificate 2015-10-29: 0.4156826(8) nm, k = 2, 22.5 \u00b0C"
"note": "certificate 2015-10-29: 0.415\u202f682\u202f6 nm \u00b1 0.000\u202f008 nm, expanded uncertainty k = 2, 22.5 \u00b0C"
}
},
"DRX_33": {
Expand Down Expand Up @@ -558,8 +558,8 @@
}
],
"generated": {
"date": "2026-11-05",
"commit": "b6cee8a",
"no_run": false
"date": "2026-11-06",
"commit": "72a539a",
"no_run": true
}
}
4 changes: 2 additions & 2 deletions docs/_generated/cross_engine_comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Fit window and points: 3767 points (every engine scores the same points).
| easydiffraction | LaB6 | a | 4.157175(18) Å | -10 | -1.5 | +84 | +8.0 |
| easydiffraction | LaB6 | a | 4.157495(17) Å | +67 | +10.2 | +161 | +15.4 |

Reference for the NIST columns: NIST SRM 660c, a = 4.156826 Å (certificate 2015-10-29: 0.4156826(8) nm, k = 2, 22.5 °C; standard uncertainty 4e-05 Å used for σ).
Reference for the NIST columns: NIST SRM 660c, a = 4.156826 Å (certificate 2015-10-29: 0.415 682 6 nm ± 0.000 008 nm, expanded uncertainty k = 2, 22.5 °C; standard uncertainty 4e-05 Å used for σ).

### DRX_33

Expand Down Expand Up @@ -59,4 +59,4 @@ Fit window and points: 3767 points (every engine scores the same points).
- GSAS-II 5.7.9: run 2026-11-05 (PowderLine 0.2.0, Python 3.13.16); `execution_mode='subprocess'`.
- TOPAS: TOPAS-64 Version 6 goldens (`tests/data/topas/native/golden/6/`, TOPAS machine runs of 2026-10-09); **not executed for this page**: the rows are the gateway's result built from the committed files.
- easydiffraction goldens (`tests/data/easydiffraction/native/golden/`, runs of 2026-10-09): easydiffraction 0.21.1, cryspy 0.13.0, crysfml 0.8.0, lmfit 1.3.4, numpy 2.4.6, scipy 1.18.0.
- Table generated 2026-11-05 by `scripts/cross_engine_comparison.py` at commit `b6cee8a`.
- Table generated 2026-11-06 by `scripts/cross_engine_comparison.py` at commit `72a539a` in `--no-run` mode (GSAS-II rows from the committed JSON record).
2 changes: 1 addition & 1 deletion docs/_generated/eb_all.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@

{bdg-secondary}`documented` · GSAS-II 5.7.9; easydiffraction 0.21.1 (CrysPy 0.13.0, CrysFML 0.8.0)

**What happens:** Lattice parameters agree between GSAS-II and easydiffraction when axial asymmetry is modelled physically (FCJ). With a symmetric profile they shift by +67 to +91 ppm, whichever calculator runs it.
**What happens:** Lattice parameters agree between GSAS-II and easydiffraction when axial asymmetry is modeled physically (FCJ). With a symmetric profile they shift by +67 to +91 ppm, whichever calculator runs it.

**PowderLine does:** Documented on the cross-engine comparison. Rwp compares models fitted to the same points, not engines.
14 changes: 7 additions & 7 deletions docs/_generated/eb_easydiffraction.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@

{bdg-success}`handled` · easydiffraction 0.21.1

**easydiffraction does:** When a fit starts, easydiffraction gives an unbounded side its physical limit: cell lengths 0–30 Å, angles 0–180°, occupancy 0–1, `adp_iso` and U11/U22/U33 0–10 Ų, scale, wavelength and μR ≥ 0. It refuses a value outside a physical limit at input, even when fixed, so no cell over 30 Å can be modelled. lmfit moves a start outside its bounds onto the bound without a message.
**easydiffraction does:** When a fit starts, easydiffraction gives an unbounded side its physical limit: cell lengths 0–30 Å, angles 0–180°, occupancy 0–1, `adp_iso` and U11/U22/U33 0–10 Ų, scale, wavelength and μR ≥ 0. It refuses a value outside a physical limit at input, even when fixed, so no cell over 30 Å can be modeled. lmfit moves a start outside its bounds onto the bound without a message.

**PowderLine does:** A stated bound replaces the physical limit on its side, and `null` keeps it. A value outside a physical limit is a validation error. A refined value that ends within 1e-6 (relative) of a bound or physical limit gets a warning. For a limit of 0, the test is within 1e-6 of the starting value.

Expand All @@ -191,9 +191,9 @@

{bdg-success}`handled` · easydiffraction 0.21.1 (CrysPy 0.13.0, CrysFML 0.8.0)

**easydiffraction does:** Both calculators model the Kα doublet, the Lorentz-polarization factor 1 − p + p cos²2θ_m cos²2θ, the 2θ offset, sample displacement and transparency, and Hewat cylinder absorption. A second wavelength alone does nothing. 2θ_m acts only with p > 0. GSAS-II's polarization P is easydiffraction's 1 − p: copying P to p is a 6.6 % intensity error at 15° 2θ.
**easydiffraction does:** Both calculators model the Kα doublet, the Lorentz factor and the polarization factor 1 − p + p cos²2θ_m cos²2θ, the 2θ offset, sample displacement and transparency, and Hewat cylinder absorption. A second wavelength alone does nothing. 2θ_m acts only with p > 0. GSAS-II's polarization P is easydiffraction's 1 − p: copying P to p is a 6.6 % intensity error at 15° 2θ.

**PowderLine does:** Every correction is a recipe slot; left out means not modelled. The schema requires both Kα2 fields or neither. The hand-derived per-engine recipes state p = 1 − P.
**PowderLine does:** Every correction is a recipe slot; left out means not modeled. The schema requires both Kα2 fields or neither. The hand-derived per-engine recipes state p = 1 − P.

**You see:** validation error

Expand Down Expand Up @@ -228,9 +228,9 @@

{bdg-secondary}`documented` · easydiffraction 0.21.1 (CrysPy 0.13.0)

**easydiffraction does:** CrysPy's texture correction is not volume-normalised, so every textured-to-untextured intensity ratio carries the constant r^1.5. With `march_random_fract` = 0 the scale absorbs it, and the refined `march_r` is the same r as GSAS-II's. With f > 0 the correction is (1 − f)·r^1.5·A + f, and the refined r and f are not GSAS-II's quantities.
**easydiffraction does:** CrysPy's texture correction is not volume-normalized, so every textured-to-untextured intensity ratio carries the constant r^1.5. With `march_random_fract` = 0 the scale absorbs it, and the refined `march_r` is the same r as GSAS-II's. With f > 0 the correction is (1 − f)·r^1.5·A + f, and the refined r and f are not GSAS-II's quantities.

**PowderLine does:** Documented. A test compares normalised ratios with GSAS-II's March–Dollase correction.
**PowderLine does:** Documented. A test compares normalized ratios with GSAS-II's March–Dollase correction.

### Data and the fit window

Expand Down Expand Up @@ -336,7 +336,7 @@

{bdg-secondary}`documented` · easydiffraction 0.21.1 (CrysFML 0.8.0)

**easydiffraction does:** Relative to m·|F|²·Lorentz, CrysFML's integrated intensities drift by −4 % from 3° to 15° 2θ. This biases `Uiso` by about +0.001 Ų.
**easydiffraction does:** Relative to m·|F|²·Lorentz, CrysFML's integrated intensities drift by −4 % from 3° to 15° 2θ. A drift this size is worth about 0.001 Ų of `Uiso`; a refinement compensates by lowering `Uiso`.

**PowderLine does:** The calculator is recorded in every result.

Expand All @@ -345,7 +345,7 @@

{bdg-secondary}`documented` · easydiffraction 0.21.1 (CrysPy 0.13.0, CrysFML 0.8.0)

**easydiffraction does:** On GSAS-II-calculated LaB6 patterns, easydiffraction refines La's Uiso 10–22 % low. B's Uiso, the thermal expansion and GoF ≈ 1 agree. The cause is the series' profile: GSAS-II clamps its Lorentzian width to 0.001 centideg above about 10.7°, so the high-angle peaks are pure Gaussian. Neither a symmetric pseudo-Voigt nor TCH can represent that, and `Uiso` absorbs the mismatch. Simulated with a profile easydiffraction can represent, CrysPy's La Uiso is 0.00863 against 0.00858 programmed. CrysFML's is 0.00823 (−4 %), the size of its intensity drift ({ref}`EB-78 <eb-78>`).
**easydiffraction does:** On GSAS-II-calculated LaB6 patterns, easydiffraction refines La's Uiso 10–22 % low. B's Uiso, the thermal expansion and GoF ≈ 1 agree. Most of it comes from the series' profile: GSAS-II clamps its Lorentzian width to 0.001 centideg above about 10.7°, so the high-angle peaks are pure Gaussian. Neither a symmetric pseudo-Voigt nor TCH can represent that, and `Uiso` absorbs the mismatch. Simulated with a profile easydiffraction can represent, CrysPy's La Uiso is 0.00863 against 0.00858 programmed. CrysFML's is 0.00823 (−4 %); its remaining profile mismatch and its intensity drift ({ref}`EB-78 <eb-78>`) both act on `Uiso`, and the two were not separated.

**PowderLine does:** Documented, no code change. Across engines, compare cells, with the {ref}`EB-88 <eb-88>`-size offsets in mind, and the expansion. Absolute `Uiso` agrees only when the profile models agree.

Expand Down
2 changes: 1 addition & 1 deletion docs/_generated/eb_gsasii.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@

**GSAS-II does:** GSAS-II carries negative `Uiso` and `Uij` values and refines from them.

**PowderLine does:** Core has no sign rule for ADPs: a negative ADP can compensate for an unmodelled effect.
**PowderLine does:** Core has no sign rule for ADPs: a negative ADP can compensate for an unmodeled effect.

(eb-20)=
#### EB-20 · Any occupancy is accepted
Expand Down
4 changes: 2 additions & 2 deletions docs/_generated/eb_topas.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@

{bdg-success}`handled` · TOPAS 6

**TOPAS does:** The `_LIMIT_MIN` and `_LIMIT_MAX` markers TOPAS 6 writes are mislabelled or spurious.
**TOPAS does:** The `_LIMIT_MIN` and `_LIMIT_MAX` markers TOPAS 6 writes are mislabeled or spurious.

**PowderLine does:** The at-limit warning is judged from the final values and the limits the writer knows, not from TOPAS's markers.

Expand All @@ -145,7 +145,7 @@

**TOPAS does:** TCHZ_Peak_Type combines a Gaussian width G and a Lorentzian width L = X·tanθ + Y/cosθ into one FWHM. With a fixed X < 0, TOPAS 6 neither takes |X| nor clamps it at 0. X = −0.01 runs, and the profile differs from X = 0. X = −0.1 makes the width negative at the top of the window, and TOPAS writes no results: a negative convolution FWHM stops TOPAS.

**PowderLine does:** PowderLine honours what TOPAS computes: a negative Lorentzian part passes while the total width is positive. With `TCHZ_Peak_Type`, the total width at the recipe's starting values must be positive at every data point of the fit window. Otherwise validation fails and names X, Y and the 2θ where the width is not positive. An output recipe whose refined values break the rule is not valid.
**PowderLine does:** PowderLine honors what TOPAS computes: a negative Lorentzian part passes while the total width is positive. With `TCHZ_Peak_Type`, the total width at the recipe's starting values must be positive at every data point of the fit window. Otherwise validation fails and names X, Y and the 2θ where the width is not positive. An output recipe whose refined values break the rule is not valid.

**You see:** validation error `topas_tchz_width_not_positive`, `output_recipe_not_valid`

Expand Down
2 changes: 1 addition & 1 deletion docs/_generated/examples/example_DRX_33.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/_generated/examples/example_DRX_33_anisoADP.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/_generated/examples/example_DRX_33_atomrefine.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading