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.
- midas_index-0.1.0/PKG-INFO +97 -0
- midas_index-0.1.0/README.md +72 -0
- midas_index-0.1.0/midas_index/__init__.py +32 -0
- midas_index-0.1.0/midas_index/__main__.py +6 -0
- midas_index-0.1.0/midas_index/benchmarks/__init__.py +0 -0
- midas_index-0.1.0/midas_index/benchmarks/bench_seed.py +183 -0
- midas_index-0.1.0/midas_index/cli.py +99 -0
- midas_index-0.1.0/midas_index/compute/__init__.py +32 -0
- midas_index-0.1.0/midas_index/compute/binning.py +48 -0
- midas_index-0.1.0/midas_index/compute/constants.py +30 -0
- midas_index-0.1.0/midas_index/compute/forward_adapter.py +252 -0
- midas_index-0.1.0/midas_index/compute/matching.py +328 -0
- midas_index-0.1.0/midas_index/compute/orientation_grid.py +74 -0
- midas_index-0.1.0/midas_index/compute/position_grid.py +173 -0
- midas_index-0.1.0/midas_index/compute/reduce.py +37 -0
- midas_index-0.1.0/midas_index/compute/rotation.py +121 -0
- midas_index-0.1.0/midas_index/compute/seeds.py +560 -0
- midas_index-0.1.0/midas_index/device.py +62 -0
- midas_index-0.1.0/midas_index/indexer.py +146 -0
- midas_index-0.1.0/midas_index/io/__init__.py +32 -0
- midas_index-0.1.0/midas_index/io/binary.py +78 -0
- midas_index-0.1.0/midas_index/io/bins_builder.py +142 -0
- midas_index-0.1.0/midas_index/io/consolidated.py +9 -0
- midas_index-0.1.0/midas_index/io/csv.py +219 -0
- midas_index-0.1.0/midas_index/io/output.py +180 -0
- midas_index-0.1.0/midas_index/io/params.py +134 -0
- midas_index-0.1.0/midas_index/params.py +76 -0
- midas_index-0.1.0/midas_index/pipeline.py +336 -0
- midas_index-0.1.0/midas_index/result.py +42 -0
- midas_index-0.1.0/midas_index.egg-info/PKG-INFO +97 -0
- midas_index-0.1.0/midas_index.egg-info/SOURCES.txt +53 -0
- midas_index-0.1.0/midas_index.egg-info/dependency_links.txt +1 -0
- midas_index-0.1.0/midas_index.egg-info/entry_points.txt +2 -0
- midas_index-0.1.0/midas_index.egg-info/requires.txt +9 -0
- midas_index-0.1.0/midas_index.egg-info/top_level.txt +1 -0
- midas_index-0.1.0/pyproject.toml +52 -0
- midas_index-0.1.0/setup.cfg +4 -0
- midas_index-0.1.0/tests/test_compute_binning.py +37 -0
- midas_index-0.1.0/tests/test_compute_matching.py +275 -0
- midas_index-0.1.0/tests/test_compute_orientation_grid.py +49 -0
- midas_index-0.1.0/tests/test_compute_position_grid.py +73 -0
- midas_index-0.1.0/tests/test_compute_reduce.py +33 -0
- midas_index-0.1.0/tests/test_compute_rotation.py +59 -0
- midas_index-0.1.0/tests/test_compute_seeds.py +97 -0
- midas_index-0.1.0/tests/test_devices.py +109 -0
- midas_index-0.1.0/tests/test_forward_adapter.py +129 -0
- midas_index-0.1.0/tests/test_io_binary.py +58 -0
- midas_index-0.1.0/tests/test_io_bins_builder.py +129 -0
- midas_index-0.1.0/tests/test_io_csv.py +135 -0
- midas_index-0.1.0/tests/test_io_output.py +133 -0
- midas_index-0.1.0/tests/test_io_params.py +132 -0
- midas_index-0.1.0/tests/test_pipeline_e2e.py +109 -0
- midas_index-0.1.0/tests/test_regression_vs_c.py +99 -0
- midas_index-0.1.0/tests/test_roundtrip_diffract.py +199 -0
- 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
|
+
]
|
|
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
|