midas-index 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. midas_index-0.1.0/PKG-INFO +97 -0
  2. midas_index-0.1.0/README.md +72 -0
  3. midas_index-0.1.0/midas_index/__init__.py +32 -0
  4. midas_index-0.1.0/midas_index/__main__.py +6 -0
  5. midas_index-0.1.0/midas_index/benchmarks/__init__.py +0 -0
  6. midas_index-0.1.0/midas_index/benchmarks/bench_seed.py +183 -0
  7. midas_index-0.1.0/midas_index/cli.py +99 -0
  8. midas_index-0.1.0/midas_index/compute/__init__.py +32 -0
  9. midas_index-0.1.0/midas_index/compute/binning.py +48 -0
  10. midas_index-0.1.0/midas_index/compute/constants.py +30 -0
  11. midas_index-0.1.0/midas_index/compute/forward_adapter.py +252 -0
  12. midas_index-0.1.0/midas_index/compute/matching.py +328 -0
  13. midas_index-0.1.0/midas_index/compute/orientation_grid.py +74 -0
  14. midas_index-0.1.0/midas_index/compute/position_grid.py +173 -0
  15. midas_index-0.1.0/midas_index/compute/reduce.py +37 -0
  16. midas_index-0.1.0/midas_index/compute/rotation.py +121 -0
  17. midas_index-0.1.0/midas_index/compute/seeds.py +560 -0
  18. midas_index-0.1.0/midas_index/device.py +62 -0
  19. midas_index-0.1.0/midas_index/indexer.py +146 -0
  20. midas_index-0.1.0/midas_index/io/__init__.py +32 -0
  21. midas_index-0.1.0/midas_index/io/binary.py +78 -0
  22. midas_index-0.1.0/midas_index/io/bins_builder.py +142 -0
  23. midas_index-0.1.0/midas_index/io/consolidated.py +9 -0
  24. midas_index-0.1.0/midas_index/io/csv.py +219 -0
  25. midas_index-0.1.0/midas_index/io/output.py +180 -0
  26. midas_index-0.1.0/midas_index/io/params.py +134 -0
  27. midas_index-0.1.0/midas_index/params.py +76 -0
  28. midas_index-0.1.0/midas_index/pipeline.py +336 -0
  29. midas_index-0.1.0/midas_index/result.py +42 -0
  30. midas_index-0.1.0/midas_index.egg-info/PKG-INFO +97 -0
  31. midas_index-0.1.0/midas_index.egg-info/SOURCES.txt +53 -0
  32. midas_index-0.1.0/midas_index.egg-info/dependency_links.txt +1 -0
  33. midas_index-0.1.0/midas_index.egg-info/entry_points.txt +2 -0
  34. midas_index-0.1.0/midas_index.egg-info/requires.txt +9 -0
  35. midas_index-0.1.0/midas_index.egg-info/top_level.txt +1 -0
  36. midas_index-0.1.0/pyproject.toml +52 -0
  37. midas_index-0.1.0/setup.cfg +4 -0
  38. midas_index-0.1.0/tests/test_compute_binning.py +37 -0
  39. midas_index-0.1.0/tests/test_compute_matching.py +275 -0
  40. midas_index-0.1.0/tests/test_compute_orientation_grid.py +49 -0
  41. midas_index-0.1.0/tests/test_compute_position_grid.py +73 -0
  42. midas_index-0.1.0/tests/test_compute_reduce.py +33 -0
  43. midas_index-0.1.0/tests/test_compute_rotation.py +59 -0
  44. midas_index-0.1.0/tests/test_compute_seeds.py +97 -0
  45. midas_index-0.1.0/tests/test_devices.py +109 -0
  46. midas_index-0.1.0/tests/test_forward_adapter.py +129 -0
  47. midas_index-0.1.0/tests/test_io_binary.py +58 -0
  48. midas_index-0.1.0/tests/test_io_bins_builder.py +129 -0
  49. midas_index-0.1.0/tests/test_io_csv.py +135 -0
  50. midas_index-0.1.0/tests/test_io_output.py +133 -0
  51. midas_index-0.1.0/tests/test_io_params.py +132 -0
  52. midas_index-0.1.0/tests/test_pipeline_e2e.py +109 -0
  53. midas_index-0.1.0/tests/test_regression_vs_c.py +99 -0
  54. midas_index-0.1.0/tests/test_roundtrip_diffract.py +199 -0
  55. midas_index-0.1.0/tests/test_smoke.py +48 -0
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: midas-index
3
+ Version: 0.1.0
4
+ Summary: Pure-Python/PyTorch FF-HEDM indexer (drop-in replacement for IndexerOMP/IndexerGPU)
5
+ Author-email: Hemant Sharma <hsharma@anl.gov>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/marinerhemant/MIDAS
8
+ Project-URL: Documentation, https://github.com/marinerhemant/MIDAS/tree/master/packages/midas_index
9
+ Project-URL: Issues, https://github.com/marinerhemant/MIDAS/issues
10
+ Keywords: MIDAS,HEDM,indexing,PyTorch,far-field,diffraction,crystallography,polycrystal,grain
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Scientific/Engineering :: Physics
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: numpy>=1.22
18
+ Requires-Dist: torch>=2.0
19
+ Requires-Dist: midas-diffract>=0.1.0
20
+ Requires-Dist: midas-stress<1.0,>=0.5.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == "dev"
23
+ Requires-Dist: pytest-cov; extra == "dev"
24
+ Requires-Dist: pytest-benchmark; extra == "dev"
25
+
26
+ # midas-index
27
+
28
+ Pure-Python/PyTorch FF-HEDM indexer. Drop-in replacement for `IndexerOMP` /
29
+ `IndexerGPU` from MIDAS, with seamless CPU / CUDA / MPS device switching.
30
+
31
+ **Status:** v0.1.0 in development. Detailed design doc lives in
32
+ `dev/implementation_plan.md` (gitignored).
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ pip install midas-index
38
+ ```
39
+
40
+ For local development:
41
+
42
+ ```bash
43
+ cd packages/midas_index
44
+ pip install -e .[dev]
45
+ ```
46
+
47
+ ## Quick start
48
+
49
+ ```bash
50
+ # CLI — drop-in for IndexerOMP / IndexerGPU
51
+ midas-index paramstest.txt 0 1 1000 8
52
+
53
+ # Pin device / dtype via env vars (auto-detect: CUDA -> MPS -> CPU)
54
+ MIDAS_INDEX_DEVICE=cuda MIDAS_INDEX_DTYPE=float32 \
55
+ midas-index paramstest.txt 0 1 1000 8
56
+ ```
57
+
58
+ Library API:
59
+
60
+ ```python
61
+ from midas_index import Indexer
62
+
63
+ result = Indexer.from_param_file("paramstest.txt", device="cuda").run(
64
+ block_nr=0, n_blocks=1, n_spots_to_index=1000,
65
+ )
66
+ ```
67
+
68
+ ## Drive from `ff_MIDAS.py`
69
+
70
+ Pass `-useTorchIndexer 1` to switch the indexing stage from C `IndexerOMP` /
71
+ `IndexerGPU` to this package:
72
+
73
+ ```bash
74
+ python ff_MIDAS.py -paramFN paramstest.txt … -useTorchIndexer 1
75
+ ```
76
+
77
+ ## Architecture
78
+
79
+ `midas-index` is a thin orchestration layer. Heavy lifting is delegated to:
80
+
81
+ - [`midas-diffract`](../midas_diffract/) — forward simulation (HKL -> theoretical spots).
82
+ - [`midas-stress`](../midas_stress/) — orientation conversions, symmetry, fundamental zone.
83
+
84
+ This package itself owns: seed enumeration, orientation / position grid layout,
85
+ binned matching, scoring, I/O, and the CLI / library API.
86
+
87
+ ## Benchmark
88
+
89
+ A bundled benchmark drives the full per-seed pipeline end-to-end:
90
+
91
+ ```bash
92
+ python -m midas_index.benchmarks.bench_seed --n-grains 5 --n-iter 3
93
+ ```
94
+
95
+ ## License
96
+
97
+ BSD-3-Clause. Part of [MIDAS](https://github.com/marinerhemant/MIDAS).
@@ -0,0 +1,72 @@
1
+ # midas-index
2
+
3
+ Pure-Python/PyTorch FF-HEDM indexer. Drop-in replacement for `IndexerOMP` /
4
+ `IndexerGPU` from MIDAS, with seamless CPU / CUDA / MPS device switching.
5
+
6
+ **Status:** v0.1.0 in development. Detailed design doc lives in
7
+ `dev/implementation_plan.md` (gitignored).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install midas-index
13
+ ```
14
+
15
+ For local development:
16
+
17
+ ```bash
18
+ cd packages/midas_index
19
+ pip install -e .[dev]
20
+ ```
21
+
22
+ ## Quick start
23
+
24
+ ```bash
25
+ # CLI — drop-in for IndexerOMP / IndexerGPU
26
+ midas-index paramstest.txt 0 1 1000 8
27
+
28
+ # Pin device / dtype via env vars (auto-detect: CUDA -> MPS -> CPU)
29
+ MIDAS_INDEX_DEVICE=cuda MIDAS_INDEX_DTYPE=float32 \
30
+ midas-index paramstest.txt 0 1 1000 8
31
+ ```
32
+
33
+ Library API:
34
+
35
+ ```python
36
+ from midas_index import Indexer
37
+
38
+ result = Indexer.from_param_file("paramstest.txt", device="cuda").run(
39
+ block_nr=0, n_blocks=1, n_spots_to_index=1000,
40
+ )
41
+ ```
42
+
43
+ ## Drive from `ff_MIDAS.py`
44
+
45
+ Pass `-useTorchIndexer 1` to switch the indexing stage from C `IndexerOMP` /
46
+ `IndexerGPU` to this package:
47
+
48
+ ```bash
49
+ python ff_MIDAS.py -paramFN paramstest.txt … -useTorchIndexer 1
50
+ ```
51
+
52
+ ## Architecture
53
+
54
+ `midas-index` is a thin orchestration layer. Heavy lifting is delegated to:
55
+
56
+ - [`midas-diffract`](../midas_diffract/) — forward simulation (HKL -> theoretical spots).
57
+ - [`midas-stress`](../midas_stress/) — orientation conversions, symmetry, fundamental zone.
58
+
59
+ This package itself owns: seed enumeration, orientation / position grid layout,
60
+ binned matching, scoring, I/O, and the CLI / library API.
61
+
62
+ ## Benchmark
63
+
64
+ A bundled benchmark drives the full per-seed pipeline end-to-end:
65
+
66
+ ```bash
67
+ python -m midas_index.benchmarks.bench_seed --n-grains 5 --n-iter 3
68
+ ```
69
+
70
+ ## License
71
+
72
+ BSD-3-Clause. Part of [MIDAS](https://github.com/marinerhemant/MIDAS).
@@ -0,0 +1,32 @@
1
+ """midas-index: Pure-Python/PyTorch FF-HEDM indexer.
2
+
3
+ Drop-in replacement for the C binaries `IndexerOMP` and `IndexerGPU` from
4
+ MIDAS, with seamless CPU/CUDA/MPS device switching.
5
+
6
+ See `dev/implementation_plan.md` (gitignored) for design and roadmap.
7
+
8
+ Quick start
9
+ -----------
10
+ # CLI
11
+ midas-index paramstest.txt 0 1 1000 8
12
+
13
+ # Library
14
+ from midas_index import Indexer, IndexerParams
15
+ result = Indexer.from_param_file("paramstest.txt", device="cuda").run(
16
+ block_nr=0, n_blocks=1, n_spots_to_index=1000,
17
+ )
18
+ """
19
+
20
+ __version__ = "0.1.0"
21
+
22
+ from .params import IndexerParams
23
+ from .result import IndexerResult, SeedResult
24
+ from .indexer import Indexer
25
+
26
+ __all__ = [
27
+ "Indexer",
28
+ "IndexerParams",
29
+ "IndexerResult",
30
+ "SeedResult",
31
+ "__version__",
32
+ ]
@@ -0,0 +1,6 @@
1
+ """Allow `python -m midas_index ...` to invoke the CLI."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
File without changes
@@ -0,0 +1,183 @@
1
+ """Per-seed throughput benchmark for `process_seed`.
2
+
3
+ Drives the full forward + match pipeline on a synthetic 5-grain dataset
4
+ (generated in-memory via `IndexerForwardAdapter`) and reports seeds/sec.
5
+
6
+ Useful for validating perf changes (e.g. the (y0,z0) cartesian-batch
7
+ vectorization in pipeline.py).
8
+
9
+ Usage:
10
+ python -m midas_index.benchmarks.bench_seed --n-grains 5 --n-iter 3
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import math
17
+ import sys
18
+ import time
19
+
20
+ import numpy as np
21
+ import torch
22
+
23
+ from midas_index import IndexerParams
24
+ from midas_index.compute.forward_adapter import IndexerForwardAdapter
25
+ from midas_index.io import build_bin_index
26
+ from midas_index.pipeline import IndexerContext, process_seed
27
+
28
+
29
+ def _toy_params() -> IndexerParams:
30
+ p = IndexerParams()
31
+ p.Distance = 1_000_000.0
32
+ p.Wavelength = 0.172979
33
+ p.Rsample = 250.0
34
+ p.Hbeam = 200.0
35
+ p.px = 200.0
36
+ p.SpaceGroup = 225
37
+ p.LatticeConstant = (4.08, 4.08, 4.08, 90.0, 90.0, 90.0)
38
+ p.StepsizePos = 100.0
39
+ p.StepsizeOrient = 0.5
40
+ p.MarginOme = 0.5
41
+ p.MarginRad = 500.0
42
+ p.MarginRadial = 500.0
43
+ p.MarginEta = 500.0
44
+ p.EtaBinSize = 0.1
45
+ p.OmeBinSize = 0.1
46
+ p.ExcludePoleAngle = 6.0
47
+ p.MinMatchesToAcceptFrac = 0.1
48
+ p.RingNumbers = [1, 2, 3, 4]
49
+ p.RingRadii = {
50
+ 1: 73582.31550724161,
51
+ 2: 85023.04143552633,
52
+ 3: 120567.4304605822,
53
+ 4: 141666.90427076322,
54
+ }
55
+ p.OmegaRanges = [(-180.0, 180.0)]
56
+ p.BoxSizes = [(-2_000_000.0, 2_000_000.0, -2_000_000.0, 2_000_000.0)]
57
+ p.UseFriedelPairs = 0
58
+ p.OutputFolder = "."
59
+ return p
60
+
61
+
62
+ def _toy_hkls():
63
+ a = 4.08
64
+ one_over_a = 1.0 / a
65
+ wl = 0.172979
66
+ hkls = []
67
+ int_rows = []
68
+ real_rows = []
69
+ for h, k, l, ring in [
70
+ (1, -1, -1, 1), (1, 1, 1, 1), (-1, 1, 1, 1), (1, -1, 1, 1),
71
+ (-1, -1, 1, 1), (-1, 1, -1, 1), (1, 1, -1, 1), (-1, -1, -1, 1),
72
+ (2, 0, 0, 2), (-2, 0, 0, 2), (0, 2, 0, 2), (0, -2, 0, 2),
73
+ (0, 0, 2, 2), (0, 0, -2, 2),
74
+ ]:
75
+ g = (h * one_over_a, k * one_over_a, l * one_over_a)
76
+ d = 1.0 / math.sqrt(sum(x * x for x in g))
77
+ sin_th = wl / (2.0 * d)
78
+ if not 0 < sin_th <= 1.0:
79
+ continue
80
+ th_rad = math.asin(sin_th)
81
+ radius = 73582.31550724161 if ring == 1 else 85023.04143552633
82
+ real_rows.append((g[0], g[1], g[2], float(ring), d, th_rad, radius))
83
+ int_rows.append((h, k, l, ring))
84
+ return (
85
+ np.asarray(real_rows, dtype=np.float64),
86
+ np.asarray(int_rows, dtype=np.int64),
87
+ )
88
+
89
+
90
+ def _build_dataset(n_grains: int, seed: int):
91
+ rng = np.random.default_rng(seed)
92
+ q = rng.normal(size=(n_grains, 4))
93
+ q /= np.linalg.norm(q, axis=1, keepdims=True)
94
+ w, x, y, z = q[:, 0], q[:, 1], q[:, 2], q[:, 3]
95
+ R = np.empty((n_grains, 3, 3))
96
+ R[:, 0, 0] = 1 - 2 * (y * y + z * z)
97
+ R[:, 0, 1] = 2 * (x * y - w * z)
98
+ R[:, 0, 2] = 2 * (x * z + w * y)
99
+ R[:, 1, 0] = 2 * (x * y + w * z)
100
+ R[:, 1, 1] = 1 - 2 * (x * x + z * z)
101
+ R[:, 1, 2] = 2 * (y * z - w * x)
102
+ R[:, 2, 0] = 2 * (x * z - w * y)
103
+ R[:, 2, 1] = 2 * (y * z + w * x)
104
+ R[:, 2, 2] = 1 - 2 * (x * x + y * y)
105
+ return torch.as_tensor(R, dtype=torch.float64)
106
+
107
+
108
+ def main() -> int:
109
+ parser = argparse.ArgumentParser()
110
+ parser.add_argument("--n-grains", type=int, default=5)
111
+ parser.add_argument("--n-iter", type=int, default=3,
112
+ help="Iterations of process_seed to time")
113
+ parser.add_argument("--seed", type=int, default=42)
114
+ args = parser.parse_args()
115
+
116
+ params = _toy_params()
117
+ hkls_real_np, hkls_int_np = _toy_hkls()
118
+ R = _build_dataset(args.n_grains, args.seed)
119
+
120
+ # Forward-sim obs via the adapter
121
+ adapter = IndexerForwardAdapter(
122
+ params=params,
123
+ hkls_real=torch.as_tensor(hkls_real_np, dtype=torch.float64),
124
+ hkls_int=torch.as_tensor(hkls_int_np, dtype=torch.long),
125
+ device=torch.device("cpu"),
126
+ dtype=torch.float64,
127
+ )
128
+ pos = torch.zeros(R.shape[0], 3, dtype=torch.float64)
129
+ theor, valid = adapter.simulate(R, pos)
130
+
131
+ rows = []
132
+ spot_id = 1
133
+ for g in range(theor.shape[0]):
134
+ for k in range(theor.shape[1]):
135
+ if not bool(valid[g, k]):
136
+ continue
137
+ y = float(theor[g, k, 10])
138
+ z = float(theor[g, k, 11])
139
+ rows.append([
140
+ y, z, float(theor[g, k, 6]), math.sqrt(y * y + z * z),
141
+ float(spot_id), float(theor[g, k, 9]),
142
+ float(theor[g, k, 7]), float(theor[g, k, 8]) * 2.0,
143
+ float(theor[g, k, 13]),
144
+ ])
145
+ spot_id += 1
146
+ obs = np.asarray(rows, dtype=np.float64)
147
+ bin_data, bin_ndata = build_bin_index(
148
+ obs, eta_bin_size=0.1, ome_bin_size=0.1, n_rings=4,
149
+ margin_eta=params.MarginEta, margin_ome=params.MarginOme,
150
+ stepsize_orient=params.StepsizeOrient,
151
+ ring_radii=params.RingRadii,
152
+ )
153
+
154
+ ctx = IndexerContext(
155
+ params=params, hkls_real=hkls_real_np, hkls_int=hkls_int_np,
156
+ obs=obs, bin_data=bin_data, bin_ndata=bin_ndata,
157
+ device=torch.device("cpu"), dtype=torch.float64,
158
+ )
159
+
160
+ spot_ids = obs[:, 4].astype(int)
161
+ print(f"benchmark: {args.n_grains} grains -> {len(obs)} obs spots")
162
+ print(f" {len(spot_ids)} candidate seed spots")
163
+
164
+ # Warm up
165
+ process_seed(int(spot_ids[0]), ctx)
166
+
167
+ # Time process_seed across a sample
168
+ sample = spot_ids[: min(args.n_grains, len(spot_ids))]
169
+ timings = []
170
+ for _ in range(args.n_iter):
171
+ t0 = time.perf_counter()
172
+ for sid in sample:
173
+ process_seed(int(sid), ctx)
174
+ timings.append(time.perf_counter() - t0)
175
+ n_seeds = len(sample)
176
+ best = min(timings)
177
+ print(f"\n best of {args.n_iter}: {best:.3f}s for {n_seeds} seeds")
178
+ print(f" -> {best / n_seeds * 1000:.1f} ms/seed, {n_seeds / best:.1f} seeds/sec")
179
+ return 0
180
+
181
+
182
+ if __name__ == "__main__":
183
+ sys.exit(main())
@@ -0,0 +1,99 @@
1
+ """CLI entry point for midas-index.
2
+
3
+ Drop-in replacement for the IndexerOMP / IndexerGPU positional argv:
4
+
5
+ midas-index <param_file> <block_nr> <n_blocks> <n_spots_to_index> <num_procs>
6
+
7
+ Optional flags (extension over C binaries):
8
+
9
+ --device {cpu,cuda,mps} override auto-detection (env: MIDAS_INDEX_DEVICE)
10
+ --dtype {float32,float64} override per-device default (env: MIDAS_INDEX_DTYPE)
11
+ --version print version and exit
12
+
13
+ Behaviour mirrors `FF_HEDM/src/IndexerOMP.c::main` exactly:
14
+ - Reads <param_file> for all 33 paramstest.txt keys.
15
+ - Computes [startRowNr, endRowNr] from (block_nr, n_blocks, n_spots_to_index).
16
+ - Writes BestPos_<block_nr>.csv plus consolidated binaries to OutputFolder.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+ import sys
23
+
24
+ from . import __version__
25
+
26
+
27
+ def _build_parser() -> argparse.ArgumentParser:
28
+ parser = argparse.ArgumentParser(
29
+ prog="midas-index",
30
+ description="Pure-Python/PyTorch FF-HEDM indexer (drop-in for IndexerOMP/IndexerGPU).",
31
+ )
32
+ parser.add_argument("param_file", help="Path to paramstest.txt")
33
+ parser.add_argument("block_nr", type=int, help="Block index for sharded run")
34
+ parser.add_argument("n_blocks", type=int, help="Total number of blocks")
35
+ parser.add_argument(
36
+ "n_spots_to_index", type=int, help="Total number of seed spots to process"
37
+ )
38
+ parser.add_argument(
39
+ "num_procs",
40
+ type=int,
41
+ help="Threads on CPU (passed to torch.set_num_threads); ignored on GPU/MPS",
42
+ )
43
+ parser.add_argument("--device", choices=["cpu", "cuda", "mps"], default=None)
44
+ parser.add_argument("--dtype", choices=["float32", "float64"], default=None)
45
+ parser.add_argument("--version", action="version", version=f"midas-index {__version__}")
46
+ return parser
47
+
48
+
49
+ def main(argv: list[str] | None = None) -> int:
50
+ """CLI entry point. Returns process exit code."""
51
+ parser = _build_parser()
52
+ args = parser.parse_args(argv)
53
+
54
+ from .indexer import Indexer
55
+ from .io.output import close_output_files, open_output_files, write_seed_record
56
+
57
+ indexer = Indexer.from_param_file(
58
+ args.param_file, device=args.device, dtype=args.dtype,
59
+ )
60
+ indexer.load_observations()
61
+ obs = indexer._observations
62
+ assert obs is not None
63
+
64
+ spot_ids = obs["spot_ids"]
65
+ n_total = int(min(args.n_spots_to_index, len(spot_ids)))
66
+
67
+ result = indexer.run(
68
+ block_nr=args.block_nr,
69
+ n_blocks=args.n_blocks,
70
+ n_spots_to_index=n_total,
71
+ num_procs=args.num_procs,
72
+ )
73
+
74
+ # Build a spot_id -> offset map (offset is the row in SpotsToIndex.csv).
75
+ sid_to_offset: dict[int, int] = {
76
+ int(sid): i for i, sid in enumerate(spot_ids[:n_total].tolist())
77
+ }
78
+
79
+ output_folder = indexer.params.OutputFolder
80
+ fd_best, fd_full = open_output_files(output_folder, n_total, args.block_nr)
81
+ try:
82
+ for seed in result.seeds:
83
+ offset = sid_to_offset.get(int(seed.spot_id), -1)
84
+ if offset < 0:
85
+ continue
86
+ write_seed_record(fd_best, seed, offset)
87
+ finally:
88
+ close_output_files(fd_best, fd_full)
89
+
90
+ print(
91
+ f"midas-index {__version__}: block {args.block_nr}/{args.n_blocks} "
92
+ f"completed. {len(result.seeds)} seeds indexed -> {output_folder}/IndexBest.bin",
93
+ file=sys.stderr,
94
+ )
95
+ return 0
96
+
97
+
98
+ if __name__ == "__main__":
99
+ sys.exit(main())
@@ -0,0 +1,32 @@
1
+ """Compute kernels for midas-index.
2
+
3
+ Most heavy lifting is delegated:
4
+ - Forward simulation -> midas-diffract.HEDMForwardModel (forward_adapter.py)
5
+ - Orientation conversions -> midas-stress.orientation (rotation.py shim)
6
+
7
+ Owned here: seed enumeration, orientation/position grids, binned matching, scoring.
8
+ """
9
+
10
+ from . import (
11
+ binning,
12
+ constants,
13
+ forward_adapter,
14
+ matching,
15
+ orientation_grid,
16
+ position_grid,
17
+ reduce,
18
+ rotation,
19
+ seeds,
20
+ )
21
+
22
+ __all__ = [
23
+ "binning",
24
+ "constants",
25
+ "forward_adapter",
26
+ "matching",
27
+ "orientation_grid",
28
+ "position_grid",
29
+ "reduce",
30
+ "rotation",
31
+ "seeds",
32
+ ]
@@ -0,0 +1,48 @@
1
+ """Binned-spot index lookups.
2
+
3
+ Mirrors `GetBin` from `FF_HEDM/src/IndexerOMP.c:115`. Given (ring_nr, eta,
4
+ omega) of a theoretical spot, computes a flat bin index and gathers the
5
+ candidate observed-spot row IDs from `data`/`ndata` arrays.
6
+
7
+ Vectorized: works on arbitrary leading-dim tensors of theoretical spots and
8
+ returns (n_in_bin, data_offset) per element. The actual gather of candidate
9
+ rows is left to `compare_spots` (it depends on the matching strategy —
10
+ dense vs. jagged).
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import torch
16
+
17
+
18
+ def get_bin_indices(
19
+ ring_nr: torch.Tensor, # int (..)
20
+ eta_deg: torch.Tensor, # float (..)
21
+ omega_deg: torch.Tensor, # float (..)
22
+ eta_bin_size: float,
23
+ ome_bin_size: float,
24
+ n_eta_bins: int,
25
+ n_ome_bins: int,
26
+ ) -> torch.Tensor:
27
+ """Compute flat bin indices `pos = (ring-1) * (n_eta * n_ome) + iEta * n_ome + iOme`.
28
+
29
+ Mirrors C: `iEta = floor((180 + eta) / EtaBinSize)`, `iOme` likewise.
30
+ """
31
+ i_ring = (ring_nr.to(torch.int64) - 1)
32
+ i_eta = torch.floor((180.0 + eta_deg) / eta_bin_size).to(torch.int64)
33
+ i_ome = torch.floor((180.0 + omega_deg) / ome_bin_size).to(torch.int64)
34
+ return i_ring * (n_eta_bins * n_ome_bins) + i_eta * n_ome_bins + i_ome
35
+
36
+
37
+ def lookup_bin_counts(
38
+ pos: torch.Tensor, # int64 (..,)
39
+ ndata: torch.Tensor, # int32 (2*n_bins,)
40
+ ) -> tuple[torch.Tensor, torch.Tensor]:
41
+ """Return (n_in_bin, data_offset) for each pos.
42
+
43
+ `ndata` layout is interleaved: `[count_0, offset_0, count_1, offset_1, ...]`.
44
+ """
45
+ pos = pos.to(torch.int64)
46
+ n_per = ndata[pos * 2].to(torch.int64)
47
+ offset = ndata[pos * 2 + 1].to(torch.int64)
48
+ return n_per, offset
@@ -0,0 +1,30 @@
1
+ """Compile-time constants from `FF_HEDM/src/IndexerOMP.c` / `IndexerGPU.cu`.
2
+
3
+ In Python these are advisory caps + bookkeeping (column counts).
4
+ Buffer sizes are determined dynamically at runtime, not from these.
5
+ """
6
+
7
+ # --- Bookkeeping caps (matches C; advisory only) ---
8
+ MAX_N_SPOTS = 100_000_000
9
+ MAX_N_RINGS = 500
10
+ MAX_N_HKLS = 5000
11
+ MAX_N_STEPS = 2000
12
+ MAX_N_OMEGARANGES = 2000
13
+ MAX_N_OR_CPU = 36_000 # IndexerOMP.c
14
+ MAX_N_OR_GPU = 7_200 # IndexerGPU.cu
15
+
16
+ # --- Column counts of legacy flat layouts ---
17
+ N_COL_THEORSPOTS = 14 # TheorSpots row layout
18
+ N_COL_OBSSPOTS = 9 # Spots.bin row layout
19
+ N_COL_GRAINSPOTS = 17
20
+ N_COL_GRAINMATCHES = 16
21
+
22
+ # --- Numerical ---
23
+ EPS_F32 = 1e-9
24
+ EPS_F64 = 1e-12
25
+
26
+ # --- Conversions ---
27
+ import math
28
+
29
+ DEG2RAD = math.pi / 180.0
30
+ RAD2DEG = 180.0 / math.pi