Commit 4422e2e0 authored by Vacaliuc, Bogdan's avatar Vacaliuc, Bogdan
Browse files

infra: rename mr_core → ref_core; 01 authoritative tone + live links;

       add 13-debt-triage and 14-mr-lr-cross-reference

Scientists have agreed on `ref_core` as the canonical name for the
unified back-end.  Single sed pass across all .md/.svg/.py files in
plan/quicknxsv2-modularization/.  Slide 1 and slide 6 PNGs
re-rendered after their SVGs were touched.

01-repo-structure.md:
  - Add live GitHub links for all four repos:
      quicknxsv2     https://github.com/neutrons/quicknxs
      mr_reduction   https://github.com/neutrons/MagnetismReflectometer
      quicknxsv1     https://github.com/bvacaliuc/quicknxs/tree/feature/read-event-nexus
      lr_reduction   https://github.com/neutrons/LiquidsReflectometer


  - Remove the "do not trust this document either" hedging;
    hack-a-thon is tomorrow, this IS the architecture-of-record
  - Reframe the Mantid 6.14/6.15 pin mismatch as packaging-only
    (MR Python algorithms byte-identical across the range)
  - Reframe lr_reduction from "not the target" to "must stay in
    scope" (the modularization is two-instrument by design)
  - Replace the closing summary's "can 1 and 2 be unified without
    breaking 3?" with the broader "can 1 and 2 be unified into a
    shared back-end that also lets lr_reduction consume it?"

New docs:
  13-debt-triage.md — spreadsheet view of the 18 technical-debt
    items that used to live in slide-7-debt-triage.svg.  Tier 1..4
    tables with effort / payoff / doc citation / dependencies.
  14-mr-lr-cross-reference.md — functional pair map of
    mr_reduction vs lr_reduction (the mantid-centric 'next' branch,
    NOT lr_reduction:new_workflow).  What ref_core shares vs keeps
    per-instrument.  Explicit note that lr_reduction:new_workflow
    and quicknxsv1:read_event_nexus are deliberately out of scope
    for this document to keep the pre-hack-a-thon message clean.

README.md index updated with the new 13 and 14 entries.

Co-Authored-By: default avatarClaude Opus 4.7 (1M context) <noreply@anthropic.com>
parent 511f5693
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ back-ends that already exist in tree** — one in
## The three real choices for the hackathon

1. **Lift-and-name** — extract `quicknxsv2/interfaces/data_handling/`
   into a new library (tentatively `mr_core` or keep the `quicknxs`
   into a new library (tentatively `ref_core` or keep the `quicknxs`
   namespace but split the wheel), and `mr_reduction` continues to
   live alongside it.
   - *Robust.* Preserves the existing reduction algorithm that the
@@ -51,22 +51,22 @@ back-ends that already exist in tree** — one in
     `CrossSectionData` / `DataManager` abstractions).  Adding that
     API without breaking autoreduce is the real task.

3. **Carve-out `mr_core`** — build a third, neutral package that
3. **Carve-out `ref_core`** — build a third, neutral package that
   contains the *shared* primitives (file loading, cross-section
   filtering, dead-time correction, peak finding, ORSO I/O).  Both
   quicknxsv2 and `mr_reduction`'s scripted pipeline depend on
   `mr_core`.  Each keeps its own orchestration.
   `ref_core`.  Each keeps its own orchestration.
   - *Most robust.* Clean layering, each package has a clear purpose,
     can be versioned independently.
   - *Cost:* Three packages to maintain.  Migration is two independent
     refactors (one per consumer) that both have to land before the
     duplication is actually eliminated.

**Recommendation: option 3 (carve-out `mr_core`) is the robust choice.**
**Recommendation: option 3 (carve-out `ref_core`) is the robust choice.**
Option 1 and option 2 each *half-solve* the problem (UI separated but
back-end still duplicated vs. back-end unified but UI awkwardly
constrained); option 3 uses each week of hackathon effort to retire
one class of duplication, and the intermediate state (after mr_core
one class of duplication, and the intermediate state (after ref_core
exists but before mr_reduction adopts it) is still a net improvement
over today.  See `06-modularization-strategies.md` for the full
decision matrix.
+27 −18
Original line number Diff line number Diff line
# Repository structure — independent survey
# Repository structure — authoritative survey

This is a ground-truth map of the four repositories in the workspace.
Every claim is backed by a file path or a `git log` output so you can
verify independently.  **Do not trust this document either** — verify
against the current state of the repo when your hackathon starts.
This is the ground-truth map of the four repositories in scope.
Every claim is backed by a file path or a `git log` output.  Read
this once at the start of the hack-a-thon and refer back when
questions of scope or dependency arise.

## The four repositories, at a glance

| Repo | Role | Size | Primary language | External deps | First commit |
|---|---|---|---|---|---|
| `quicknxsv2` | Current Qt GUI for REF_M reduction | ~14,280 src LOC | Python 3.10–3.12 | Mantid, `mr_reduction==2.17.0`, PyQt5, matplotlib | 2017-10-24 |
| `mr_reduction` | Scripted reduction library + autoreduce + livereduce | ~7,620 src LOC | Python 3.11 | Mantid ≥6.15, Flask/gunicorn, plotly, plot_publisher | 2018-02-23 |
| `quicknxsv1` | Original Qt GUI (Python/numpy/h5py) | ~30,250 src LOC (inc. 7,625 from `icons_rc.py`) | Python 3.10–3.12 | PyQt5, h5py, numpy, scipy (no Mantid) | 2012-12-02 |
| `lr_reduction` | Sibling REF_L reduction project (not part of the hackathon target, but an architectural reference) | see `src/lr_reduction/` | Python 3.11 | Mantid, pure-numpy tools | 2020 (see `git log`) |
| [`quicknxsv2`](https://github.com/neutrons/quicknxs) | Current Qt GUI for REF_M reduction | ~14,280 src LOC | Python 3.10–3.12 | Mantid, `mr_reduction==2.17.0`, PyQt5, matplotlib | 2017-10-24 |
| [`mr_reduction`](https://github.com/neutrons/MagnetismReflectometer) | Scripted reduction library + autoreduce + livereduce | ~7,620 src LOC | Python 3.11 | Mantid ≥6.15, Flask/gunicorn, plotly, plot_publisher | 2018-02-23 |
| [`quicknxsv1:read_event_nexus`](https://github.com/bvacaliuc/quicknxs/tree/feature/read-event-nexus) | Original Qt GUI (Python/numpy/h5py); branch under active development for `.nxs.h5` event data | ~30,250 src LOC (incl. 7,625 from `icons_rc.py`) | Python 3.10–3.12 | PyQt5, h5py, numpy, scipy (no Mantid) | 2012-12-02 |
| [`lr_reduction`](https://github.com/neutrons/LiquidsReflectometer) | Sibling REF_L reduction project — architectural reference for the modularization | see `src/lr_reduction/` | Python 3.11 | Mantid, pure-numpy tools | 2020 |

Commit counts (as of 2026-04-18 on default branches):

@@ -127,8 +127,8 @@ TODO

All three sections of the "high-level design" are literal `TODO`.  The
other files under `docs/developer/` cover process (branching, release
cycle, CI troubleshooting) but not architecture.  This is consistent
with the user's instruction to *independently* investigate.
cycle, CI troubleshooting) but not architecture.  This document
(`01-…`) is therefore the architecture-of-record for the hack-a-thon.

## mr_reduction

@@ -206,8 +206,11 @@ mantid = ">=6.15.0,<6.16.0"

This is a **mismatch with quicknxsv2**, which pins
`mr_reduction == 2.17.0` and annotates (in its own `pyproject.toml`)
that version "requires mantid >=6.14.0,<6.15.0".  The hackathon will
trip on this.
that version "requires mantid >=6.14.0,<6.15.0".  The hack-a-thon
will need to resolve this at the packaging layer.  It is not a
numerical / reduction-math problem: the MR Python algorithms in
Mantid are byte-identical between 6.14 and current HEAD (see
`10-mantid-algorithms-deep-dive.md § 0`).

## quicknxsv1

@@ -261,8 +264,11 @@ From `quicknxsv1/CLAUDE.md`:
## lr_reduction

Short version, for architectural comparison.  `lr_reduction` targets
REF_L (the Liquids Reflectometer), not REF_M, and so it is not the
modularization target.  But its layout is instructive:
REF_L (the Liquids Reflectometer).  The modularization must keep
REF_L support in scope — even if the hack-a-thon works on REF_M
only, the back-end's shape must make REF_L adoption possible.  Its
layout is already a close match for what the target back-end should
look like:

```
lr_reduction/
@@ -316,6 +322,9 @@ Today the SNS reflectometry ecosystem has:
| mr_reduction (scripted, post-experiment) | Automated post-experiment reduction | Mantid `MagnetismReflectometryReduction` algorithm + `ReductionProcess` orchestration | Web form (Flask) for parameter selection; otherwise none |
| mr_livereduce (streaming, during experiment) | Live data monitoring | Subset of mr_reduction; runs inside the live-reduce service | Auto-generated HTML monitor pages |

The modularization question is really **can the first and second be
unified in a way that doesn't break the third**.  See
`04-reduction-pipeline.md` for the detailed data flows.
The modularization question is **can the first and second be
unified into a shared back-end (`ref_core`) that also lets
`lr_reduction` consume the same primitives, without breaking the
third.**  See `04-reduction-pipeline.md` for the detailed data
flows and `10-mantid-algorithms-deep-dive.md` / `11-technical-debt.md`
for the specific couplings that must be severed to get there.
+5 −5
Original line number Diff line number Diff line
@@ -223,18 +223,18 @@ question ("which library wins?") is resolved by end of Day 2:

Each one: write a test (or run existing tests), delete code, confirm green.

### Day 3 — pick a back-end strategy and carve `mr_core`
### Day 3 — pick a back-end strategy and carve `ref_core`

Based on the Day 1-2 learnings, the team picks one of the three
strategies from `06-modularization-strategies.md`.  Create the
`mr_core` (or equivalent) package skeleton.  Move:
`ref_core` (or equivalent) package skeleton.  Move:

- `filepath.py`
- `peak_finding.py` (from mr_reduction; v2's is gone)
- `types.py` (MantidWorkspace alias + others needed by both)
- `settings.py` (PolarizationLogs)

Start importing from `mr_core` in both quicknxsv2 and mr_reduction.
Start importing from `ref_core` in both quicknxsv2 and mr_reduction.

### Day 4 — unify the big parallel code

@@ -273,7 +273,7 @@ To make the refactor safe, the hackathon should consider adding:
- [ ] **Reference-data numerical equivalence tests.**  Before/after
      reduction outputs checksum-equivalent within tolerance.
- [ ] **Cross-repo equivalence tests.**  When a file is lifted from
      v2 to mr_reduction (or to `mr_core`), a test that calls the
      v2 to mr_reduction (or to `ref_core`), a test that calls the
      same function from both packages and asserts identical output.
      Lives in a third repo (or a "consumer" test fixture).
- [ ] **Contract tests** for the minimum API surface the UI depends
@@ -297,7 +297,7 @@ Tests that need Mantid are fundamentally slower than tests that
don't.  For a red-green refactor to be *fast*, design the new
back-end so that pure-Python primitives (peak finding, filepath
parsing, configuration validation) are importable without loading
Mantid.  A `mr_core.pure_python` subpackage that can be tested
Mantid.  A `ref_core.pure_python` subpackage that can be tested
quickly is a good pattern.

## Platform caveats
+34 −34
Original line number Diff line number Diff line
@@ -142,11 +142,11 @@ mr_reduction (back-end + autoreduce + livereduce) ──► mantid
- 2 days: update quicknxsv2 to consume the new API, test, ship.
- **Total: ~2 weeks.**

## Option 3 — Carve-out `mr_core` (robust recommendation)
## Option 3 — Carve-out `ref_core` (robust recommendation)

### The move

1. Create a new package `mr_core`.  Populate with:
1. Create a new package `ref_core`.  Populate with:
   - `filepath.py`, `peak_finding.py`, `types.py`, `settings.py`
     (low-hanging fruit, no Mantid version sensitivity).
   - `dead_time_correction.py` (Mantid algo, de-duplicated).
@@ -156,14 +156,14 @@ mr_reduction (back-end + autoreduce + livereduce) ──► mantid
   - Common data types: `CrossSection`, `PeakRegion`, `ReflectivityResult`
     — dataclasses that replace the ad-hoc Mantid-workspace-as-state
     pattern (Mantid workspaces are fine internally, but
     `mr_core` should expose Python-level data types for its
     `ref_core` should expose Python-level data types for its
     downstream consumers to test against).
2. `quicknxsv2` and `mr_reduction` both import from `mr_core`.
2. `quicknxsv2` and `mr_reduction` both import from `ref_core`.
   Each keeps its own orchestration class:
   - quicknxsv2: `DataManager` / `ProcessingWorkflow` → interactive.
   - mr_reduction: `ReductionProcess` → scripted.
3. Each orchestration class is documented as a thin "session" wrapper
   over `mr_core` primitives.  They may well evolve to converge
   over `ref_core` primitives.  They may well evolve to converge
   (e.g. share a base class) over time, but they don't have to on
   Day 1.

@@ -172,13 +172,13 @@ mr_reduction (back-end + autoreduce + livereduce) ──► mantid
```
                     ┌──► mantid

mr_core ─────────────┤
ref_core ─────────────┤

                     └──► orsopy (for ORSO format)

quicknxs (Qt GUI + ProcessingWorkflow + DataManager) ──► mr_core
quicknxs (Qt GUI + ProcessingWorkflow + DataManager) ──► ref_core

mr_reduction (ReductionProcess + autoreduce + livereduce) ──► mr_core
mr_reduction (ReductionProcess + autoreduce + livereduce) ──► ref_core
                                                              + mantid
                                                              + plotly
                                                              + flask
@@ -191,43 +191,43 @@ mr_reduction (ReductionProcess + autoreduce + livereduce) ──► mr_core

### Pros

- **Duplicate science code lives in exactly one place: `mr_core`.**
- **Duplicate science code lives in exactly one place: `ref_core`.**
- Each orchestration layer can evolve independently.  The UI team
  doesn't have to coordinate every release with autoreduce.
- **Graceful migration.**  mr_core can ship before either consumer
- **Graceful migration.**  ref_core can ship before either consumer
  fully migrates — both consumers can start importing individual
  functions as they're ready.
- mr_core's dependency set is *minimal* (mantid + orsopy).  No Flask,
  no plotly, no pyoncat.  Scientists can use mr_core in notebooks
- ref_core's dependency set is *minimal* (mantid + orsopy).  No Flask,
  no plotly, no pyoncat.  Scientists can use ref_core in notebooks
  without spinning up autoreduce.
- **Opens the door to other UIs.**  A Jupyter / streamlit / CLI
  front-end can wrap mr_core the same way quicknxsv2 does.
- **Resolves Mantid version pinning cleanly.**  mr_core picks one
  front-end can wrap ref_core the same way quicknxsv2 does.
- **Resolves Mantid version pinning cleanly.**  ref_core picks one
  Mantid range.  Both consumers align to it in lockstep.

### Cons

- **Three packages to version, release, and document** instead of
  two.  Real ongoing cost.
- The intermediate state (mr_core exists but mr_reduction has not
- The intermediate state (ref_core exists but mr_reduction has not
  yet migrated) has three places a function could be.  Requires
  discipline about deprecation markers / compatibility shims.
- Until mr_reduction migrates, duplication isn't fully resolved —
  it just has a third copy in mr_core.  The hackathon needs to
  it just has a third copy in ref_core.  The hackathon needs to
  schedule the mr_reduction migration PR as part of the same
  effort.

### Effort

- Week 1 (hackathon): create `mr_core` skeleton + migrate the easy
- Week 1 (hackathon): create `ref_core` skeleton + migrate the easy
  files (peak_finding, filepath, types, settings, dead_time).
  Release mr_core 0.1.  Update quicknxsv2 and mr_reduction to
  import from mr_core where trivial.
- Week 2: lift `io_orso` into mr_core.  Add first dataclass-based
  Release ref_core 0.1.  Update quicknxsv2 and mr_reduction to
  import from ref_core where trivial.
- Week 2: lift `io_orso` into ref_core.  Add first dataclass-based
  APIs.
- Weeks 3-4 (follow-on): unify `DataInfo` into mr_core.  Write the
- Weeks 3-4 (follow-on): unify `DataInfo` into ref_core.  Write the
  numerical-equivalence test harness.  Migrate both consumers.
- **Hackathon milestone is mr_core 0.1 + easy wins.  Full
- **Hackathon milestone is ref_core 0.1 + easy wins.  Full
  consolidation is multi-sprint.**

## The decision matrix
@@ -246,9 +246,9 @@ Scoring each option against the criteria (3 = fully addresses,
| Mantid version convergence | 1 | 3 | 3 |
| **Total** | **13** | **16** | **19** |

## The recommendation: Option 3 (`mr_core`)
## The recommendation: Option 3 (`ref_core`)

The robust choice is **option 3 (carve-out mr_core)** because:
The robust choice is **option 3 (carve-out ref_core)** because:

1. It's the only option that *actually removes duplication* AND
   *keeps autoreduce working unchanged* AND *gives the UI a clear
@@ -257,7 +257,7 @@ The robust choice is **option 3 (carve-out mr_core)** because:
   hackathon effort produces a commit that is a strict improvement
   on the previous day.  Options 1 and 2 require a flag day.
3. It forces the hackathon to *design* the back-end API (what goes
   in mr_core, what stays in each consumer) rather than lift and
   in ref_core, what stays in each consumer) rather than lift and
   hope.  The design conversation is necessary anyway.
4. It sets up a world in which alternative reflectometry front-ends
   (Jupyter, CLI, another GUI) become feasible.  This is aligned
@@ -267,18 +267,18 @@ The robust choice is **option 3 (carve-out mr_core)** because:

Simplicity variant: option 1 (lift-and-name) can be treated as
**Phase 0 of option 3** — step 1 is lift the UI out, and the new
package is named "mr_core" from the start.  Phase 1 (next sprint) is
mr_reduction starts importing from mr_core.  Phase 2 (sprint after
package is named "ref_core" from the start.  Phase 1 (next sprint) is
mr_reduction starts importing from ref_core.  Phase 2 (sprint after
that) is full consolidation of `DataInfo` and stitching.

## What "done" looks like

For hackathon milestone #1 (end of week):

- [ ] `mr_core` package exists with `filepath.py`, `peak_finding.py`,
- [ ] `ref_core` package exists with `filepath.py`, `peak_finding.py`,
      `types.py`, `settings.py`, `dead_time_correction.py`.
- [ ] quicknxsv2 imports from `mr_core` for the above.
- [ ] mr_reduction imports from `mr_core` for the above; its
- [ ] quicknxsv2 imports from `ref_core` for the above.
- [ ] mr_reduction imports from `ref_core` for the above; its
      duplicated copies are deleted.
- [ ] A numerical-equivalence test harness exists that proves
      reduction output is byte-for-byte identical against a
@@ -291,8 +291,8 @@ For hackathon milestone #1 (end of week):

For hackathon milestone #2 (follow-on sprint):

- [ ] `DataInfo` is unified in `mr_core`.
- [ ] `io_orso` is unified in `mr_core`.
- [ ] `DataInfo` is unified in `ref_core`.
- [ ] `io_orso` is unified in `ref_core`.
- [ ] quicknxsv2's `interfaces/data_handling/data_info.py` is deleted.
- [ ] mr_reduction's `data_info.py` is deleted (or is a thin shim).
- [ ] A `Session`/`Pipeline` API is drafted (design only; not
@@ -302,7 +302,7 @@ For milestone #3:

- [ ] Stitching logic is unified.
- [ ] quicknxsv2's `ProcessingWorkflow` becomes a thin wrapper over
      `mr_core` primitives.
      `ref_core` primitives.
- [ ] mr_reduction's `ReductionProcess` is refactored to share
      internals with `ProcessingWorkflow`.

@@ -335,7 +335,7 @@ For milestone #3:

## Questions the hackathon must answer on Day 1

1. **Who maintains mr_core if we create it?**  Same team, different
1. **Who maintains ref_core if we create it?**  Same team, different
   cadence?
2. **Does SNS's deploy infrastructure handle a new conda package
   cleanly?**  (Check with CIS team.)
+11 −11
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ re-reduction of old data.
### R3 — Autoreduce/livereduce silently break

**Symptom.** The hackathon rewrites `ReductionProcess` and moves it
into `mr_core`.  The facility's autoreduce service stops producing
into `ref_core`.  The facility's autoreduce service stops producing
reports.  Data from an ongoing experiment is lost.

**Likelihood:** moderate.  The autoreduce pipeline is well-tested
@@ -145,8 +145,8 @@ require a second iteration.

- Talk to the v1 maintainer on Day 1.  Is v1 being kept alive
  deliberately?  If so, should its `qreduce.py` eventually adopt
  mr_core?  If v1 is on its way out, how soon?
- Favor non-Mantid-type-leakage in the mr_core API — don't expose
  ref_core?  If v1 is on its way out, how soon?
- Favor non-Mantid-type-leakage in the ref_core API — don't expose
  Mantid workspace handles across the package boundary, use plain
  numpy arrays and dataclasses.  This makes a pure-Python back-end
  (v1-style) a possible future consumer.
@@ -154,22 +154,22 @@ require a second iteration.
### R7 — lr_reduction team may be affected

**Symptom.** lr_reduction has its own `peak_finding.py`,
`dead_time_correction.py`, etc.  If `mr_core` is created for REF_M
`dead_time_correction.py`, etc.  If `ref_core` is created for REF_M
use, lr_reduction might want to join.  But lr_reduction is REF_L,
a different instrument; the "M" in `mr_core` is awkward.
a different instrument; the "M" in `ref_core` is awkward.

**Likelihood:** low for day 1, moderate over time.

**Impact:** mr_core's name may need to change if cross-instrument
**Impact:** ref_core's name may need to change if cross-instrument
use is in scope.

**Mitigation:**

- Name-bikeshedding on Day 1.  `mr_core` implies REF_M only;
- Name-bikeshedding on Day 1.  `ref_core` implies REF_M only;
  something like `reflectometry_core` or `sns_reflectometry` is
  more portable.  But don't over-engineer — name after what you're
  actually shipping in week 1.
- Keep mr_core's inputs/outputs in terms of generic reflectometry
- Keep ref_core's inputs/outputs in terms of generic reflectometry
  types, not REF_M-specific.  That way the decision to include
  lr_reduction is additive, not a rewrite.

@@ -245,10 +245,10 @@ and coordination when bumping cross-package pins.
- Talk to the CIS / neutron DevOps team early.  Their Confluence
  (https://ornl-neutrons.atlassian.net/wiki/spaces/NDEVOPS/) has
  established patterns.
- Single-repo monorepo alternative: `mr_core` lives inside the
- Single-repo monorepo alternative: `ref_core` lives inside the
  `MagnetismReflectometer` (mr_reduction) repo as a separately-installed
  package.  Pro: one release pipeline.  Con: coupling the releases
  of mr_core and mr_reduction together.
  of ref_core and mr_reduction together.
- Use Python's namespace packaging if three `conda` packages are
  too much: all three packages publish into a `sns_reflectometry.*`
  namespace, but each is its own pypi/conda artifact.
@@ -305,7 +305,7 @@ were imported, the registration order would determine which wins
— behavior not deterministic.

**How to resolve:** pick one home for the algorithm; either (a)
consolidate in `mr_core` and have both sides import from there, or
consolidate in `ref_core` and have both sides import from there, or
(b) keep it in mr_reduction and ensure v2 imports it only via
mr_reduction.

Loading