de-ebsd 0.2.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 (69) hide show
  1. de_ebsd/__init__.py +76 -0
  2. de_ebsd/core/__init__.py +23 -0
  3. de_ebsd/core/constants.py +81 -0
  4. de_ebsd/core/device.py +12 -0
  5. de_ebsd/core/geometry.py +67 -0
  6. de_ebsd/core/lambert.py +148 -0
  7. de_ebsd/core/rotation.py +139 -0
  8. de_ebsd/crystal/__init__.py +22 -0
  9. de_ebsd/crystal/data/__init__.py +0 -0
  10. de_ebsd/crystal/data/element_properties.py +29 -0
  11. de_ebsd/crystal/data/scattering_tables.py +414 -0
  12. de_ebsd/crystal/lattice.py +517 -0
  13. de_ebsd/crystal/scattering.py +339 -0
  14. de_ebsd/crystal/scattering_torch.py +233 -0
  15. de_ebsd/crystal/structure_factors.py +291 -0
  16. de_ebsd/diffraction/__init__.py +45 -0
  17. de_ebsd/diffraction/backends.py +318 -0
  18. de_ebsd/diffraction/bethe.py +28 -0
  19. de_ebsd/diffraction/kinematic.py +389 -0
  20. de_ebsd/diffraction/kvectors.py +120 -0
  21. de_ebsd/diffraction/prism.py +204 -0
  22. de_ebsd/diffraction/reflections.py +234 -0
  23. de_ebsd/index/__init__.py +61 -0
  24. de_ebsd/index/_kernels.py +222 -0
  25. de_ebsd/index/backproject.py +171 -0
  26. de_ebsd/index/crosscorr.py +654 -0
  27. de_ebsd/index/dictionary.py +362 -0
  28. de_ebsd/index/io.py +324 -0
  29. de_ebsd/index/live/__init__.py +5 -0
  30. de_ebsd/index/live/background.py +103 -0
  31. de_ebsd/index/live/calibration.py +70 -0
  32. de_ebsd/index/live/common.py +44 -0
  33. de_ebsd/index/live/deformation.py +289 -0
  34. de_ebsd/index/live/indexer.py +386 -0
  35. de_ebsd/index/preprocess.py +195 -0
  36. de_ebsd/index/refine/__init__.py +7 -0
  37. de_ebsd/index/refine/highres.py +479 -0
  38. de_ebsd/index/refine/levenberg.py +33 -0
  39. de_ebsd/index/refine/orientation.py +367 -0
  40. de_ebsd/index/refine/pattern_center.py +356 -0
  41. de_ebsd/index/spherical/__init__.py +41 -0
  42. de_ebsd/index/spherical/correlation.py +106 -0
  43. de_ebsd/index/spherical/harmonics.py +311 -0
  44. de_ebsd/index/spherical/indexer.py +345 -0
  45. de_ebsd/index/spherical/wigner.py +190 -0
  46. de_ebsd/sim/__init__.py +20 -0
  47. de_ebsd/sim/detector.py +145 -0
  48. de_ebsd/sim/masterpattern.py +231 -0
  49. de_ebsd/sim/microscope.py +93 -0
  50. de_ebsd/sim/montecarlo/__init__.py +19 -0
  51. de_ebsd/sim/montecarlo/depth.py +167 -0
  52. de_ebsd/sim/montecarlo/transport.py +532 -0
  53. de_ebsd/sim/simulator.py +291 -0
  54. de_ebsd/viz/__init__.py +53 -0
  55. de_ebsd/viz/camera.py +56 -0
  56. de_ebsd/viz/depth_widget.js +76 -0
  57. de_ebsd/viz/explorer.py +647 -0
  58. de_ebsd/viz/figures.py +67 -0
  59. de_ebsd/viz/maps.py +277 -0
  60. de_ebsd/viz/patterns.py +164 -0
  61. de_ebsd/viz/setup_widget.js +171 -0
  62. de_ebsd/viz/views.py +125 -0
  63. de_ebsd/viz/widgets.py +191 -0
  64. de_ebsd-0.2.0.dist-info/METADATA +251 -0
  65. de_ebsd-0.2.0.dist-info/RECORD +69 -0
  66. de_ebsd-0.2.0.dist-info/WHEEL +5 -0
  67. de_ebsd-0.2.0.dist-info/licenses/LICENSE +674 -0
  68. de_ebsd-0.2.0.dist-info/licenses/NOTICE +44 -0
  69. de_ebsd-0.2.0.dist-info/top_level.txt +1 -0
de_ebsd/viz/views.py ADDED
@@ -0,0 +1,125 @@
1
+ """Host-neutral compositions of de_ebsd display objects.
2
+
3
+ Scientific panels remain real :class:`anyplotlib.Figure` instances. This
4
+ module only groups figures with small JSON-compatible metadata so Jupyter and
5
+ SpyDE can choose their own layout and lifecycle without nested HTML/iframes.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from functools import cached_property
12
+ from html import escape
13
+ from typing import TYPE_CHECKING
14
+
15
+ if TYPE_CHECKING:
16
+ from de_ebsd.crystal.lattice import Crystal
17
+ from de_ebsd.sim.masterpattern import MasterPattern
18
+
19
+ __all__ = ["PhaseOverview"]
20
+
21
+
22
+ @dataclass
23
+ class PhaseOverview:
24
+ """A phase's metadata and native anyplotlib panels.
25
+
26
+ ``figures`` is the integration boundary for application hosts. SpyDE can
27
+ register each value with its existing anyplotlib Electron bridge; Jupyter
28
+ displays this object directly through :meth:`_ipython_display_`.
29
+ """
30
+
31
+ crystal: Crystal
32
+ master_pattern: MasterPattern | None = None
33
+ name: str | None = None
34
+ texture_height: int = 128
35
+ n_grid: int = 96
36
+
37
+ def __post_init__(self) -> None:
38
+ if self.texture_height < 8:
39
+ raise ValueError("texture_height must be >= 8")
40
+ if self.n_grid < 8:
41
+ raise ValueError("n_grid must be >= 8")
42
+
43
+ @property
44
+ def metadata(self) -> dict[str, str | int | float | None]:
45
+ """Compact, JSON-compatible phase metadata for a sidebar/popover."""
46
+ return {
47
+ "name": self.name or _phase_name(self.crystal),
48
+ "space_group": self.crystal.space_group,
49
+ "space_group_symbol": str(self.crystal._spacegroup.short_name),
50
+ "a_nm": self.crystal.a,
51
+ "b_nm": self.crystal.b,
52
+ "c_nm": self.crystal.c,
53
+ "alpha_deg": self.crystal.alpha,
54
+ "beta_deg": self.crystal.beta,
55
+ "gamma_deg": self.crystal.gamma,
56
+ "volume_nm3": self.crystal.volume,
57
+ "density_g_cm3": self.crystal.density,
58
+ "atom_count": sum(self.crystal.multiplicities),
59
+ "simulation_backend": (
60
+ str(self.master_pattern.metadata.get("backend", "computed"))
61
+ if self.master_pattern is not None
62
+ else None
63
+ ),
64
+ }
65
+
66
+ @cached_property
67
+ def figure(self):
68
+ """The single native figure mounted by Jupyter or SpyDE."""
69
+ if self.master_pattern is None:
70
+ return self.crystal.show(title=f"{self.metadata['name']} structure")
71
+
72
+ from de_ebsd.viz.patterns import phase_overview_figure
73
+
74
+ texture = self.master_pattern.to_equirectangular(height=self.texture_height)
75
+ return phase_overview_figure(
76
+ self.crystal,
77
+ texture.detach().cpu().numpy(),
78
+ name=str(self.metadata["name"]),
79
+ n_grid=self.n_grid,
80
+ )
81
+
82
+ @property
83
+ def figures(self) -> dict[str, object]:
84
+ """Named native figures for notebook or application host mounting."""
85
+ return {"phase_overview": self.figure}
86
+
87
+ def _ipython_display_(self) -> None:
88
+ from IPython.display import HTML, display
89
+
90
+ display(HTML(_metadata_card(self.metadata)))
91
+ for figure in self.figures.values():
92
+ display(figure)
93
+
94
+ def __repr__(self) -> str:
95
+ metadata = self.metadata
96
+ return (
97
+ f"PhaseOverview(name={metadata['name']!r}, "
98
+ f"space_group={metadata['space_group']}, "
99
+ f"master_pattern={self.master_pattern is not None})"
100
+ )
101
+
102
+
103
+ def _phase_name(crystal: Crystal) -> str:
104
+ elements = "+".join(dict.fromkeys(site.element for site in crystal.sites))
105
+ return elements or "phase"
106
+
107
+
108
+ def _metadata_card(metadata: dict[str, str | int | float | None]) -> str:
109
+ name = escape(str(metadata["name"]))
110
+ symbol = escape(str(metadata["space_group_symbol"]))
111
+ backend = metadata["simulation_backend"] or "not computed"
112
+ cell = " × ".join(f"{float(metadata[key]):.4f}" for key in ("a_nm", "b_nm", "c_nm"))
113
+ return f"""
114
+ <div style="font:12px var(--jp-ui-font-family,system-ui,sans-serif);
115
+ color:var(--jp-ui-font-color1,#d8dee9);background:var(--jp-layout-color1,#20242d);
116
+ border:1px solid var(--jp-border-color2,#3b4252);border-radius:6px;
117
+ padding:8px 10px;max-width:940px;display:flex;gap:18px;align-items:baseline;flex-wrap:wrap">
118
+ <strong style="font-size:13px">{name}</strong>
119
+ <span>{symbol} ({int(metadata["space_group"])})</span>
120
+ <span>cell {cell} nm</span>
121
+ <span>{int(metadata["atom_count"])} atoms</span>
122
+ <span>ρ {float(metadata["density_g_cm3"]):.2f} g/cm³</span>
123
+ <span>{escape(str(backend))}</span>
124
+ </div>
125
+ """
de_ebsd/viz/widgets.py ADDED
@@ -0,0 +1,191 @@
1
+ """Portable experiment inspectors; one renderer for Jupyter, HTML, and SpyDE.
2
+
3
+ HTML controls edit a local snapshot; notebook controls synchronize via traitlets.
4
+ Scientific master-pattern and crystal views remain native anyplotlib figures.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from uuid import uuid4
12
+
13
+ import anywidget
14
+ import numpy as np
15
+ import traitlets
16
+
17
+ from de_ebsd.sim.detector import EBSDDetector
18
+ from de_ebsd.sim.microscope import Microscope
19
+
20
+ __all__ = ["DepthWidget", "MicroscopeWidget"]
21
+
22
+
23
+ class _PortableWidget(anywidget.AnyWidget):
24
+ """Export the actual renderer without a notebook manager or CDN dependency."""
25
+
26
+ def _repr_html_(self):
27
+ identity = f"de-ebsd-{uuid4().hex}"
28
+ state = {
29
+ name: getattr(self, name)
30
+ for name in self.traits(sync=True)
31
+ if not name.startswith("_") and name != "layout"
32
+ }
33
+ payload = json.dumps(state, allow_nan=False).replace("<", "\\u003c")
34
+ renderer = str(self._esm).replace("</script", "<\\/script")
35
+ return f'''<div id="{identity}"></div><script type="module">
36
+ {renderer}
37
+ const state = {payload};
38
+ const events = new Map();
39
+ const model = {{
40
+ get: key => state[key],
41
+ set(key, value) {{
42
+ state[key] = value;
43
+ for (const callback of events.get(`change:${{key}}`) || []) callback();
44
+ }},
45
+ on(key, callback) {{
46
+ if (!events.has(key)) events.set(key, new Set());
47
+ events.get(key).add(callback);
48
+ }},
49
+ off(key, callback) {{ events.get(key)?.delete(callback); }},
50
+ save_changes() {{}},
51
+ }};
52
+ render({{model, el: document.getElementById("{identity}")}});
53
+ </script>'''
54
+
55
+ def to_html(self):
56
+ """Interactive standalone HTML; edits do not run Python simulations."""
57
+ return (
58
+ '<!doctype html><html><head><meta charset="utf-8">'
59
+ '<meta name="viewport" content="width=device-width,initial-scale=1">'
60
+ "<title>de-ebsd experiment inspector</title></head><body>"
61
+ + self._repr_html_()
62
+ + "</body></html>"
63
+ )
64
+
65
+
66
+ class MicroscopeWidget(_PortableWidget):
67
+ """Editable geometry with laboratory side view and detector-face inset.
68
+
69
+ Edits never mutate inputs or recompute existing master patterns. Read an
70
+ edited setup explicitly with to_microscope()/to_detector().
71
+ """
72
+
73
+ _esm = Path(__file__).with_name("setup_widget.js").read_text(encoding="utf-8")
74
+ energy_kev = traitlets.Float(20.0, min=0.001).tag(sync=True)
75
+ sample_tilt = traitlets.Float(70.0, min=0.0, max=90.0).tag(sync=True)
76
+ detector_tilt = traitlets.Float(0.0).tag(sync=True)
77
+ azimuthal = traitlets.Float(0.0).tag(sync=True)
78
+ twist = traitlets.Float(0.0).tag(sync=True)
79
+ probe_diameter_nm = traitlets.Float(0.0, min=0.0).tag(sync=True)
80
+ pcx = traitlets.Float(0.5).tag(sync=True)
81
+ pcy = traitlets.Float(0.5).tag(sync=True)
82
+ pcz = traitlets.Float(0.5, min=0.000001).tag(sync=True)
83
+ detector_rows = traitlets.Int(480, min=1).tag(sync=True)
84
+ detector_cols = traitlets.Int(480, min=1).tag(sync=True)
85
+ px_size = traitlets.Float(1.0, min=0.000001).tag(sync=True)
86
+ crystal_label = traitlets.Unicode("").tag(sync=True)
87
+ has_detector = traitlets.Bool(False).tag(sync=True)
88
+
89
+ @traitlets.validate(
90
+ "energy_kev",
91
+ "sample_tilt",
92
+ "detector_tilt",
93
+ "azimuthal",
94
+ "twist",
95
+ "probe_diameter_nm",
96
+ "pcx",
97
+ "pcy",
98
+ "pcz",
99
+ "px_size",
100
+ )
101
+ def _finite(self, proposal):
102
+ if not np.isfinite(proposal["value"]):
103
+ raise traitlets.TraitError("geometry values must be finite")
104
+ return proposal["value"]
105
+
106
+ @classmethod
107
+ def from_setup(cls, microscope=None, detector=None, crystal=None):
108
+ widget = cls()
109
+ if microscope is not None:
110
+ widget.energy_kev = microscope.energy_kev
111
+ widget.sample_tilt = microscope.sample_tilt
112
+ widget.azimuthal = microscope.azimuthal
113
+ widget.probe_diameter_nm = microscope.probe_diameter_nm or 0.0
114
+ if detector is not None:
115
+ widget.has_detector = True
116
+ widget.detector_rows, widget.detector_cols = detector.shape
117
+ widget.pcx, widget.pcy, widget.pcz = detector.pattern_center
118
+ widget.detector_tilt = detector.tilt
119
+ widget.twist = detector.twist
120
+ widget.px_size = detector.px_size
121
+ if microscope is not None and not np.allclose(
122
+ [microscope.sample_tilt, microscope.azimuthal],
123
+ [detector.sample_tilt, detector.azimuthal],
124
+ ):
125
+ raise ValueError("microscope and detector sample_tilt/azimuthal must agree")
126
+ widget.sample_tilt = detector.sample_tilt
127
+ widget.azimuthal = detector.azimuthal
128
+ if crystal is not None:
129
+ widget.crystal_label = _phase_name(crystal)
130
+ return widget
131
+
132
+ def to_microscope(self):
133
+ return Microscope(
134
+ energy=self.energy_kev * 1e3,
135
+ sample_tilt=self.sample_tilt,
136
+ azimuthal=self.azimuthal,
137
+ probe_diameter_nm=self.probe_diameter_nm or None,
138
+ )
139
+
140
+ def to_detector(self):
141
+ return EBSDDetector(
142
+ shape=(self.detector_rows, self.detector_cols),
143
+ pattern_center=(self.pcx, self.pcy, self.pcz),
144
+ sample_tilt=self.sample_tilt,
145
+ tilt=self.detector_tilt,
146
+ azimuthal=self.azimuthal,
147
+ twist=self.twist,
148
+ px_size=self.px_size,
149
+ )
150
+
151
+
152
+ class DepthWidget(_PortableWidget):
153
+ """Read-only depth model, with local energy and slice selection."""
154
+
155
+ _esm = Path(__file__).with_name("depth_widget.js").read_text(encoding="utf-8")
156
+ energies_kev = traitlets.List().tag(sync=True)
157
+ profiles = traitlets.List().tag(sync=True)
158
+ integration_bins = traitlets.List().tag(sync=True)
159
+ depth_step = traitlets.Float(1.0).tag(sync=True)
160
+ energy_index = traitlets.Int(0, min=0).tag(sync=True)
161
+ slice_index = traitlets.Int(0, min=0).tag(sync=True)
162
+
163
+ @classmethod
164
+ def from_distribution(cls, depth, energy_kev=None):
165
+ counts = np.asarray(depth.accum_z, dtype=float)
166
+ energies = np.asarray(depth.energies_kev, dtype=float)
167
+ if (
168
+ counts.ndim != 4
169
+ or 0 in counts.shape
170
+ or len(energies) != counts.shape[0]
171
+ or not np.isfinite(counts).all()
172
+ or (counts < 0).any()
173
+ or not np.isfinite(energies).all()
174
+ or not np.isfinite(depth.depth_step)
175
+ or depth.depth_step <= 0
176
+ or not np.isfinite(depth.n_total)
177
+ or depth.n_total <= 0
178
+ ):
179
+ raise ValueError("depth display requires finite nonnegative counts and positive scales")
180
+ index = depth.n_energies - 1 if energy_kev is None else depth.energy_index(energy_kev)
181
+ return cls(
182
+ energies_kev=energies.tolist(),
183
+ profiles=(counts.sum(axis=(2, 3)) / depth.n_total).tolist(),
184
+ integration_bins=depth.integration_depths().tolist(),
185
+ depth_step=depth.depth_step,
186
+ energy_index=index,
187
+ )
188
+
189
+
190
+ def _phase_name(crystal):
191
+ return "+".join(dict.fromkeys(site.element for site in crystal.sites)) or "phase"
@@ -0,0 +1,251 @@
1
+ Metadata-Version: 2.4
2
+ Name: de-ebsd
3
+ Version: 0.2.0
4
+ Summary: Differentiable, GPU-accelerated EBSD: dynamical master-pattern simulation, Monte Carlo transport, indexing, refinement and HR-EBSD, in PyTorch
5
+ Author-email: Carter Francis <cartsfrancis@gmail.com>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/directelectron/de-ebsd
8
+ Project-URL: Source, https://github.com/directelectron/de-ebsd
9
+ Project-URL: Issues, https://github.com/directelectron/de-ebsd/issues
10
+ Project-URL: Changelog, https://github.com/directelectron/de-ebsd/blob/main/CHANGELOG.rst
11
+ Project-URL: Documentation, https://directelectron.github.io/de-ebsd/
12
+ Keywords: EBSD,dynamical-diffraction,Bloch-wave,master-pattern,pytorch,kikuchipy
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Physics
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ License-File: NOTICE
25
+ Requires-Dist: numpy
26
+ Requires-Dist: torch>=2.4
27
+ Requires-Dist: orix
28
+ Requires-Dist: diffsims
29
+ Requires-Dist: h5py
30
+ Requires-Dist: anyplotlib
31
+ Requires-Dist: anywidget
32
+ Provides-Extra: validate
33
+ Requires-Dist: kikuchipy; extra == "validate"
34
+ Provides-Extra: live
35
+ Requires-Dist: cupy-cuda12x; extra == "live"
36
+ Dynamic: license-file
37
+
38
+ # de-ebsd
39
+
40
+ Differentiable, GPU-accelerated EBSD in PyTorch: dynamical master-pattern
41
+ simulation from a crystal structure, native Monte Carlo electron transport,
42
+ orientation indexing (dictionary, spherical-harmonic and live), gradient and
43
+ high-resolution refinement, pattern-center calibration and HR-EBSD strain. The
44
+ API is composable and object oriented, in the manner of
45
+ [abTEM](https://github.com/abTEM/abTEM), and every intermediate stage is
46
+ inspectable.
47
+
48
+ A master pattern is the backscattered-electron intensity as a function of
49
+ crystal direction, stored on the square-Lambert grid. de-ebsd builds it from
50
+ first principles -- crystal, structure factors with absorption, reflections,
51
+ Bloch-wave dynamical solve, depth integration over a Monte Carlo exit-depth
52
+ profile -- and then runs the inverse problem against measured patterns. The
53
+ physics is ported from [EMsoft](https://github.com/EMsoft-org/EMsoftOO)
54
+ (Callahan & De Graef 2013), which remains the validation oracle (de-ebsd does
55
+ not call EMsoft at runtime); see [NOTICE](NOTICE). Indexing follows EMSphInx
56
+ (Lenthe, Singh & De Graef 2019) and emits [orix](https://github.com/pyxem/orix)
57
+ objects, so [kikuchipy](https://github.com/pyxem/kikuchipy) and other tools can
58
+ use it as a backend.
59
+
60
+ ## Installation
61
+
62
+ Windows / Linux with uv (Python >= 3.11; torch comes from the CUDA 12.4
63
+ index on Windows and the CPU index on Linux, configured in `pyproject.toml`):
64
+
65
+ ```
66
+ git clone https://github.com/directelectron/de-ebsd
67
+ cd de-ebsd
68
+ uv sync
69
+ ```
70
+
71
+ The distribution is named `de-ebsd`; the Python package is `de_ebsd`. The
72
+ fused CUDA kernels of live indexing need CuPy: `uv sync --extra live`.
73
+
74
+ ## Quickstart: simulation
75
+
76
+ ```python
77
+ import de_ebsd as de
78
+
79
+ crystal = de.Crystal.from_parameters(
80
+ a=0.5431, space_group=227, atoms=[("Si", (0.125, 0.125, 0.125))]
81
+ )
82
+ microscope = de.Microscope(energy=20e3, sample_tilt=70.0)
83
+ sim = de.MasterPatternSimulator(
84
+ crystal,
85
+ microscope,
86
+ npx=500,
87
+ dmin=0.05,
88
+ bethe=de.BetheParameters(),
89
+ # backend="bloch_prism" is the default: the batched dynamical solve.
90
+ # "bloch" is the one-direction-at-a-time float64 reference it reproduces.
91
+ )
92
+
93
+ # every stage is inspectable (all figures render through anyplotlib):
94
+ sim.structure_factors().show() # |U_g| per family, absences, V_0
95
+ sim.reflections(k=(0, 0, 1)).show() # Bethe strong / weak split for one beam
96
+ sim.dynamical_matrix(k=(0, 0, 1)).show()
97
+
98
+ master = sim.compute(device="cuda") # -> MasterPattern
99
+ master.show(projection="lambert") # also "stereographic" and "sphere"
100
+ sim.crystal.show() # real 3-D expanded crystal structure
101
+
102
+ # refine and recompute (structure factors are reused when still valid):
103
+ better = sim.refine(npx=1000, dmin=0.04)
104
+
105
+ # project to a detector for a batch of orientations (orix Rotation):
106
+ detector = de.EBSDDetector(shape=(480, 480), pattern_center=(0.5, 0.5, 0.6))
107
+ patterns = master.project(detector, orientation=rotations)
108
+
109
+ # interactive geometry widget in Jupyter (anywidget):
110
+ sim.show_setup(detector=detector)
111
+
112
+ # phase composition for Jupyter or a host application; figures stay native:
113
+ phase = sim.crystal.overview(master_pattern=master)
114
+ sim.microscope.show(detector=detector, crystal=sim.crystal)
115
+
116
+ # STEM zone-axis exploration: geometric Kikuchi lines + kinematic spots,
117
+ # independently toggleable in one projection; no master pattern required:
118
+ explorer = sim.crystal.explore(energy_kev=200, dmin=0.1)
119
+ ```
120
+
121
+ The exit-depth statistics the dynamical solve integrates over come from a native
122
+ Monte Carlo (`de_ebsd.sim.montecarlo`) unless you pass a `DepthDistribution`,
123
+ for example one read from an EMsoft Monte Carlo file.
124
+
125
+ ## Quickstart: indexing
126
+
127
+ ```python
128
+ from de_ebsd.index import (
129
+ DictionaryIndexer,
130
+ SphericalIndexer,
131
+ refine_orientation,
132
+ remove_background,
133
+ )
134
+
135
+ patterns = remove_background(raw, static_bg=static_bg, operation="divide")
136
+
137
+ # Spherical indexing: one SO(3) cross-correlation per pattern, on the GPU.
138
+ indexer = SphericalIndexer(master, detector, bandwidth=68, device="cuda")
139
+ rotations, quality = indexer.index(patterns)
140
+
141
+ # Dictionary indexing over a fundamental-zone sample, as an orix CrystalMap:
142
+ xmap = DictionaryIndexer(master, detector, resolution=2.5).index_scan(scan, step=0.5)
143
+
144
+ # Sharpen a coarse index by gradient or Levenberg-Marquardt refinement:
145
+ refined, score = refine_orientation(patterns, master, detector, rotations, method="lm")
146
+ ```
147
+
148
+ Also in `de_ebsd.index`: `refine_hr` (band-edge refinement of orientation and,
149
+ optionally, pattern center), `determine_pattern_center` (one center shared by
150
+ many patterns), `hrebsd` (lattice strain and rotation by cross-correlation
151
+ against a reference pattern) and `LiveIndexer`, which indexes, refines and
152
+ strain-fits a camera's frames where they already are on the GPU, at thousands of
153
+ patterns a second.
154
+
155
+ ## Examples
156
+
157
+ The [examples folder](examples/) contains executable Sphinx-Gallery tutorials,
158
+ grouped by the layer they exercise.
159
+
160
+ **[Master-pattern simulation](examples/simulation/)** -- the forward chain:
161
+
162
+ - [CIF to detector](examples/simulation/cif_to_detector.py): silicon structure,
163
+ native Monte Carlo, diffraction intermediates, full 3D master pattern,
164
+ orix orientations, detector images, phase overview, and HDF5 round trip.
165
+ - [Energy-resolved simulation](examples/simulation/energy_resolved_simulation.py):
166
+ nickel transport statistics and dynamical patterns at multiple energies.
167
+ - [Resolution and backends](examples/simulation/resolution_and_backends.py):
168
+ grid refinement, detector-space differences, and PRISM/Bloch comparison,
169
+ plus the microscope, probe, detector and depth-slice inspectors.
170
+ - [Compare with EMsoft](examples/simulation/compare_emsoft.py): use the bundled
171
+ nickel reference's crystal, Bethe settings and Monte Carlo profile, then
172
+ compare both hemispheres, normalized residuals, and detector patterns.
173
+ - [STEM orientation explorer](examples/simulation/stem_orientation_explorer.py):
174
+ a band-line sphere, crystal structure, and a combined lines/spots view. It
175
+ provides geometric band centerlines and relative elastic kinematic spot
176
+ intensities; it does not simulate transmission Kikuchi contrast or
177
+ convergent-beam discs.
178
+
179
+ **[Orientation indexing](examples/indexing/)** -- the inverse problem, each on a
180
+ synthetic two-grain scan with known orientations:
181
+
182
+ - [Dictionary indexing](examples/indexing/dictionary_indexing.py): background
183
+ removal, a fundamental-zone template library, a `CrystalMap` with IPF and
184
+ quality maps, and Levenberg-Marquardt refinement to sub-degree accuracy.
185
+ - [Spherical indexing](examples/indexing/spherical_indexing.py): the SO(3)
186
+ cross-correlation surface, the bandwidth accuracy/cost trade-off and how it
187
+ fails when truncated too far, then refinement off the peak.
188
+
189
+ Run scripts with `uv run python examples/simulation/cif_to_detector.py`. They
190
+ use small CPU-friendly teaching settings; see
191
+ [examples/README.rst](examples/README.rst) for execution, display, and
192
+ convergence notes.
193
+
194
+ ## Documentation
195
+
196
+ The full site -- user guide, design notes, the executable example gallery, and
197
+ an API reference generated from the docstrings -- is published at
198
+ <https://directelectron.github.io/de-ebsd/>. To build it locally:
199
+
200
+ ```shell
201
+ uv sync --group docs
202
+ uv run python -m sphinx -b html -W --keep-going docs docs/_build/html
203
+ ```
204
+
205
+ Open `docs/_build/html/index.html`. The build executes every example, so a
206
+ broken example fails the build.
207
+
208
+ Start at [`docs/user/conventions.rst`](docs/user/conventions.rst) before
209
+ interpreting any number: units, the Cartesian crystal frame, square-Lambert
210
+ storage, the orientation convention and the space-group origin choice are all
211
+ load-bearing, and they are what make de-ebsd's output comparable to EMsoft's
212
+ and kikuchipy's. The design notes under [`docs/dev/`](docs/dev/) cover the
213
+ architecture, the dynamical algorithm, the Monte Carlo, the indexers and the
214
+ validation numbers.
215
+
216
+ ## Validation
217
+
218
+ EMsoft is the oracle (validation only). The script compares a simulated master
219
+ pattern with EMsoft references by per-pixel Pearson correlation of the Lambert
220
+ maps -- scale invariant, so normalization differences do not matter:
221
+
222
+ ```
223
+ uv run python scripts/validate_master_pattern.py --material ni
224
+ uv run python scripts/validate_master_pattern.py --material si \
225
+ --mc /path/to/Si_MC.h5 --device cuda
226
+ ```
227
+
228
+ Ni uses kikuchipy's bundled master pattern and runs with no external files. Si
229
+ needs a local EMsoft `Si_MC.h5` reference run (not redistributed here) -- point
230
+ `--mc` or `DE_EBSD_SI_MC` at your copy.
231
+
232
+ **Si r = 1.0000** at native npx-500 against `Si_MC.h5`, and **Ni r = 1.00000**
233
+ against a matched-settings EMsoft master; cubic and tetragonal phases, single-
234
+ and multi-component, agree to that precision. The npx-500 Si master (about two
235
+ million directions) computes in about 10 s (fp32) on a Pascal TITAN X, roughly
236
+ 10 times faster than EMsoft on a 24-thread CPU for a full energy-resolved
237
+ master. Monoclinic settings and one hexagonal intensity residual are the open
238
+ items; see the validation page of the design notes.
239
+
240
+ ## Development
241
+
242
+ ```
243
+ uv run pytest
244
+ uv run ruff check
245
+ uv run ruff format --check
246
+ ```
247
+
248
+ ## License
249
+
250
+ GPL-3.0-or-later. See [LICENSE](LICENSE) and [NOTICE](NOTICE) for the work this
251
+ package is built on.
@@ -0,0 +1,69 @@
1
+ de_ebsd/__init__.py,sha256=2R1Wzpzn46bZkEd0Citsyfs40HSvCyBozP0zF588Y5U,2113
2
+ de_ebsd/core/__init__.py,sha256=S50uL6VEj-vhj2EWnMbc9khHwCTGplXZb7BkbXgzpnQ,750
3
+ de_ebsd/core/constants.py,sha256=Myd3_n3r7pxZa9wOrUh8VSy4vrfHp1y_UOZP-ClIqQw,3202
4
+ de_ebsd/core/device.py,sha256=KhrkdALz7lZhoKx4ARU3_tazsUUusBFcijFK3coY7Pw,360
5
+ de_ebsd/core/geometry.py,sha256=UetwFkMSJ2GS1tOefIqRpqwJ8atw8IwQuJoGtg7Grlg,2617
6
+ de_ebsd/core/lambert.py,sha256=Snhu7CpUfIzoq7BrEBEEeOeqklV9Urn51q3qCn_8IYQ,5636
7
+ de_ebsd/core/rotation.py,sha256=FoQKm4eXi2zeqBviIHrjCpeBIcyx_IvHJ6AQq2hHTLc,5238
8
+ de_ebsd/crystal/__init__.py,sha256=r526mVbO-DXp43Q2RYs0KvL1NR_YS_L6M7sesvdprGc,669
9
+ de_ebsd/crystal/lattice.py,sha256=JNsRitSHxi_l3TxytWutV_MMTltZu5zYM6pTHiNlxyE,18538
10
+ de_ebsd/crystal/scattering.py,sha256=BiawEYuBtMhX4gsupVV2aAA4cqX3ZE0AuHZpEsZ8FJI,11194
11
+ de_ebsd/crystal/scattering_torch.py,sha256=GQU95u6Lk6rpRg0Y12ANKkzM7b5AFBXw8p-uR5BIY2k,8965
12
+ de_ebsd/crystal/structure_factors.py,sha256=wiYRgUbSStwlse9ZWl_LZapEQhpgdvX1k14c1ooriKU,11015
13
+ de_ebsd/crystal/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ de_ebsd/crystal/data/element_properties.py,sha256=wApsi2JKzs8T3ET6wOG84U3FbMN27xvBYieYKAcEJFk,1860
15
+ de_ebsd/crystal/data/scattering_tables.py,sha256=iX2wli6NgeRVDWn1z_u2AeRrnP-VUVylCyPqpn_G5-Q,16357
16
+ de_ebsd/diffraction/__init__.py,sha256=Gjid94oeawbvq2pWE71VKmjnIKZmhcjzwPSQeScLxRk,1177
17
+ de_ebsd/diffraction/backends.py,sha256=ze16Dcq4HwrBTxlptPWch9B468oYrVWxZPErVCi7nGU,12144
18
+ de_ebsd/diffraction/bethe.py,sha256=RdYUWS9i1UG--kcu8_Y_UfkLUzJNmRYUGjfzXAOb28g,839
19
+ de_ebsd/diffraction/kinematic.py,sha256=hXA2hp4pxc3kO8hIop7o7DS4AAd0xtR8zsng4WI1pmY,17298
20
+ de_ebsd/diffraction/kvectors.py,sha256=Ic1XJgQs03rjM5GOhSfJYiqPbBVQpcD4f1clfdVULew,4886
21
+ de_ebsd/diffraction/prism.py,sha256=7Src9NPts6LtvUJb61izM4GVWUdsR7hU8J4l_ISc0xI,9890
22
+ de_ebsd/diffraction/reflections.py,sha256=pJh9ktSUqSmX9Tpbe70L6V3Rlwd3sV09M-E567e6n8A,8130
23
+ de_ebsd/index/__init__.py,sha256=jDj4Q_IxWduESve0eW1kRdNJHGk8BE1I_dm-GX12h2c,2023
24
+ de_ebsd/index/_kernels.py,sha256=jrsk-7Ael9YHCP_wzRGLFh1Ads6nz8WtcHKqD5nrzGA,8233
25
+ de_ebsd/index/backproject.py,sha256=2LB-W3RLXAKzXHMt6gNJXq4Ih6pqbPsqXixNHkgK6x4,7341
26
+ de_ebsd/index/crosscorr.py,sha256=QQlk37BCHTwP6jenQuLgBlbCIP0g9UU1j2SSnaeYtrc,28059
27
+ de_ebsd/index/dictionary.py,sha256=raj8Q-MF8ptlBzICdJnkQSK727OP1infc1qA5f-uRLc,15251
28
+ de_ebsd/index/io.py,sha256=xEPzhfnkAhPWytQ38Hn16Y1vsE30Eb5JYUhlZ42TM24,12361
29
+ de_ebsd/index/preprocess.py,sha256=1E7aPbWgACjbFoZakZiyGZ2NcemRwSZSI596_E59SPU,6672
30
+ de_ebsd/index/live/__init__.py,sha256=NIN0GkG0-jglfOMNXqeCdaYwFVJjM3erw5RV2IQWcfE,126
31
+ de_ebsd/index/live/background.py,sha256=Igs0IyUhGnhIulwRqIcskYUbmWOuPWiUwHLpvyWIcjc,4697
32
+ de_ebsd/index/live/calibration.py,sha256=--AOwvfADAmqpIIx8NwS4Oo4_Qjp9CjviBHInej99wg,3130
33
+ de_ebsd/index/live/common.py,sha256=58lkc7z4EIeNDeS9SWHsEx_oFZNDFxM1yZxmuQgxvmo,2036
34
+ de_ebsd/index/live/deformation.py,sha256=lervj_iY9E4CZzwOqU2R1CHmh3Wyzva_NWOQ1j-V0dQ,14066
35
+ de_ebsd/index/live/indexer.py,sha256=Nne84trTGP6EO4lb970eNwpxEK84PMpUI0dxMlR5l-k,19076
36
+ de_ebsd/index/refine/__init__.py,sha256=V9UESmUAp1IJkAx-fPOsKHQuw3pLdw06dt-90n_yAfk,325
37
+ de_ebsd/index/refine/highres.py,sha256=oilFaN-ej7KWFOLogOTrFyNJlE-FL6AtetPnAHIjI14,20564
38
+ de_ebsd/index/refine/levenberg.py,sha256=sL3-sfkNirj0xd25XRnmk_wsJsVFdiMxuPKKlZ8HhuU,1044
39
+ de_ebsd/index/refine/orientation.py,sha256=hFwxrenVd90z0ShlF2c0jj9oz8sm67qs2L16H2ro-GQ,15363
40
+ de_ebsd/index/refine/pattern_center.py,sha256=jfBAIImjmHcepFbGBHoWcyKN7ymhcJyTovImJllWpU0,14845
41
+ de_ebsd/index/spherical/__init__.py,sha256=3-Abio7NeLOeQr8Xh3S7Z5P517muh4EoR42LgCIQFm0,958
42
+ de_ebsd/index/spherical/correlation.py,sha256=V0L5aDCQYs_XL4egOwiN_7zC4ODw76fLa42m8Yt4KcU,4821
43
+ de_ebsd/index/spherical/harmonics.py,sha256=9beJ7MBvxL9z5hsqWWn1RLKcM1-1_RzfYCo-ZPkczrA,13072
44
+ de_ebsd/index/spherical/indexer.py,sha256=QrjQsve7dFd-Xtc4nXK7H-tnJEO22BULeLUguFCZxaw,14787
45
+ de_ebsd/index/spherical/wigner.py,sha256=tl60rzeUQ4hD1sl51730ShbpdkNS4mSXuaJOC5GW5SQ,7115
46
+ de_ebsd/sim/__init__.py,sha256=NpQpmclRQHzyOX3VmClgIOPzbQpARd6LVCOw6a6m6ps,642
47
+ de_ebsd/sim/detector.py,sha256=lBp1H48gd-7hK-p-ME4EGWIW1Dx-L1RxhZjA-C4g3cw,5059
48
+ de_ebsd/sim/masterpattern.py,sha256=3b8PgONOeMIVUZNjfIdNWdkcVwaSnnX7mfY1mMONgdE,9285
49
+ de_ebsd/sim/microscope.py,sha256=XaxP87N4APKYreWIuDWdtA7r6ofI85QGypm5xRg0KpY,3149
50
+ de_ebsd/sim/simulator.py,sha256=A5sTfj8i5MS-7O2vq0yG7UysLJsVgZchoz4Ie3XvaKU,12025
51
+ de_ebsd/sim/montecarlo/__init__.py,sha256=1kNvxm5VOEvSAxq7UB9Vc6WDwIx7GhkXwMdQvSVaAUY,499
52
+ de_ebsd/sim/montecarlo/depth.py,sha256=YTVM3IT4a15E_zUJeH9ccr3vwOx8_SXzJPx8wUx_Rtk,6658
53
+ de_ebsd/sim/montecarlo/transport.py,sha256=1yniQ-JBfL7nZ3Gc2HxHEW0gPapX2chDGVm-xbwab4U,20694
54
+ de_ebsd/viz/__init__.py,sha256=_9r_z04l-0RVVDA2WjbWfpu2twArVqoXceyXTBv100c,1442
55
+ de_ebsd/viz/camera.py,sha256=AnLOjq46hcoERF7zifg5reYXkc2LQJZv3ssya9jY-vw,2186
56
+ de_ebsd/viz/depth_widget.js,sha256=FSFxwQtCgmJT8ebvy6hXwLnzwocUuaNKoah7gm5sE30,5925
57
+ de_ebsd/viz/explorer.py,sha256=KkSfnCddA6u2LwbcW4TygaqUvlTuVaBZRwnNtkMQ2p4,26115
58
+ de_ebsd/viz/figures.py,sha256=S9kT7gL7IpU4_WIEXrn2ZcSxRdn_14WPARf4AXacEFA,2388
59
+ de_ebsd/viz/maps.py,sha256=ARbngihHbDbhshI50krCw3fT3xtBL4mnVdbHfzUSLS0,10514
60
+ de_ebsd/viz/patterns.py,sha256=mRKKZ5eUrsCLwP7_Ar6NsPcIZJ5RaILF9NtDLI8U7sA,5788
61
+ de_ebsd/viz/setup_widget.js,sha256=mdaJplaYLfDLTfPyLcZi-biwfidp0Jb-iuVPLt3Wau4,12141
62
+ de_ebsd/viz/views.py,sha256=SdIDocKWOTqgb4CZkMogjQk9k9o7auKqBI2xKC-EMnU,4684
63
+ de_ebsd/viz/widgets.py,sha256=yWqAsHZOGXIS4HjfxR2oGqjwvuXiLltwL8pfzp8lfNg,7228
64
+ de_ebsd-0.2.0.dist-info/licenses/LICENSE,sha256=v1Fr86wVTKRvW6ogT1m8daMI2FbBTYfy8F1AeVOlk9o,35155
65
+ de_ebsd-0.2.0.dist-info/licenses/NOTICE,sha256=0cW-1S8ZVedeYdAa-iVV-WqKkza5F203vhFbesGUXkI,2338
66
+ de_ebsd-0.2.0.dist-info/METADATA,sha256=JtkRrzLtOP66d_0idje7Y8iZ-OsUFlpElYjR277s3Tc,10750
67
+ de_ebsd-0.2.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
68
+ de_ebsd-0.2.0.dist-info/top_level.txt,sha256=qsZnl1OBHXFetZDzxHVYURMzNGwfuEjo-KBRbmngz2I,8
69
+ de_ebsd-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+