midas-process-grains 0.1.0__py3-none-any.whl

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 (33) hide show
  1. midas_process_grains/__init__.py +41 -0
  2. midas_process_grains/__main__.py +9 -0
  3. midas_process_grains/cli.py +145 -0
  4. midas_process_grains/compute/__init__.py +35 -0
  5. midas_process_grains/compute/c_parity.py +498 -0
  6. midas_process_grains/compute/c_parity_emit.py +653 -0
  7. midas_process_grains/compute/c_parity_run.py +286 -0
  8. midas_process_grains/compute/canonicalize.py +131 -0
  9. midas_process_grains/compute/cluster.py +515 -0
  10. midas_process_grains/compute/conflict.py +245 -0
  11. midas_process_grains/compute/geometry.py +116 -0
  12. midas_process_grains/compute/pass_a.py +218 -0
  13. midas_process_grains/compute/refine_cluster.py +347 -0
  14. midas_process_grains/compute/strain.py +608 -0
  15. midas_process_grains/compute/stress.py +86 -0
  16. midas_process_grains/compute/symmetry.py +189 -0
  17. midas_process_grains/compute/twins.py +180 -0
  18. midas_process_grains/device.py +60 -0
  19. midas_process_grains/io/__init__.py +42 -0
  20. midas_process_grains/io/binary.py +317 -0
  21. midas_process_grains/io/consolidated.py +142 -0
  22. midas_process_grains/io/csv.py +179 -0
  23. midas_process_grains/io/hkls.py +61 -0
  24. midas_process_grains/io/ids_hash.py +111 -0
  25. midas_process_grains/modes.py +77 -0
  26. midas_process_grains/params.py +214 -0
  27. midas_process_grains/pipeline.py +836 -0
  28. midas_process_grains/result.py +157 -0
  29. midas_process_grains-0.1.0.dist-info/METADATA +176 -0
  30. midas_process_grains-0.1.0.dist-info/RECORD +33 -0
  31. midas_process_grains-0.1.0.dist-info/WHEEL +5 -0
  32. midas_process_grains-0.1.0.dist-info/entry_points.txt +2 -0
  33. midas_process_grains-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,41 @@
1
+ """midas-process-grains: pure-Python FF-HEDM grain-determination + strain.
2
+
3
+ Drop-in replacement for ``FF_HEDM/src/ProcessGrains.c``. Reads the binary
4
+ outputs of the upstream pipeline (``IndexBest{,Full}.bin``, ``FitBest.bin``,
5
+ ``Key.bin``, ``OrientPosFit.bin``, ``ProcessKey.bin``) and emits the canonical
6
+ MIDAS grain artefacts (``Grains.csv``, ``SpotMatrix.csv``, ``GrainIDsKey.csv``).
7
+
8
+ Three operating modes (`mode=` kwarg):
9
+
10
+ * ``"legacy"`` — bit-for-bit reproduce the current C ProcessGrains
11
+ output (used for regression tests during migration).
12
+ * ``"paper_claim"`` — the §3.6 spec from the MIDAS methodology paper that
13
+ the current C code does not actually enforce
14
+ (90% shared peaks, 0.01° misorientation, 15 µm pos).
15
+ * ``"spot_aware"`` — DEFAULT. Symmetry-aware row-aligned per-hkl SpotID
16
+ consistency, Jaccard pre-screen, union-of-cluster
17
+ emission, lstsq strain. No position gate.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ __version__ = "0.1.0"
23
+
24
+ from .params import ProcessGrainsParams, read_paramstest_pg
25
+
26
+ __all__ = [
27
+ "__version__",
28
+ "ProcessGrainsParams",
29
+ "read_paramstest_pg",
30
+ ]
31
+
32
+
33
+ def __getattr__(name):
34
+ """Lazy import of pipeline-level symbols (avoid module cycles during build-up)."""
35
+ if name == "ProcessGrains":
36
+ from .pipeline import ProcessGrains
37
+ return ProcessGrains
38
+ if name == "ProcessGrainsResult":
39
+ from .result import ProcessGrainsResult
40
+ return ProcessGrainsResult
41
+ raise AttributeError(f"module 'midas_process_grains' has no attribute {name!r}")
@@ -0,0 +1,9 @@
1
+ """``python -m midas_process_grains`` shim."""
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+
6
+ from .cli import main
7
+
8
+ if __name__ == "__main__":
9
+ sys.exit(main())
@@ -0,0 +1,145 @@
1
+ """CLI: ``midas-process-grains`` (and ``python -m midas_process_grains``).
2
+
3
+ Mirrors the C ``ProcessGrains`` invocation pattern (single positional arg:
4
+ the parameter file path) with optional flags to override mode, device,
5
+ dtype, and a couple of merge knobs.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import List, Optional
14
+
15
+ from . import __version__
16
+
17
+
18
+ def _build_parser() -> argparse.ArgumentParser:
19
+ p = argparse.ArgumentParser(
20
+ prog="midas-process-grains",
21
+ description=(
22
+ "Pure-Python FF-HEDM grain-determination + strain pipeline "
23
+ "(drop-in for ProcessGrains)."
24
+ ),
25
+ )
26
+ p.add_argument(
27
+ "param_file",
28
+ type=Path,
29
+ help="Path to paramstest.txt (the same file IndexerOMP/FitPosOrStrains "
30
+ "consumed for this run).",
31
+ )
32
+ p.add_argument(
33
+ "num_procs", type=int, nargs="?", default=1,
34
+ help="CPU thread count (used only on cpu device). Default 1.",
35
+ )
36
+ p.add_argument(
37
+ "--mode", choices=("legacy", "paper_claim", "spot_aware", "c_parity"),
38
+ default="spot_aware",
39
+ help="Pipeline mode. Use 'c_parity' for a bit-level replica of the "
40
+ "C ProcessGrains pipeline (writes Grains.csv, GrainIDsKey.csv, "
41
+ "SpotMatrix.csv in C's exact format).",
42
+ )
43
+ p.add_argument(
44
+ "--min-nr-spots", type=int, default=None,
45
+ help="MinNrSpots threshold (Stage 1 cluster-size cutoff). C ProcessGrains "
46
+ "default is 1; the original peakfit_hard run used 3.",
47
+ )
48
+ p.add_argument("--device", choices=("cpu", "cuda", "mps"), default=None)
49
+ p.add_argument("--dtype", choices=("float32", "float64"), default=None)
50
+ p.add_argument("--misori-tol", type=float, default=None,
51
+ help="Override the Phase 1 misorientation tolerance (degrees).")
52
+ p.add_argument(
53
+ "--strain-method",
54
+ choices=(
55
+ "kenesei", "kenesei_unbounded", "fable_beaudoin", "both",
56
+ # backwards-compat aliases (resolved in params.validated())
57
+ "lstsq", "lattice",
58
+ ),
59
+ default=None,
60
+ help="Per-grain strain solver. Default: kenesei (bounded ±0.01, "
61
+ "matches C reference). Use fable_beaudoin for the lattice-"
62
+ "parameter route, or both to emit each.",
63
+ )
64
+ p.add_argument("--material", default=None,
65
+ help="Material name for stiffness lookup (e.g. Cu, Ni, Fe).")
66
+ p.add_argument("--stiffness-file", type=Path, default=None,
67
+ help="Path to a 6×6 stiffness matrix (CSV/TXT/NPY).")
68
+ p.add_argument("--out-dir", type=Path, default=None,
69
+ help="Where to write outputs. Default: param-file directory.")
70
+ p.add_argument("--no-h5", action="store_true",
71
+ help="Skip writing data_consolidated.h5.")
72
+ p.add_argument("--no-diagnostics-h5", action="store_true",
73
+ help="Skip writing processgrains_diagnostics.h5.")
74
+ p.add_argument("--max-seeds", type=int, default=None,
75
+ help="Process only the first N alive seeds (smoke / dev).")
76
+ p.add_argument("--version", action="version",
77
+ version=f"midas-process-grains {__version__}")
78
+ return p
79
+
80
+
81
+ def main(argv: Optional[List[str]] = None) -> int:
82
+ """CLI entry point. Returns process exit code."""
83
+ args = _build_parser().parse_args(argv)
84
+
85
+ from .device import apply_cpu_threads, resolve_device, resolve_dtype
86
+ from .pipeline import ProcessGrains
87
+
88
+ # ── c_parity mode: dispatch to the C-replica pipeline and return ────────
89
+ if args.mode == "c_parity":
90
+ from .compute.c_parity_run import run_c_parity_pipeline_from_disk
91
+ run_dir = args.param_file.parent
92
+ out_dir = args.out_dir if args.out_dir is not None else run_dir
93
+ device = resolve_device(args.device)
94
+ # torch device strings: "cpu" / "cuda" / "cuda:0" / "mps"
95
+ device_str = str(device) if not hasattr(device, "type") else (
96
+ device.type if device.index is None else f"{device.type}:{device.index}"
97
+ )
98
+ apply_cpu_threads(args.num_procs, device)
99
+ run_c_parity_pipeline_from_disk(
100
+ run_dir=run_dir,
101
+ out_dir=out_dir,
102
+ min_nr_spots=(args.min_nr_spots
103
+ if args.min_nr_spots is not None else 1),
104
+ device=device_str,
105
+ )
106
+ return 0
107
+
108
+ pg = ProcessGrains.from_param_file(
109
+ args.param_file,
110
+ device=args.device,
111
+ dtype=args.dtype,
112
+ )
113
+ apply_cpu_threads(args.num_procs, pg.device)
114
+
115
+ # CLI overrides on top of paramstest.
116
+ if args.misori_tol is not None:
117
+ pg.params.MisoriTol = float(args.misori_tol)
118
+ if args.strain_method is not None:
119
+ pg.params.StrainMethod = args.strain_method
120
+ if args.material is not None:
121
+ pg.params.MaterialName = args.material
122
+ if args.stiffness_file is not None:
123
+ pg.params.StiffnessFile = str(args.stiffness_file)
124
+ pg.params = pg.params.validated()
125
+
126
+ if args.max_seeds is not None:
127
+ pg.params.raw["__max_seeds__"] = [str(args.max_seeds)]
128
+
129
+ result = pg.run(mode=args.mode)
130
+ out_dir = args.out_dir if args.out_dir is not None else pg.run_dir
131
+ result.write(
132
+ out_dir,
133
+ h5=not args.no_h5,
134
+ diagnostics_h5=not args.no_diagnostics_h5,
135
+ )
136
+ print(
137
+ f"midas-process-grains {__version__}: "
138
+ f"{result.n_grains} grains written to {out_dir}",
139
+ file=sys.stderr,
140
+ )
141
+ return 0
142
+
143
+
144
+ if __name__ == "__main__":
145
+ sys.exit(main())
@@ -0,0 +1,35 @@
1
+ """Compute submodule.
2
+
3
+ Pure-tensor implementations of:
4
+ - symmetry table builders (24-op cubic / hexagonal / etc.)
5
+ - hkl-row permutation under each symmetry op
6
+ - cluster-level orientation canonicalisation
7
+ - misorientation graph + connected components (Phase 1)
8
+ - spot-aware sub-clustering (Phase 2)
9
+ - per-hkl SpotID conflict resolution (Phase 3)
10
+ - lstsq strain solver (Phase 4)
11
+ - Hooke's-law stress (Phase 5)
12
+ - twin post-processor
13
+
14
+ Design rule: every public function takes a ``device`` / ``dtype`` argument or
15
+ honours the caller's tensors' device + dtype, mirroring the conventions of
16
+ ``midas_index`` and ``midas_transforms``.
17
+ """
18
+
19
+ from .symmetry import (
20
+ SymmetryTable,
21
+ build_symmetry_table,
22
+ apply_sym_to_hkl_int,
23
+ )
24
+ from .canonicalize import (
25
+ pick_best_sym_op,
26
+ align_member_to_rep,
27
+ )
28
+
29
+ __all__ = [
30
+ "SymmetryTable",
31
+ "build_symmetry_table",
32
+ "apply_sym_to_hkl_int",
33
+ "pick_best_sym_op",
34
+ "align_member_to_rep",
35
+ ]