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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## [2.0.0] - Unreleased

### Release-readiness hardening (2026-08-27)

- Harden the Workbench preflight boundary with content-aware input identities,
language-only display refreshes, localized empty-queue/completion messages,
and stale-progress reset on failed Tab 2/3 runs. Update the release-readiness
snapshot and operator documentation; this remains an unreleased source
candidate.
- Harden structured canSAS/NXcanSAS Q-axis handling to fail closed on missing
or unknown units, and canonicalize `1/m` to Å⁻¹. Detect NaN/Inf in headerless
profiles, publish canSAS XML atomically, handle Pydidas numeric tokens and
inline comments, and reject empty arrays in the scientific core. These are
engineering safeguards, not measured scientific acceptance.

### Fluorescence subtraction (1D)

- Add an opt-in absolute-scale fluorescence kernel
Expand Down
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@
reusable data writers, and provenance checks. The result and the processing
record remain reviewable together.

Reviewers should use the unreleased 2.0.0 tree on
[`main`](https://github.com/D-sudoasd/SASAbs). GitHub Release
[v1.1.1](https://github.com/D-sudoasd/SASAbs/releases/tag/v1.1.1) is an earlier
archive and is not this candidate. Do not treat the Zenodo concept DOI as a
version DOI for 2.0.0.
Version status:

- Current source candidate: branch [`main`](https://github.com/D-sudoasd/SASAbs),
version `2.0.0`, unreleased.
- Stable archive: GitHub Release
[`v1.1.1`](https://github.com/D-sudoasd/SASAbs/releases/tag/v1.1.1) release assets.
- The `2.0.0` candidate is source-only: no PyPI installation is documented, and
no version tag, GitHub Release, or Zenodo version archive has been created.
The DOI above is the project concept DOI.

<p align="center">
<a href="#quick-start"><strong>Quick start</strong></a> ·
Expand Down Expand Up @@ -55,8 +59,11 @@ python -m pip install -e ".[gui]"
saxsabs-workbench --lang en
```

The core package requires Python 3.10+, NumPy, pandas, and xraydb. The project
does not currently document a PyPI installation.
On Windows, `py -m pip install -e ".[gui]"` and `py saxsabs_workbench.py --lang en`
are equivalent Python-launcher forms.

The core package requires Python 3.10+, NumPy, pandas, and xraydb. The commands
above install from the source tree.

<details>
<summary><strong>Optional dependency groups</strong></summary>
Expand Down Expand Up @@ -189,9 +196,10 @@ python scripts/check_submission_readiness.py \
```

Run the strict command from the exact branch and commit that will be submitted.
PR #1 is already on `main`. A PASS recorded on an earlier revision does not
cover a later commit; update `submitted_branch` and `submitted_commit` and rerun
the gate on the revision sent to JOSS.
The submitted branch and 40-character SHA must identify the same revision as the
public README and paper blobs and the successful CI run. A PASS recorded for an
earlier revision does not cover a later commit; update `submitted_branch` and
`submitted_commit` and rerun both gates.

After the strict local gate passes, verify the same commit, branch, visible
README and paper, repository identity, and successful CI run against GitHub:
Expand Down
453 changes: 393 additions & 60 deletions SASAbs.py

Large diffs are not rendered by default.

51 changes: 29 additions & 22 deletions SUBMISSION_READINESS.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
# Submission readiness snapshot

Updated: 16 August 2026 (Asia/Shanghai)
Updated: 27 August 2026 (Asia/Shanghai)

Review the unreleased 2.0.0 tree on `main`, not GitHub Release v1.1.1. Do not
create `v2.0.0`, a GitHub Release, or a Zenodo version archive during review.
Review the current unreleased 2.0.0 source candidate on `main`; the stable
archive is GitHub Release v1.1.1 and its release assets. `v2.0.0` remains
unreleased: do not create its tag, GitHub Release, or Zenodo version archive
during this review.

## Locally verified

- Full source suite: PASS in a fully provisioned Python 3.13 environment; exact
count and duration are retained in the dated external validation record.
- Ruff: root modules, package, tests, paper scripts, and submission gate pass.
- Full source suite: PASS under Python 3.11 and 3.12, with
`py -3.11 -B -m pytest -q -p no:cacheprovider --tb=short -W error` and the
equivalent Python 3.12 command each reporting `1155 passed`.
- Full repository Ruff check: PASS.
- Current `git diff --check`: PASS.
- Distribution smoke from a clean temporary clone outside the checkout: sdist
and wheel builds, fresh-venv installation of `wheel[gui,hdf5]`, CLI/import/
`pip check`, and the `minimal_2d` synthetic smoke all PASS. The temporary
clone path is intentionally omitted because it is not durable evidence.
- Python 3.10 and 3.13 remain pending the remote CI matrix; the local full-suite
evidence above covers only Python 3.11 and 3.12.
- README: 5 local images and all local links resolve; SVG/image audit passes.
- Minimal 2D example: 9×9 homemade radial average (not pyFAI) recovers planted
K and sample maximum relative errors of `0.001933697...`; CSV, TSV, XML, and
HDF5 outputs are written with unknown uncertainty.
- Fresh-copy distribution build: wheel and sdist PASS from a source tree
outside every Git checkout. The exact archive inventory is retained in the
dated external validation record; the sdist includes README assets,
workflows, docs, examples, tests, and paper sources.
- Installed-wheel smoke: CLI reports `saxsabs 2.0.0`; `SASAbs`,
`saxs_mpl_style`, and `saxsabs` import from the temporary environment; the
copied minimal example passes outside the checkout. A fresh Python 3.13
environment resolves the declared GUI/HDF5 extras with no broken
requirements.
- Paper: 1100-word body by the documented Pandoc method; 16 references; current
HDF5 outputs are written with unknown uncertainty. This synthetic smoke is
an engineering/reproducibility check, not BL19B2 measured scientific
acceptance.
- Paper: 1228-word body by the documented Pandoc method; 16 references; current
Inara TeX and well-formed JATS resolve both figures.
- Review PDF: the official CI paper job produces a five-page draft whose pages,
bounds, figures, citations, and embedded fonts have been visually checked.
Expand Down Expand Up @@ -56,6 +59,9 @@ create `v2.0.0`, a GitHub Release, or a Zenodo version archive during review.
6. Before submission, verify that the public GitHub description, homepage
concept DOI, visible README, submitted branch, and green CI all identify the
exact candidate revision.
7. Complete measured beamline/scientific acceptance with archived raw inputs,
repeatability, and an independent comparison; synthetic validation and
engineering tests do not satisfy this gate.
Run the strict decision gate with Pandoc available:

```bash
Expand All @@ -64,10 +70,10 @@ python scripts/check_submission_readiness.py \
--manual-confirmations path/to/submission-confirmations.json
```

The gate must run on the exact branch and commit submitted to JOSS. PR #1 is
already on `main`; rerun the gate on the clean `main` commit that will be
submitted and record `submitted_branch` and `submitted_commit` accordingly.
Evidence from an earlier revision is not evidence for a later commit.
The gate must run on the exact branch and commit submitted to JOSS. Record the
submitted branch and 40-character SHA, and require that the local/public
README, paper blobs, and successful CI run all resolve to that same revision.
Evidence from an earlier commit does not cover a later commit.

After that local PASS, run:

Expand All @@ -83,7 +89,8 @@ editorialbot branch command when the paper is not on `main`.

The current strict result is intentionally **FAIL** because the paper still has
four author-input placeholders, no confirmed corresponding author, and no paper
email. The mechanical preflight passes when
email. No research-use evidence or measured scientific acceptance is recorded
as complete. The mechanical preflight passes when
`--allow-author-placeholders --as-of 2026-08-26` is used; this override is not a
submission authorization.

Expand Down
2 changes: 1 addition & 1 deletion codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"SRM 3600"
],
"dateCreated": "2026-02-25",
"dateModified": "2026-08-16",
"dateModified": "2026-08-27",
"developmentStatus": "active",
"softwareRequirements": [
"numpy >= 1.24",
Expand Down
24 changes: 17 additions & 7 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,18 @@
control and both Tab 2/Tab 3 existence-only resume controls are UI-disabled;
forced values make Dry Check BLOCKED and are rejected again at Run. K and μ
are read-only in Tab 2, and K is read-only in Tab 3.
- Workbench file identities currently bind resolved path, size, and mtime, not a
content SHA-256 for every selected source. `CAUTION` currently permits Run
without a separately persisted acknowledgement; these are deliberate open
boundaries, not properties of the strict runners. BG/Dark reference-library
mutations explicitly invalidate the in-memory Tab 2 approval.
- Workbench identities bind resolved path, size, mtime, and a streaming SHA-256
for selected queue files and currently active configured inputs. A disabled
optional field may retain a stale raw path, but its identity is normalized as
disabled: it is not hashed and does not block Run. When that input is enabled,
a missing, unreadable, or changing file is unverified and blocks Run; replacing
content at the same path with the same size/mtime changes the preflight
fingerprint. Changing a switch or configuration still invalidates approval;
language refresh only redraws display text and retains current Tab 2/Tab 3
approvals. `CAUTION` currently permits Run without a separately persisted
acknowledgement; these are deliberate open boundaries, not properties of the
strict runners. BG/Dark reference-library mutations explicitly invalidate the
in-memory Tab 2 approval.
- Tab 3 raw correction is disabled. Formal K/Kd accepts only an explicitly
reduced `relative` profile; `raw_counts`, `absolute_cm^-1`, and `ambiguous`
states fail closed. K/d requires `d > 0`; K-only applies K without repeating
Expand Down Expand Up @@ -149,7 +156,8 @@
- **Implemented**: normalization, header parsing, external 1D parsing, robust K
estimation, NIST 30 keV material core, Elam diagnostic calculator, 1D
intensity ledger, signed-in-memory Workbench preflight, fixed-thickness
enforcement, disabled legacy/resume controls, exact K-only/Kd/buffer gates,
enforcement, content-aware source identities, disabled legacy/resume controls,
exact K-only/Kd/buffer gates,
absolute-buffer validation, optional absolute 1D fluorescence subtraction,
provenance-aware scrollable μ UI, disabled Tab 3 raw mode, screen-aware
startup, strict BL19B2 workflows, standard writers, bilingual GUI, CLI, CI,
Expand All @@ -164,7 +172,9 @@
is disabled rather than treated as safe.
- **Desktop operation**: long GUI jobs run on the Tk event thread and are not
cancellable. Users should prefer headless workflows for unattended or large
campaigns. `CAUTION` remains visible but is not separately persisted as an
campaigns. Tab 2/Tab 3 progress resets at job entry and is marked failed on
an outer run error, but whole-job atomic publication is not implemented.
`CAUTION` remains visible but is not separately persisted as an
acknowledgement.
- **Input resources**: Workbench and headless detector readers share
`saxsabs.io.detector_images`. Reviewers should still use the documented
Expand Down
28 changes: 25 additions & 3 deletions docs/bl19b2_abs2d_batch_runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,32 @@ scientific outputs silently.

## Reuse Command

For a new beamtime, copy `examples/bl19b2_abs2d_template/processing_config.example.yml`,
edit the paths, run a dry scan first, then run the full export.
For a new beamtime, copy `examples/bl19b2_abs2d_template/processing_config.example.yml`
and edit the paths. Always use the following two-step sequence: inspect the
dry-run result first, then repeat the same command without `--dry-run` only
after the inputs and planned output root have been confirmed.

Template command:
Step 1 — preflight only (no formal output publication):

```powershell
$env:PYTHONPATH='src'
python -m saxsabs.cli bl19b2-abs2d `
--input-root '<BL19B2 DATA>\datXXX' `
--pydidas-cali-yaml '<BL19B2 DATA>\datXXX\reference_saxs\Cali.yaml' `
--output-root '<BL19B2 DATA>\datXXX_absolute_corrected_2D' `
--monitor-mode rate `
--mu 20.2 `
--standard-key SRM3600 `
--correct-solid-angle-for-k `
--no-polarization-correction `
--dry-run
```

Review the returned status, discovered sample inventory, geometry/mask
selection, thickness and uncertainty gates, and output paths. Correct any
failure or unexpected classification before continuing.

Step 2 — formal run after explicit confirmation:

```powershell
$env:PYTHONPATH='src'
Expand Down
16 changes: 12 additions & 4 deletions docs/joss-submission-checklist.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# JOSS submission checklist

This checklist follows the current JOSS author and reviewer documentation,
accessed 16 August 2026:
accessed 27 August 2026. It is a release-readiness snapshot for the unreleased
2.0.0 source candidate on `main`; the stable archived assets remain Release
v1.1.1.

- [Submission requirements](https://joss.readthedocs.io/en/latest/submitting.html)
- [Paper format](https://joss.readthedocs.io/en/latest/paper.html)
Expand All @@ -11,9 +13,10 @@ accessed 16 August 2026:
## Pre-review screening gates

- [ ] **More than six months of public development.** GitHub reports that this
repository was created on 25 February 2026. The date gate is therefore not
satisfied on 16 August 2026; 26 August 2026 is the first conservative
submission date, provided public development remains active.
repository was created on 25 February 2026. The first conservative
eligibility date is 26 August 2026; the author must still recheck the
public history on the actual submission date and keep this gate open until
that evidence is confirmed.
- [ ] **Demonstrated research use.** The repository contains a concrete BL19B2
workflow and reproducible synthetic validation material, but the author
must supply evidence that the software has been used in research. Claims
Expand Down Expand Up @@ -43,6 +46,8 @@ accessed 16 August 2026:
- [ ] Immediately before submission, record green push and Draft-PR runs for
the exact submitted HEAD in the dated external validation record. Do not
embed a self-referential commit hash in this tracked checklist.
- [ ] Confirm that the submitted branch and 40-character SHA identify the same
revision as the visible public README, paper blobs, and successful CI run.
- [ ] Verify that the public repository description, homepage concept DOI,
visible README, and submitted branch identify the same candidate.
- [ ] Run `scripts/check_public_candidate.py` against the completed confirmation
Expand Down Expand Up @@ -75,6 +80,9 @@ accessed 16 August 2026:
acknowledgements, funding, conflicts of interest, and contribution roles.
- [ ] The author confirms the complete AI disclosure and human review statement.
- [ ] The author supplies research-use evidence suitable for the impact section.
- [ ] Measured beamline/scientific acceptance is archived with raw inputs,
repeatability, and an independent comparison; synthetic validation and
engineering tests do not satisfy this gate.
- [x] The current official Inara workflow converts the paper to TeX and
well-formed JATS with citations and figures resolved.
- [x] The current candidate PDF was built from Inara-generated TeX with
Expand Down
38 changes: 27 additions & 11 deletions examples/manual-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ cannot be fully public.
1. Install package:

```bash
pip install -e .[dev,hdf5]
pip install -e ".[dev,hdf5]"
```

2. Run tests:
Expand Down Expand Up @@ -104,12 +104,23 @@ front end to the strict BL19B2 campaign runner.
one tracked scientific parameter or source path and confirm Run immediately
disables. Add BG/Dark files, add either library recursively, and clear the
libraries; every mutation must immediately invalidate Tab 2 approval. Run
must also reject a stale approval if a file's recorded size or modification
time changes after Dry Check.
must also reject a stale approval if a selected queue file or currently active
configured input's content, size, or modification time changes after Dry
Check. Replace a fixture with different bytes while preserving its size and
mtime to verify the streaming SHA-256 catches the replacement. Missing,
unreadable, or changing-while-read active files must fail closed rather than
become valid identities. Leave a stale missing path in a disabled optional
buffer/fluorescence field and confirm it does not block; then enable that
option without changing the path and confirm the active identity blocks Run.
Changing a switch or configuration must invalidate approval. Toggle the UI
language and confirm the approval remains current; this display-only action
must not invalidate Tab 2/Tab 3 preflight.
5. Repeat with BLOCKED fixtures and confirm Run remains disabled. Include Tab 3
K/d with blank, non-finite, zero, and negative thickness. Record that a
CAUTION result currently permits Run without a separately persisted
acknowledgement; this remains an open release gate.
acknowledgement; this remains an open release gate. Set a prior progress bar
to 100%, trigger a preflight failure at each Run entry, and confirm the bar
returns to 0 with a visible failed-job status.
6. In the material calculator, select the NIST 30 keV source and verify:

- Ti-24Nb-4Zr-8Sn: `74.550355 cm^-1`
Expand Down Expand Up @@ -202,13 +213,18 @@ These capabilities are outside the current Workbench support contract. They are
not claimed by the README or paper; use the strict headless workflow where
applicable:

- formal multi-folder/per-sample fixed-thickness campaigns have a Workbench
owner equivalent to the strict CLI/batch campaign;
- Workbench and strict BL19B2 runner use one shared scientific kernel;
- Workbench output root has an owner manifest, atomic campaign publication, and
content-signature resume (existence-only resume must remain disabled);
- Workbench preflight binds critical file content hashes and persists explicit
CAUTION acceptance;
- formal multi-folder/per-sample fixed-thickness campaigns do not have a
Workbench owner equivalent to the strict CLI/batch campaign;
- Workbench and strict BL19B2 runner do not use one shared campaign-level
scientific kernel;
- Workbench output root does not have an owner manifest, atomic campaign
publication, or content-signature resume (existence-only resume must remain
disabled);
- Workbench preflight binds SHA-256 content identities for selected queue files
and currently active configured inputs. Disabled optional fields may retain a
stale path without hashing or blocking; enabling them makes missing,
unreadable, or changing inputs fail closed. Configuration changes still
invalidate approval, and explicit CAUTION acceptance is not persisted;
- all FabIO readers pass OS-level handle audits on every Windows workstation
(unit tests now cover the shared copy-and-close helper; a full desktop
handle audit remains a local check);
Expand Down
2 changes: 1 addition & 1 deletion examples/minimal_2d/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Expected key result:
- `summary.json` with `k_relative_error < 0.005` and
`sample_max_relative_error < 0.01`
- `absolute_profile.csv`, `absolute_profile.tsv`, `absolute_profile.xml`
- `absolute_profile.h5` if `h5py` is installed (`pip install -e .[hdf5]`)
- `absolute_profile.h5` if `h5py` is installed (`pip install -e ".[hdf5]"`)

The example recovers a planted synthetic $K$ and sample curve on a 9×9 array
using a homemade integer-bin radial average (not pyFAI), writes labeled
Expand Down
3 changes: 3 additions & 0 deletions src/saxsabs/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,9 @@ def _normalize_q_profile(
if source_unit == "nm^-1":
x = x / 10.0
conversion = "nm^-1_to_A^-1"
elif source_unit == "m^-1":
x = x * 1.0e-10
conversion = "m^-1_to_A^-1"

updated = dict(profile)
updated["x"] = x.copy()
Expand Down
2 changes: 2 additions & 0 deletions src/saxsabs/core/buffer_subtraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def _as_1d_float_array(name: str, values: np.ndarray | None, *, require_finite:
arr = np.asarray(values, dtype=np.float64)
if arr.ndim != 1:
raise ValueError(f"{name} must be a 1-D array")
if arr.size == 0:
raise ValueError(f"{name} must not be empty")
if require_finite and not np.all(np.isfinite(arr)):
raise ValueError(f"{name} contains non-finite values")
return arr
Expand Down
Loading