Skip to main content

midas-index

Pure-Python/PyTorch FF-HEDM indexer. Drop-in replacement for IndexerOMP / IndexerGPU from MIDAS, with seamless CPU / CUDA / MPS device switching.

Status: v0.4.x — production. Bit-identical to C IndexerOMP on the 500/500 seed FF parity gate, plus the new scanning indexer that matches C IndexerScanningOMP on the 1-voxel PF parity gate. Auto dense ↔ jagged compare_spots strategy picker (see midas_index.compute.matching.pick_compare_strategy) keeps GPU runs inside the available memory budget without OOM. Detailed design doc lives in dev/implementation_plan.md (gitignored).

Install

pip install midas-index

For local development:

cd packages/midas_index
pip install -e .[dev]

Notebooks

Worked-example Jupyter notebooks live in notebooks/. They are not shipped with pip install — get them by cloning the MIDAS repository.

Quick start

# CLI — drop-in for IndexerOMP / IndexerGPU
midas-index paramstest.txt 0 1 1000 8

# Pin device / dtype via env vars (auto-detect: CUDA -> MPS -> CPU)
MIDAS_INDEX_DEVICE=cuda MIDAS_INDEX_DTYPE=float32 \
    midas-index paramstest.txt 0 1 1000 8

Library API:

from midas_index import Indexer

result = Indexer.from_param_file("paramstest.txt", device="cuda").run(
    block_nr=0, n_blocks=1, n_spots_to_index=1000,
)

C backend (midas_indexer)

A bundled C binary, midas_indexer, ships alongside the Python+numba path. It supersedes the legacy MIDAS IndexerOMP (FF) and IndexerScanningOMP (PF) binaries with one unified algorithm: PF subsumes FF as the nScans=1 specialization, Friedel-pair plane normals and 3D per-seed position search activate when nScans=1, scan-position filter activates when nScans>1. Output is always consolidated (IndexBest_all.bin + IndexKey_all.bin + IndexBest_IDs_all.bin) + a Phase 8 sidecar IndexBest_weights_all.bin carrying per-match soft-attribution weights.

Build: scikit-build-core compiles c_src/IndexerUnified.c at pip install time and installs the binary at <site-packages>/midas_index/bin/midas_indexer. Requires OpenMP. On macOS install libomp first (brew install libomp); on Linux gcc/clang with libgomp suffices. If OpenMP is missing the install still succeeds — only the Python path is available, and backend_c.available() returns False.

Use from the library

from midas_index import Indexer

ind = Indexer.from_param_file("paramstest.txt")
ind.run_scanning(
    scan_positions=positions, out_path="Output/IndexBest_all.bin",
    backend="c-omp",         # "python" (default) | "c-omp"
    num_procs=8,
)

Indexer.run() takes the same backend= kwarg. paramstest_path is required when backend="c-omp" unless the Indexer was constructed via from_param_file (which captures the path).

Building the spot table yourself (obs, Spots.bin)

The pipeline writes Spots.bin for you. A bridge that feeds midas_index spots from any other source must fill the same columns, and one of them fails silently when it is wrong:

col quantity unit
0, 1 y, z on the detector, lab frame µm
2 ω deg
3 ring radius (the radial pass compares it with the ring's reference radius) µm
4 spot id —
5 ring number —
6 η deg
7 2θ (as the package's own tests build it) deg
8 radial displacement from the ring: observed radius − ring radius µm
9 scan number (unified / pf Spots.bin only) —

Column 8 is not intensity and not the ring radius. A candidate is accepted only if |theor[..., 13] − obs[..., 8]| < MarginRadial, where theoretical column 13 is rad_diff = sqrt(yl_disp² + zl_disp²) − ring_radius (compute/forward_numba.py, compute/matching.py, compute/seeds.py). A bridge that packed spot intensity there (1000.0 for every spot) made every candidate fail the radial test, and "midas_index fails on this sample" stood until a 120-spot synthetic positive control found it; filling column 8 from the prediction's rad_diff took that control from 0/4 to 1/2 matched (La3Ni2O7 project, 2026-08-20). Validate any bridge on spots simulated from a known grain before blaming the indexer.

Use from midas-pipeline

midas-pipeline run --indexer-backend c-omp ...

Soft beam attribution

Set in paramstest.txt:

SoftAttrMode      gaussian   # none | top_hat | gaussian
SoftAttrFwhm      2.5        # FWHM in µm
SoftAttrTruncate  6.0        # gaussian tail cut (µm); 0 = unbounded
SoftAttrFalloff   1.0        # top-hat edge ramp (µm); 0 = strict

Mode none (default) preserves the legacy ScanPosTol hard window bit-identically. Modes top_hat and gaussian widen the candidate window and emit per-match weights into the IndexBest_weights_all.bin sidecar (1.0 weights for mode none so downstream code can rely on the file's presence).

Drive from ff_MIDAS.py

Pass -useTorchIndexer 1 to switch the indexing stage from C IndexerOMP / IndexerGPU to this package:

python ff_MIDAS.py -paramFN paramstest.txt … -useTorchIndexer 1

Architecture

midas-index is a thin orchestration layer. Heavy lifting is delegated to:

  • midas-diffract — forward simulation (HKL -> theoretical spots).
  • midas-stress — orientation conversions, symmetry, fundamental zone.

This package itself owns: seed enumeration, orientation / position grid layout, binned matching, scoring, I/O, and the CLI / library API.

Benchmark

A bundled benchmark drives the full per-seed pipeline end-to-end:

python -m midas_index.benchmarks.bench_seed --n-grains 5 --n-iter 3

License

BSD-3-Clause. Part of MIDAS.

Metadata

Release files for midas-index 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for midas-index 0.10.0
File Size Uploaded
midas_index-0.10.0.tar.gz 921.0 kB Details

Release files / midas_index-0.10.0.tar.gz

Download URL midas_index-0.10.0.tar.gz
Size 921.0 kB
Tags Source
SHA-256 checksum
How to use checksums
ad9c256e3322ff99245cd0787dec1cab59b7c18fc29473378780c8f9864e58e3
BLAKE2b-256 checksum
How to use checksums
78baa39c4c3f8a9110b8030051b706eef90f27a34c27e94f591f8752e09bbd2b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.10.0 This release

1 release file

0.9.0

1 release file

0.8.0

1 release file

0.7.9

1 release file

0.7.8

1 release file

0.7.7

1 release file

0.7.6

1 release file

0.7.5

1 release file

0.7.4

1 release file

0.7.3

1 release file

0.7.2

1 release file

0.7.1

1 release file

0.7.0

1 release file

0.6.0

1 release file

0.5.23

1 release file

0.5.2

1 release file

0.5.1

1 release file

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page