Skip to content

Latest commit

 

History

History
133 lines (107 loc) · 6.58 KB

File metadata and controls

133 lines (107 loc) · 6.58 KB

multitorch — Project Instructions

Current status (updated 2026-04-17)

Public repo: https://github.com/follmerlab/multitorch Version: 0.1.0 (MIT license) Tests: 477/477 passing Audits: Scientific, code review, and test builder audits all passed — deployment gate PASSED

Deferred items (pick up here)

  • SCA-001: Change default broaden_mode from "legacy" to "correct" in pseudo_voigt — breaking API change across 6 function signatures, needs a deprecation cycle
  • GPU acceleration: Plan at docs/GPU_ACCELERATION_PLAN.md
  • Minor: document Eg-only autograd flow in calcXAS docstring
  • Minor: add assertion that shell pair is 3D for current scope

What this is

PyTorch port of the Fortran ttmult/Cowan multiplet X-ray spectroscopy suite for L-edge XAS of 3d transition metals. Must match Fortran numerically.

Environment

  • Conda env: multi (Python 3.11, PyTorch 2.5)
  • Python: /opt/anaconda3/envs/multi/bin/python
  • Pytest: /opt/anaconda3/envs/multi/bin/pytest tests/ -q
  • Working dir: /Users/afollmer/Follmer_UCD/Follmer_Lab/Code/multiplets/multitorch/

Codebase layout

multitorch/
  multitorch/           # package source
    angular/            # Wigner 3j/6j/9j, CFP, RME builder, symmetry
    atomic/             # HFS SCF, Slater integrals, radial mesh
    hamiltonian/        # assemble.py (main CT pipeline), diagonalize, transitions
    spectrum/           # sticks, broaden (Voigt/pseudo-Voigt), rixs, background
    io/                 # parsers: read_rme.py, read_ban.py, read_rcf.py, read_oba.py
    api/                # calc.py (calcXAS entry point), plot.py
    data/
      cfp/              # bundled CFP tables (rcg_cfp72, rcg_cfp73)
      fixtures/         # bundled Fortran reference outputs (9 Ti-Ni fixtures)
  tests/
    reference_data/     # symlink/copy of fixtures for test compatibility
    test_angular/       # Wigner, CFP, RME tests
    test_atomic/        # HFS, Slater, mesh tests
    test_hamiltonian/   # assemble, diagonalize, BAN parser tests
    test_integration/   # end-to-end nid8ct XAS tests
    test_spectrum/      # broadening, sticks tests
  notebooks/            # 4 tutorial notebooks
  docs/                 # GPU_ACCELERATION_PLAN.md

Fortran source lives at ../ttmult/src/ (rcn31.f, rcn2.f, ttrcg.f, ttrac.c, ttban_exact.f). Fortran binaries at ../ttmult/bin/. Python wrapper at ../pyttmult/. Legacy Python API at ../pyctm/.

Orchestration

All implementation progress, bugs, investigation logs, and task tracking are in .claude/orchestration/. See .claude/orchestration/INDEX.md for the full index.

Before starting any work:

  1. Read .claude/orchestration/INDEX.md to understand current state
  2. Check if your task has an existing investigation log
  3. Update the relevant files when you make progress or find something new

Key conventions

  • All tensors use float64 (DTYPE from _constants.py)
  • COWAN store sections are 0-indexed in Python; ADD entry matrix_idx is 1-based (subtract 1)
  • Energy offsets (EG/EF) are NOT scaled by 1/sqrt(IDIM); Hamiltonian matrix elements ARE
  • .rme_rcg sections delimited by FINISHED markers map 1:1 to Fortran PAIRIN calls
  • .rme_rac operator blocks: "GROUND" = config 1, "EXCITE" = config 2 (even in ground state manifold)
  • Hybridization blocks in .rme_rac are labeled TRANSI with geometry containing "HYBR"

Known pitfalls

  • The Fortran PAIRIN subroutine reads .rme_rcg/.rme_rac SEQUENTIALLY. Each call consumes one COWAN section. The Python code indexes by section number instead.
  • PAIRIN overwrites mateg(1)/mateg(2) on each call. The FINAL call (ground mixing, section 2) is the one that matters for make_hamiltonian.
  • Empty .rme_rac blocks (no ADD entries) must be skipped — they exist as placeholders from earlier PAIRIN calls.
  • The Fortran gausslegendre with W=0, N=1 gives point=0, weight=1 (special case, line 375-378 of ttban_exact.f).
  • SUBANAx parses DEF EG2 = 4.000 UNITY by finding the keyword, scanning backwards for the numeric coefficient, and multiplying by the keyword's value (UNITY=1.0, DEL=delta, UCV=Ucv, UVV=Uvv).

Track C Phase 5 — scoping decision at C3d (recorded 2026-04-11, commit 42a0e30)

Where we are: C3-pre, C3a, C3b, C3c, C3d landed. Next up is C3e (build_cowan_store_in_memory).

The decision point at C3d: build_rac_in_memory is implemented as a loader that parses .rme_rac + .rme_rcg fixture paths and derives the SectionPlan from the parsed COWAN store. It is not a from-scratch generator. The reason this is the right scoping:

  1. The ADD entry coefficients in .rme_rac are pure CFP/Wigner-3j/6j angular products generated by Fortran ttrac.c (~7000 lines). Reproducing them in PyTorch is a massive port and is orthogonal to the autograd story.
  2. The Track C autograd path routes through the atomic-parameter scalars (slater_scale, soc_scale, cf, delta, lmct, mlct) that multiply into the COWAN store tensors C3e will build — not through the ADD coefficients themselves. ADD coefficients are constants regardless of physics inputs.
  3. build_rac_in_memory exposes source_rac_path / source_rcg_path kwargs as the explicit extension point for a future ttrac port. The SectionPlan contract C3e consumes is identical either way, so a from-scratch generator can be slotted in later without changing C3e, C3f, C4, or any consumer.

If something goes sideways from here, come back to this point and re-read:

  • multitorch/hamiltonian/build_rac.py (the loader-based implementation
    • module docstring explaining the scoping)
  • tests/test_hamiltonian/test_build_rac.py::test_in_memory_builder_round_trips_through_assembler (the contract test that locks in the C3d → assembler boundary)
  • .claude/orchestration/TRACK-C-PHASE5.md row C3d (status table)
  • This section of CLAUDE.md (the "why we punted on the ttrac port" rationale)

The C3e step that follows must produce a COWAN store whose section counts and per-slot shapes match the SectionPlan, with each tensor built from the existing torch wrappers in multitorch/angular/torch_blocks.py (C3a) multiplied by the scaled atomic parameters from multitorch/atomic/scaled_params.py (C3c). Element-wise parity against the parsed COWAN store at 1e-6 is the success criterion. C3e is the actual angular reconstruction work; C3d is just the contract.

Do not

  • Do not modify reference data in tests/reference_data/
  • Do not add features beyond what's needed to match Fortran output
  • Do not refactor code that already works and passes tests
  • Do not commit to git without explicit user request