ebsdsim 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 (85) hide show
  1. ebsdsim-0.1.0/LICENSE +21 -0
  2. ebsdsim-0.1.0/MANIFEST.in +6 -0
  3. ebsdsim-0.1.0/PKG-INFO +359 -0
  4. ebsdsim-0.1.0/README.md +303 -0
  5. ebsdsim-0.1.0/docs/ebsdsim-banner.svg +153 -0
  6. ebsdsim-0.1.0/ebsdsim/__init__.py +29 -0
  7. ebsdsim-0.1.0/ebsdsim/_pg_ops_data.py +92 -0
  8. ebsdsim-0.1.0/ebsdsim/_sg_ops_data.py +9 -0
  9. ebsdsim-0.1.0/ebsdsim/api.py +478 -0
  10. ebsdsim-0.1.0/ebsdsim/binning.py +133 -0
  11. ebsdsim-0.1.0/ebsdsim/cif.py +425 -0
  12. ebsdsim-0.1.0/ebsdsim/data/__init__.py +1 -0
  13. ebsdsim-0.1.0/ebsdsim/data/goldens/kgrid_rasterize_golden.json +1 -0
  14. ebsdsim-0.1.0/ebsdsim/data/lightweight_hist_surrogate.npz +0 -0
  15. ebsdsim-0.1.0/ebsdsim/data/preset_cifs/GaN.cif +44 -0
  16. ebsdsim-0.1.0/ebsdsim/data/preset_cifs/Ni.cif +222 -0
  17. ebsdsim-0.1.0/ebsdsim/elements.py +60 -0
  18. ebsdsim-0.1.0/ebsdsim/golden.py +46 -0
  19. ebsdsim-0.1.0/ebsdsim/gpu/__init__.py +14 -0
  20. ebsdsim-0.1.0/ebsdsim/gpu/buffers.py +104 -0
  21. ebsdsim-0.1.0/ebsdsim/gpu/device.py +54 -0
  22. ebsdsim-0.1.0/ebsdsim/gpu/dynamical.py +1045 -0
  23. ebsdsim-0.1.0/ebsdsim/gpu/limits.py +34 -0
  24. ebsdsim-0.1.0/ebsdsim/gpu/lu.py +209 -0
  25. ebsdsim-0.1.0/ebsdsim/gpu/monte_carlo.py +392 -0
  26. ebsdsim-0.1.0/ebsdsim/gpu/pipelines.py +174 -0
  27. ebsdsim-0.1.0/ebsdsim/gpu/rasterize.py +376 -0
  28. ebsdsim-0.1.0/ebsdsim/integrate.py +305 -0
  29. ebsdsim-0.1.0/ebsdsim/kgrid.py +224 -0
  30. ebsdsim-0.1.0/ebsdsim/lookup.py +585 -0
  31. ebsdsim-0.1.0/ebsdsim/material.py +96 -0
  32. ebsdsim-0.1.0/ebsdsim/mploader.py +601 -0
  33. ebsdsim-0.1.0/ebsdsim/pg_ops.py +92 -0
  34. ebsdsim-0.1.0/ebsdsim/prescan.py +126 -0
  35. ebsdsim-0.1.0/ebsdsim/rasterize.py +324 -0
  36. ebsdsim-0.1.0/ebsdsim/runner.py +341 -0
  37. ebsdsim-0.1.0/ebsdsim/save.py +214 -0
  38. ebsdsim-0.1.0/ebsdsim/sgh.py +68 -0
  39. ebsdsim-0.1.0/ebsdsim/spacegroup.py +62 -0
  40. ebsdsim-0.1.0/ebsdsim/structure.py +216 -0
  41. ebsdsim-0.1.0/ebsdsim/surrogate.py +296 -0
  42. ebsdsim-0.1.0/ebsdsim/types.py +77 -0
  43. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_assemble_geff_q.wgsl +98 -0
  44. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_bethe_weight_vws_c64.wgsl +50 -0
  45. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_excitation_score.wgsl +90 -0
  46. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gather_diagonal_c64.wgsl +47 -0
  47. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gather_sgh_site_c64.wgsl +32 -0
  48. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gemm_c64_batched.wgsl +64 -0
  49. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gemv_c64_batched.wgsl +69 -0
  50. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_hash_diff_u32.wgsl +45 -0
  51. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_intensity_contract.wgsl +87 -0
  52. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_lambert_fill.wgsl +71 -0
  53. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_lookup_submatrix_c64.wgsl +56 -0
  54. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_output_writeback_f32.wgsl +29 -0
  55. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_pack_w_c64.wgsl +39 -0
  56. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_prescan_counts.wgsl +122 -0
  57. ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_topk_indices.wgsl +134 -0
  58. ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_complex64.wgsl +1 -0
  59. ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_lead_complex64.wgsl +1 -0
  60. ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_trailing_complex64.wgsl +1 -0
  61. ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_upper_complex64.wgsl +1 -0
  62. ebsdsim-0.1.0/ebsdsim/wgsl/lu_solve_large_complex64.wgsl +1 -0
  63. ebsdsim-0.1.0/ebsdsim/wgsl/lu_solve_shared_complex64.wgsl +1 -0
  64. ebsdsim-0.1.0/ebsdsim/wgsl/mc_boundary_modes.wgsl +237 -0
  65. ebsdsim-0.1.0/ebsdsim/wk.py +455 -0
  66. ebsdsim-0.1.0/ebsdsim/wk_params.py +8 -0
  67. ebsdsim-0.1.0/ebsdsim.egg-info/PKG-INFO +359 -0
  68. ebsdsim-0.1.0/ebsdsim.egg-info/SOURCES.txt +83 -0
  69. ebsdsim-0.1.0/ebsdsim.egg-info/dependency_links.txt +1 -0
  70. ebsdsim-0.1.0/ebsdsim.egg-info/requires.txt +10 -0
  71. ebsdsim-0.1.0/ebsdsim.egg-info/top_level.txt +1 -0
  72. ebsdsim-0.1.0/examples/01_quick_start.ipynb +149 -0
  73. ebsdsim-0.1.0/examples/02_save_and_load.ipynb +203 -0
  74. ebsdsim-0.1.0/pyproject.toml +68 -0
  75. ebsdsim-0.1.0/setup.cfg +4 -0
  76. ebsdsim-0.1.0/tests/test_api.py +82 -0
  77. ebsdsim-0.1.0/tests/test_cif.py +53 -0
  78. ebsdsim-0.1.0/tests/test_cpu_goldens.py +58 -0
  79. ebsdsim-0.1.0/tests/test_gpu.py +176 -0
  80. ebsdsim-0.1.0/tests/test_kvector.py +57 -0
  81. ebsdsim-0.1.0/tests/test_lookup.py +175 -0
  82. ebsdsim-0.1.0/tests/test_material.py +32 -0
  83. ebsdsim-0.1.0/tests/test_mc.py +64 -0
  84. ebsdsim-0.1.0/tests/test_save_load.py +104 -0
  85. ebsdsim-0.1.0/tests/test_surrogate.py +50 -0
ebsdsim-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZacharyVarley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include LICENSE
2
+ include README.md
3
+ recursive-include docs *
4
+ recursive-include ebsdsim/wgsl *.wgsl
5
+ recursive-include ebsdsim/data *
6
+ recursive-include examples *.ipynb
ebsdsim-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,359 @@
1
+ Metadata-Version: 2.4
2
+ Name: ebsdsim
3
+ Version: 0.1.0
4
+ Summary: GPU-accelerated dynamical EBSD master-pattern simulation.
5
+ Author-email: Zachary Varley <zacharytvarley@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 ZacharyVarley
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/ZacharyVarley/ebsdsim
29
+ Project-URL: Repository, https://github.com/ZacharyVarley/ebsdsim
30
+ Project-URL: Issues, https://github.com/ZacharyVarley/ebsdsim/issues
31
+ Project-URL: Changelog, https://github.com/ZacharyVarley/ebsdsim/blob/main/CHANGELOG.md
32
+ Keywords: EBSD,electron backscatter diffraction,master pattern,dynamical diffraction,Kikuchi,crystallography,Monte Carlo,WebGPU,wgpu
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.10
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Programming Language :: Python :: 3.13
42
+ Classifier: Topic :: Scientific/Engineering :: Physics
43
+ Classifier: Topic :: Scientific/Engineering :: Visualization
44
+ Requires-Python: >=3.10
45
+ Description-Content-Type: text/markdown
46
+ License-File: LICENSE
47
+ Requires-Dist: numpy>=1.21
48
+ Requires-Dist: wgpu>=0.29
49
+ Provides-Extra: dev
50
+ Requires-Dist: build>=1.0; extra == "dev"
51
+ Requires-Dist: pytest>=7.0; extra == "dev"
52
+ Provides-Extra: docs
53
+ Requires-Dist: jupyter>=1.0; extra == "docs"
54
+ Requires-Dist: matplotlib>=3.5; extra == "docs"
55
+ Dynamic: license-file
56
+
57
+ <p align="center">
58
+ <img src="docs/ebsdsim-banner.svg" alt="ebsdsim — dynamical EBSD master patterns" width="100%">
59
+ </p>
60
+
61
+ # ebsdsim
62
+
63
+ [![CI](https://github.com/ZacharyVarley/ebsdsim/actions/workflows/ci.yml/badge.svg)](https://github.com/ZacharyVarley/ebsdsim/actions/workflows/ci.yml)
64
+
65
+ **Dynamical EBSD master-pattern simulation for Python.**
66
+
67
+ `ebsdsim` computes Lambert-projected Kikuchi master patterns from a crystal structure
68
+ using multi-beam dynamical electron diffraction (Bloch formulation). Beams are classified
69
+ with **Bethe perturbation theory** (`bethe_c_*` cutoffs); the pattern is shaped equally by
70
+ the **beam and Monte Carlo model** (energy, tilt, depth / energy distribution) and by
71
+ those **dynamical settings** (Bethe cutoffs, rank, `dmin`).
72
+
73
+ The default Lambert raster uses `halfw=250`, i.e. **501×501** pixels
74
+ (`side = 1 + 2 × halfw`, always odd).
75
+
76
+ ---
77
+
78
+ ## Features
79
+
80
+ - **Dynamical diffraction** — Bloch-theory multi-beam solve with fixed-rank Smith /
81
+ Lyapunov iteration; Bethe theory selects which reflections enter the system.
82
+ - **Experiment-aware weighting** — Monte Carlo depth / energy distributions (fast
83
+ surrogate by default, or full GPU Monte Carlo) keyed to beam voltage and specimen
84
+ tilt.
85
+ - **Automatic stopping** — energy-bin integration and MC both stop when their outputs
86
+ stop changing.
87
+ - **Portable output** — compressed `.npz` files with symmetry-reduced fundamental-sector
88
+ data; expand to full Lambert hemispheres with NumPy only via `mploader`.
89
+
90
+ ---
91
+
92
+ ## What drives the pattern?
93
+
94
+ | Input family | Examples | Typical use |
95
+ | --- | --- | --- |
96
+ | **Beam & specimen** | `voltage_kv`, `sigma_deg`, `omega_deg`, `energy_binwidth_keV` | Match microscope settings and sample tilt — usually the first knobs to tune against experiment. |
97
+ | **Monte Carlo** | `mc_backend`, `mc_auto_stop`, `mc_relative_tol`, `n_trajectories` | Control how depth and energy loss are sampled (or approximated by the surrogate). |
98
+ | **Dynamical diffraction** | `bethe_c_strong`, `bethe_c_weak`, `bethe_c_cutoff`, `dbdiff_sg_cutoff`, `rank`, `dmin` | Bethe beam selection and multi-beam solve settings. |
99
+ | **Raster** | `halfw` | Lambert half-width; output size is `(1 + 2×halfw)²`. |
100
+ | **Structure** | lattice, sites, `b_iso` | Crystal geometry and thermal motion (structure factors). |
101
+
102
+ All of the above are keyword arguments to `master_pattern` and `master_pattern_from_cif`.
103
+
104
+ ---
105
+
106
+ ## Debye–Waller factor
107
+
108
+ Per-site isotropic Debye–Waller factors are often **unknown** when only a lattice and
109
+ composition are available. When `b_iso` is omitted on an `Atom`, or when a CIF lacks
110
+ `_atom_site_B_iso_or_equiv` / `_atom_site_U_iso_or_equiv`, `ebsdsim` defaults to
111
+ **0.5 Ų** (stored internally as **0.005 nm²**) — a room-temperature isotropic
112
+ fallback.
113
+
114
+ Override per site when you have better data:
115
+
116
+ ```python
117
+ es.Atom("Ga", x=1/3, y=2/3, z=0.0, b_iso=0.45) # Ų
118
+ ```
119
+
120
+ In the future, defaults should come from the CIF when present (e.g. values refined
121
+ against neutron diffraction), from DFT, or from modern surrogates that predict
122
+ site-resolved thermal parameters reliably. Until then, treat the default as a
123
+ placeholder and set `b_iso` explicitly when it matters for your comparison.
124
+
125
+ ---
126
+
127
+ ## Requirements
128
+
129
+ | | |
130
+ | --- | --- |
131
+ | Python | 3.10 – 3.13 |
132
+ | Runtime deps | NumPy, [wgpu](https://pypi.org/project/wgpu/) ≥ 0.29 |
133
+ | GPU | WebGPU-capable adapter (see platforms below) |
134
+
135
+ ### Supported platforms
136
+
137
+ | OS | Backend | Notes |
138
+ | --- | --- | --- |
139
+ | **macOS** | Metal | |
140
+ | **Windows** | Vulkan or DirectX 12 | Requires recent GPU drivers. |
141
+ | **Linux** | Vulkan | Install Mesa/Vulkan drivers for your GPU. |
142
+
143
+ Simulation requires a working WebGPU adapter. Headless servers without GPU passthrough
144
+ will not be able to run `master_pattern*`.
145
+
146
+ ---
147
+
148
+ ## Install
149
+
150
+ ```bash
151
+ pip install ebsdsim
152
+ ```
153
+
154
+ From source (editable), with dev and notebook extras:
155
+
156
+ ```bash
157
+ git clone https://github.com/ZacharyVarley/ebsdsim.git
158
+ cd ebsdsim
159
+ pip install -e ".[dev,docs]"
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Quick start
165
+
166
+ ```python
167
+ import ebsdsim as es
168
+
169
+ mp = es.master_pattern_from_cif(
170
+ "GaN.cif", # bundled preset, or any path to a .cif file
171
+ halfw=250, # Lambert half-width → 501×501 output
172
+ voltage_kv=20.0,
173
+ sigma_deg=70.0, # specimen tilt (degrees)
174
+ omega_deg=0.0, # azimuthal sample rotation (degrees)
175
+ energy_binwidth_keV=1.0,
176
+ mc_backend="surrogate", # or "gpu"
177
+ bethe_c_strong=20.0,
178
+ bethe_c_weak=40.0,
179
+ bethe_c_cutoff=200.0,
180
+ dbdiff_sg_cutoff=1.0,
181
+ rank=20,
182
+ dmin=0.05, # nm
183
+ )
184
+
185
+ print(mp.pattern.shape) # (501, 501)
186
+ mp.save("GaN-master-pattern.npz")
187
+ ```
188
+
189
+ Bundled presets: `"GaN.cif"`, `"Ni.cif"` (also accepts a full filesystem path).
190
+
191
+ Manual lattice + sites (lengths in Å). Omit `b_iso` to use the 0.5 Ų default:
192
+
193
+ ```python
194
+ ni = es.Material(
195
+ cell=es.Cell(a=3.52, b=3.52, c=3.52, space_group="Fm-3m"),
196
+ atoms=[es.Atom("Ni", x=0.0, y=0.0, z=0.0)],
197
+ name="Ni",
198
+ )
199
+ mp = es.master_pattern(ni, voltage_kv=20.0, sigma_deg=70.0, halfw=250)
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Beam & Monte Carlo parameters
205
+
206
+ These set the incident beam and how electrons lose energy and penetrate the sample before
207
+ diffraction. They are passed directly to `master_pattern*`:
208
+
209
+ | Parameter | Default | Role |
210
+ | --- | --- | --- |
211
+ | `voltage_kv` | `20.0` | Nominal beam energy (kV) |
212
+ | `sigma_deg` | `70.0` | Specimen tilt angle (degrees) fed to the MC / surrogate model |
213
+ | `omega_deg` | `0.0` | Azimuthal sample rotation (degrees); used by GPU MC |
214
+ | `energy_binwidth_keV` | `1.0` | Width of energy-loss bins integrated into the master pattern |
215
+ | `relative_image_stop` | `0.01` | Stop adding bins when Δimage/image falls below this |
216
+ | `marginal_coverage` | `1.0` | Fraction of MC energy bins to include (1.0 = all) |
217
+ | `mc_backend` | `"surrogate"` | `"gpu"` for full boundary Monte Carlo |
218
+ | `mc_auto_stop` | `True` | GPU MC: stop when depth/energy fits converge |
219
+ | `mc_relative_tol` | `0.01` | GPU MC convergence tolerance |
220
+ | `n_trajectories` | `1048576` | GPU MC trajectory budget when `mc_auto_stop=False` |
221
+
222
+ GPU Monte Carlo example:
223
+
224
+ ```python
225
+ mp = es.master_pattern_from_cif(
226
+ "GaN.cif",
227
+ voltage_kv=20.0,
228
+ sigma_deg=70.0,
229
+ omega_deg=0.0,
230
+ mc_backend="gpu",
231
+ mc_auto_stop=True,
232
+ mc_relative_tol=0.01,
233
+ )
234
+ print(mp.metadata["mc_n_trajectories"], mp.metadata["mc_converged"])
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Dynamical diffraction parameters
240
+
241
+ The solve always uses the **Bloch** formulation. **Bethe perturbation theory** ranks
242
+ reflections and decides which beams are strong, weak, or excluded (`bethe_c_*`). Those
243
+ cutoffs usually matter more than fine-tuning `dmin` or `rank` when matching reference
244
+ patterns:
245
+
246
+ | Parameter | Default | Role |
247
+ | --- | --- | --- |
248
+ | `bethe_c_strong` | `20.0` | Excitation-score threshold for strong beams |
249
+ | `bethe_c_weak` | `40.0` | Threshold band for weak beams |
250
+ | `bethe_c_cutoff` | `200.0` | Upper cutoff — beams above this are excluded |
251
+ | `dbdiff_sg_cutoff` | `1.0` | Structure-factor difference cutoff for beam coupling |
252
+ | `rank` | `20` | Truncation rank for the fixed-rank Lyapunov (Smith) solve |
253
+ | `dmin` | `0.05` nm | Minimum d-spacing — sets the reflection sphere (smaller → more beams, slower) |
254
+ | `halfw` | `250` | Lambert half-width; pattern size is `(1 + 2×halfw) × (1 + 2×halfw)` |
255
+
256
+ ---
257
+
258
+ ## Loading a saved pattern (NumPy only)
259
+
260
+ [`ebsdsim/mploader.py`](ebsdsim/mploader.py) is self-contained (NumPy only). Copy it
261
+ into any project that only needs to read `.npz` output.
262
+
263
+ ```python
264
+ from ebsdsim.mploader import load_master_pattern, to_uint8, save_png_gray
265
+
266
+ mp = load_master_pattern("GaN-master-pattern.npz")
267
+
268
+ nh = mp.data[0, 0, 0] # energy-integrated, site-integrated, north hemisphere
269
+ save_png_gray(to_uint8(nh), "GaN_integrated_nh.png")
270
+ ```
271
+
272
+ ### `.npz` layout (summary)
273
+
274
+ | Array | Meaning |
275
+ | --- | --- |
276
+ | `fundamental_sector` | Symmetry-reduced intensities `(E, S, n_k)` |
277
+ | `fundamental_kij`, `fundamental_khat` | Lambert indices and unit directions per FS pixel |
278
+ | `pg_operators`, `fs_normals` | Point-group matrices and sector bounding normals |
279
+ | `bin_voltages_kv`, `bin_weights` | Per-bin beam voltage and energy weight |
280
+ | `meta_json` | UTF-8 JSON simulation metadata (includes beam, MC, and dynamical settings) |
281
+
282
+ See [`examples/02_save_and_load.ipynb`](examples/02_save_and_load.ipynb) for a full
283
+ walkthrough.
284
+
285
+ ---
286
+
287
+ ## Examples
288
+
289
+ | Resource | Description |
290
+ | --- | --- |
291
+ | [`examples/01_quick_start.ipynb`](examples/01_quick_start.ipynb) | GaN and Ni patterns |
292
+ | [`examples/02_save_and_load.ipynb`](examples/02_save_and_load.ipynb) | Save, reload, export PNGs |
293
+ | [`scripts/run_gan_example.py`](scripts/run_gan_example.py) | Full-resolution GaN script |
294
+
295
+ Preset CIFs ship under `ebsdsim/data/preset_cifs/` (Ni, GaN).
296
+
297
+ ---
298
+
299
+ ## Development
300
+
301
+ ```bash
302
+ pip install -e ".[dev,docs]"
303
+ pytest -m "not slow" # CPU tests; GPU tests skip if no adapter
304
+ pytest -m slow # end-to-end GPU runs (needs WebGPU)
305
+ python -m build # sdist + wheel (requires `build` extra)
306
+ ```
307
+
308
+ CI runs CPU tests on Ubuntu (Python 3.11–3.12) and full GPU tests on macOS
309
+ (Metal). See [`.github/workflows/ci.yml`](.github/workflows/ci.yml).
310
+
311
+ ### Releasing (automated PyPI)
312
+
313
+ Releases are published by pushing a version tag. The
314
+ [`.github/workflows/release.yml`](.github/workflows/release.yml) workflow builds the
315
+ sdist/wheel, uploads them to the GitHub release, and publishes to PyPI via **trusted
316
+ publishing** (no API token in the repo).
317
+
318
+ **One-time setup**
319
+
320
+ 1. On [PyPI](https://pypi.org/manage/account/publishing/): add a **pending publisher**
321
+ - PyPI project name: `ebsdsim`
322
+ - Owner: `ZacharyVarley`, repository: `ebsdsim`
323
+ - Workflow: `release.yml`, environment: `pypi`
324
+ 2. On GitHub: repo **Settings → Environments** → create environment `pypi` (no secrets
325
+ required for trusted publishing).
326
+
327
+ **Each release**
328
+
329
+ 1. Set `version` in `pyproject.toml` and move notes in `CHANGELOG.md` out of
330
+ **Unreleased**.
331
+ 2. Commit and push to `main`.
332
+ 3. Tag and push (tag must match `pyproject.toml`, without the `v` prefix):
333
+
334
+ ```bash
335
+ git tag v0.1.0
336
+ git push origin v0.1.0
337
+ ```
338
+
339
+ 4. Watch the **Release** workflow; the package appears on
340
+ [pypi.org/project/ebsdsim](https://pypi.org/project/ebsdsim/) when it finishes.
341
+
342
+ ---
343
+
344
+ ## Contributing
345
+
346
+ 1. Fork the repo and create a branch from `main`.
347
+ 2. `pip install -e ".[dev]"` and run `pytest -m "not slow"` before opening a PR.
348
+ 3. If your change touches GPU paths or save/load, also run `pytest -m slow` on a
349
+ machine with WebGPU (macOS Metal works).
350
+ 4. Keep PRs focused; include a short note in `CHANGELOG.md` under **Unreleased**
351
+ when user-visible behavior changes.
352
+
353
+ Bug reports and feature requests: [GitHub Issues](https://github.com/ZacharyVarley/ebsdsim/issues).
354
+
355
+ ---
356
+
357
+ ## License
358
+
359
+ MIT — see [LICENSE](LICENSE). Copyright (c) Zachary Varley.
@@ -0,0 +1,303 @@
1
+ <p align="center">
2
+ <img src="docs/ebsdsim-banner.svg" alt="ebsdsim — dynamical EBSD master patterns" width="100%">
3
+ </p>
4
+
5
+ # ebsdsim
6
+
7
+ [![CI](https://github.com/ZacharyVarley/ebsdsim/actions/workflows/ci.yml/badge.svg)](https://github.com/ZacharyVarley/ebsdsim/actions/workflows/ci.yml)
8
+
9
+ **Dynamical EBSD master-pattern simulation for Python.**
10
+
11
+ `ebsdsim` computes Lambert-projected Kikuchi master patterns from a crystal structure
12
+ using multi-beam dynamical electron diffraction (Bloch formulation). Beams are classified
13
+ with **Bethe perturbation theory** (`bethe_c_*` cutoffs); the pattern is shaped equally by
14
+ the **beam and Monte Carlo model** (energy, tilt, depth / energy distribution) and by
15
+ those **dynamical settings** (Bethe cutoffs, rank, `dmin`).
16
+
17
+ The default Lambert raster uses `halfw=250`, i.e. **501×501** pixels
18
+ (`side = 1 + 2 × halfw`, always odd).
19
+
20
+ ---
21
+
22
+ ## Features
23
+
24
+ - **Dynamical diffraction** — Bloch-theory multi-beam solve with fixed-rank Smith /
25
+ Lyapunov iteration; Bethe theory selects which reflections enter the system.
26
+ - **Experiment-aware weighting** — Monte Carlo depth / energy distributions (fast
27
+ surrogate by default, or full GPU Monte Carlo) keyed to beam voltage and specimen
28
+ tilt.
29
+ - **Automatic stopping** — energy-bin integration and MC both stop when their outputs
30
+ stop changing.
31
+ - **Portable output** — compressed `.npz` files with symmetry-reduced fundamental-sector
32
+ data; expand to full Lambert hemispheres with NumPy only via `mploader`.
33
+
34
+ ---
35
+
36
+ ## What drives the pattern?
37
+
38
+ | Input family | Examples | Typical use |
39
+ | --- | --- | --- |
40
+ | **Beam & specimen** | `voltage_kv`, `sigma_deg`, `omega_deg`, `energy_binwidth_keV` | Match microscope settings and sample tilt — usually the first knobs to tune against experiment. |
41
+ | **Monte Carlo** | `mc_backend`, `mc_auto_stop`, `mc_relative_tol`, `n_trajectories` | Control how depth and energy loss are sampled (or approximated by the surrogate). |
42
+ | **Dynamical diffraction** | `bethe_c_strong`, `bethe_c_weak`, `bethe_c_cutoff`, `dbdiff_sg_cutoff`, `rank`, `dmin` | Bethe beam selection and multi-beam solve settings. |
43
+ | **Raster** | `halfw` | Lambert half-width; output size is `(1 + 2×halfw)²`. |
44
+ | **Structure** | lattice, sites, `b_iso` | Crystal geometry and thermal motion (structure factors). |
45
+
46
+ All of the above are keyword arguments to `master_pattern` and `master_pattern_from_cif`.
47
+
48
+ ---
49
+
50
+ ## Debye–Waller factor
51
+
52
+ Per-site isotropic Debye–Waller factors are often **unknown** when only a lattice and
53
+ composition are available. When `b_iso` is omitted on an `Atom`, or when a CIF lacks
54
+ `_atom_site_B_iso_or_equiv` / `_atom_site_U_iso_or_equiv`, `ebsdsim` defaults to
55
+ **0.5 Ų** (stored internally as **0.005 nm²**) — a room-temperature isotropic
56
+ fallback.
57
+
58
+ Override per site when you have better data:
59
+
60
+ ```python
61
+ es.Atom("Ga", x=1/3, y=2/3, z=0.0, b_iso=0.45) # Ų
62
+ ```
63
+
64
+ In the future, defaults should come from the CIF when present (e.g. values refined
65
+ against neutron diffraction), from DFT, or from modern surrogates that predict
66
+ site-resolved thermal parameters reliably. Until then, treat the default as a
67
+ placeholder and set `b_iso` explicitly when it matters for your comparison.
68
+
69
+ ---
70
+
71
+ ## Requirements
72
+
73
+ | | |
74
+ | --- | --- |
75
+ | Python | 3.10 – 3.13 |
76
+ | Runtime deps | NumPy, [wgpu](https://pypi.org/project/wgpu/) ≥ 0.29 |
77
+ | GPU | WebGPU-capable adapter (see platforms below) |
78
+
79
+ ### Supported platforms
80
+
81
+ | OS | Backend | Notes |
82
+ | --- | --- | --- |
83
+ | **macOS** | Metal | |
84
+ | **Windows** | Vulkan or DirectX 12 | Requires recent GPU drivers. |
85
+ | **Linux** | Vulkan | Install Mesa/Vulkan drivers for your GPU. |
86
+
87
+ Simulation requires a working WebGPU adapter. Headless servers without GPU passthrough
88
+ will not be able to run `master_pattern*`.
89
+
90
+ ---
91
+
92
+ ## Install
93
+
94
+ ```bash
95
+ pip install ebsdsim
96
+ ```
97
+
98
+ From source (editable), with dev and notebook extras:
99
+
100
+ ```bash
101
+ git clone https://github.com/ZacharyVarley/ebsdsim.git
102
+ cd ebsdsim
103
+ pip install -e ".[dev,docs]"
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Quick start
109
+
110
+ ```python
111
+ import ebsdsim as es
112
+
113
+ mp = es.master_pattern_from_cif(
114
+ "GaN.cif", # bundled preset, or any path to a .cif file
115
+ halfw=250, # Lambert half-width → 501×501 output
116
+ voltage_kv=20.0,
117
+ sigma_deg=70.0, # specimen tilt (degrees)
118
+ omega_deg=0.0, # azimuthal sample rotation (degrees)
119
+ energy_binwidth_keV=1.0,
120
+ mc_backend="surrogate", # or "gpu"
121
+ bethe_c_strong=20.0,
122
+ bethe_c_weak=40.0,
123
+ bethe_c_cutoff=200.0,
124
+ dbdiff_sg_cutoff=1.0,
125
+ rank=20,
126
+ dmin=0.05, # nm
127
+ )
128
+
129
+ print(mp.pattern.shape) # (501, 501)
130
+ mp.save("GaN-master-pattern.npz")
131
+ ```
132
+
133
+ Bundled presets: `"GaN.cif"`, `"Ni.cif"` (also accepts a full filesystem path).
134
+
135
+ Manual lattice + sites (lengths in Å). Omit `b_iso` to use the 0.5 Ų default:
136
+
137
+ ```python
138
+ ni = es.Material(
139
+ cell=es.Cell(a=3.52, b=3.52, c=3.52, space_group="Fm-3m"),
140
+ atoms=[es.Atom("Ni", x=0.0, y=0.0, z=0.0)],
141
+ name="Ni",
142
+ )
143
+ mp = es.master_pattern(ni, voltage_kv=20.0, sigma_deg=70.0, halfw=250)
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Beam & Monte Carlo parameters
149
+
150
+ These set the incident beam and how electrons lose energy and penetrate the sample before
151
+ diffraction. They are passed directly to `master_pattern*`:
152
+
153
+ | Parameter | Default | Role |
154
+ | --- | --- | --- |
155
+ | `voltage_kv` | `20.0` | Nominal beam energy (kV) |
156
+ | `sigma_deg` | `70.0` | Specimen tilt angle (degrees) fed to the MC / surrogate model |
157
+ | `omega_deg` | `0.0` | Azimuthal sample rotation (degrees); used by GPU MC |
158
+ | `energy_binwidth_keV` | `1.0` | Width of energy-loss bins integrated into the master pattern |
159
+ | `relative_image_stop` | `0.01` | Stop adding bins when Δimage/image falls below this |
160
+ | `marginal_coverage` | `1.0` | Fraction of MC energy bins to include (1.0 = all) |
161
+ | `mc_backend` | `"surrogate"` | `"gpu"` for full boundary Monte Carlo |
162
+ | `mc_auto_stop` | `True` | GPU MC: stop when depth/energy fits converge |
163
+ | `mc_relative_tol` | `0.01` | GPU MC convergence tolerance |
164
+ | `n_trajectories` | `1048576` | GPU MC trajectory budget when `mc_auto_stop=False` |
165
+
166
+ GPU Monte Carlo example:
167
+
168
+ ```python
169
+ mp = es.master_pattern_from_cif(
170
+ "GaN.cif",
171
+ voltage_kv=20.0,
172
+ sigma_deg=70.0,
173
+ omega_deg=0.0,
174
+ mc_backend="gpu",
175
+ mc_auto_stop=True,
176
+ mc_relative_tol=0.01,
177
+ )
178
+ print(mp.metadata["mc_n_trajectories"], mp.metadata["mc_converged"])
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Dynamical diffraction parameters
184
+
185
+ The solve always uses the **Bloch** formulation. **Bethe perturbation theory** ranks
186
+ reflections and decides which beams are strong, weak, or excluded (`bethe_c_*`). Those
187
+ cutoffs usually matter more than fine-tuning `dmin` or `rank` when matching reference
188
+ patterns:
189
+
190
+ | Parameter | Default | Role |
191
+ | --- | --- | --- |
192
+ | `bethe_c_strong` | `20.0` | Excitation-score threshold for strong beams |
193
+ | `bethe_c_weak` | `40.0` | Threshold band for weak beams |
194
+ | `bethe_c_cutoff` | `200.0` | Upper cutoff — beams above this are excluded |
195
+ | `dbdiff_sg_cutoff` | `1.0` | Structure-factor difference cutoff for beam coupling |
196
+ | `rank` | `20` | Truncation rank for the fixed-rank Lyapunov (Smith) solve |
197
+ | `dmin` | `0.05` nm | Minimum d-spacing — sets the reflection sphere (smaller → more beams, slower) |
198
+ | `halfw` | `250` | Lambert half-width; pattern size is `(1 + 2×halfw) × (1 + 2×halfw)` |
199
+
200
+ ---
201
+
202
+ ## Loading a saved pattern (NumPy only)
203
+
204
+ [`ebsdsim/mploader.py`](ebsdsim/mploader.py) is self-contained (NumPy only). Copy it
205
+ into any project that only needs to read `.npz` output.
206
+
207
+ ```python
208
+ from ebsdsim.mploader import load_master_pattern, to_uint8, save_png_gray
209
+
210
+ mp = load_master_pattern("GaN-master-pattern.npz")
211
+
212
+ nh = mp.data[0, 0, 0] # energy-integrated, site-integrated, north hemisphere
213
+ save_png_gray(to_uint8(nh), "GaN_integrated_nh.png")
214
+ ```
215
+
216
+ ### `.npz` layout (summary)
217
+
218
+ | Array | Meaning |
219
+ | --- | --- |
220
+ | `fundamental_sector` | Symmetry-reduced intensities `(E, S, n_k)` |
221
+ | `fundamental_kij`, `fundamental_khat` | Lambert indices and unit directions per FS pixel |
222
+ | `pg_operators`, `fs_normals` | Point-group matrices and sector bounding normals |
223
+ | `bin_voltages_kv`, `bin_weights` | Per-bin beam voltage and energy weight |
224
+ | `meta_json` | UTF-8 JSON simulation metadata (includes beam, MC, and dynamical settings) |
225
+
226
+ See [`examples/02_save_and_load.ipynb`](examples/02_save_and_load.ipynb) for a full
227
+ walkthrough.
228
+
229
+ ---
230
+
231
+ ## Examples
232
+
233
+ | Resource | Description |
234
+ | --- | --- |
235
+ | [`examples/01_quick_start.ipynb`](examples/01_quick_start.ipynb) | GaN and Ni patterns |
236
+ | [`examples/02_save_and_load.ipynb`](examples/02_save_and_load.ipynb) | Save, reload, export PNGs |
237
+ | [`scripts/run_gan_example.py`](scripts/run_gan_example.py) | Full-resolution GaN script |
238
+
239
+ Preset CIFs ship under `ebsdsim/data/preset_cifs/` (Ni, GaN).
240
+
241
+ ---
242
+
243
+ ## Development
244
+
245
+ ```bash
246
+ pip install -e ".[dev,docs]"
247
+ pytest -m "not slow" # CPU tests; GPU tests skip if no adapter
248
+ pytest -m slow # end-to-end GPU runs (needs WebGPU)
249
+ python -m build # sdist + wheel (requires `build` extra)
250
+ ```
251
+
252
+ CI runs CPU tests on Ubuntu (Python 3.11–3.12) and full GPU tests on macOS
253
+ (Metal). See [`.github/workflows/ci.yml`](.github/workflows/ci.yml).
254
+
255
+ ### Releasing (automated PyPI)
256
+
257
+ Releases are published by pushing a version tag. The
258
+ [`.github/workflows/release.yml`](.github/workflows/release.yml) workflow builds the
259
+ sdist/wheel, uploads them to the GitHub release, and publishes to PyPI via **trusted
260
+ publishing** (no API token in the repo).
261
+
262
+ **One-time setup**
263
+
264
+ 1. On [PyPI](https://pypi.org/manage/account/publishing/): add a **pending publisher**
265
+ - PyPI project name: `ebsdsim`
266
+ - Owner: `ZacharyVarley`, repository: `ebsdsim`
267
+ - Workflow: `release.yml`, environment: `pypi`
268
+ 2. On GitHub: repo **Settings → Environments** → create environment `pypi` (no secrets
269
+ required for trusted publishing).
270
+
271
+ **Each release**
272
+
273
+ 1. Set `version` in `pyproject.toml` and move notes in `CHANGELOG.md` out of
274
+ **Unreleased**.
275
+ 2. Commit and push to `main`.
276
+ 3. Tag and push (tag must match `pyproject.toml`, without the `v` prefix):
277
+
278
+ ```bash
279
+ git tag v0.1.0
280
+ git push origin v0.1.0
281
+ ```
282
+
283
+ 4. Watch the **Release** workflow; the package appears on
284
+ [pypi.org/project/ebsdsim](https://pypi.org/project/ebsdsim/) when it finishes.
285
+
286
+ ---
287
+
288
+ ## Contributing
289
+
290
+ 1. Fork the repo and create a branch from `main`.
291
+ 2. `pip install -e ".[dev]"` and run `pytest -m "not slow"` before opening a PR.
292
+ 3. If your change touches GPU paths or save/load, also run `pytest -m slow` on a
293
+ machine with WebGPU (macOS Metal works).
294
+ 4. Keep PRs focused; include a short note in `CHANGELOG.md` under **Unreleased**
295
+ when user-visible behavior changes.
296
+
297
+ Bug reports and feature requests: [GitHub Issues](https://github.com/ZacharyVarley/ebsdsim/issues).
298
+
299
+ ---
300
+
301
+ ## License
302
+
303
+ MIT — see [LICENSE](LICENSE). Copyright (c) Zachary Varley.