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.
- ebsdsim-0.1.0/LICENSE +21 -0
- ebsdsim-0.1.0/MANIFEST.in +6 -0
- ebsdsim-0.1.0/PKG-INFO +359 -0
- ebsdsim-0.1.0/README.md +303 -0
- ebsdsim-0.1.0/docs/ebsdsim-banner.svg +153 -0
- ebsdsim-0.1.0/ebsdsim/__init__.py +29 -0
- ebsdsim-0.1.0/ebsdsim/_pg_ops_data.py +92 -0
- ebsdsim-0.1.0/ebsdsim/_sg_ops_data.py +9 -0
- ebsdsim-0.1.0/ebsdsim/api.py +478 -0
- ebsdsim-0.1.0/ebsdsim/binning.py +133 -0
- ebsdsim-0.1.0/ebsdsim/cif.py +425 -0
- ebsdsim-0.1.0/ebsdsim/data/__init__.py +1 -0
- ebsdsim-0.1.0/ebsdsim/data/goldens/kgrid_rasterize_golden.json +1 -0
- ebsdsim-0.1.0/ebsdsim/data/lightweight_hist_surrogate.npz +0 -0
- ebsdsim-0.1.0/ebsdsim/data/preset_cifs/GaN.cif +44 -0
- ebsdsim-0.1.0/ebsdsim/data/preset_cifs/Ni.cif +222 -0
- ebsdsim-0.1.0/ebsdsim/elements.py +60 -0
- ebsdsim-0.1.0/ebsdsim/golden.py +46 -0
- ebsdsim-0.1.0/ebsdsim/gpu/__init__.py +14 -0
- ebsdsim-0.1.0/ebsdsim/gpu/buffers.py +104 -0
- ebsdsim-0.1.0/ebsdsim/gpu/device.py +54 -0
- ebsdsim-0.1.0/ebsdsim/gpu/dynamical.py +1045 -0
- ebsdsim-0.1.0/ebsdsim/gpu/limits.py +34 -0
- ebsdsim-0.1.0/ebsdsim/gpu/lu.py +209 -0
- ebsdsim-0.1.0/ebsdsim/gpu/monte_carlo.py +392 -0
- ebsdsim-0.1.0/ebsdsim/gpu/pipelines.py +174 -0
- ebsdsim-0.1.0/ebsdsim/gpu/rasterize.py +376 -0
- ebsdsim-0.1.0/ebsdsim/integrate.py +305 -0
- ebsdsim-0.1.0/ebsdsim/kgrid.py +224 -0
- ebsdsim-0.1.0/ebsdsim/lookup.py +585 -0
- ebsdsim-0.1.0/ebsdsim/material.py +96 -0
- ebsdsim-0.1.0/ebsdsim/mploader.py +601 -0
- ebsdsim-0.1.0/ebsdsim/pg_ops.py +92 -0
- ebsdsim-0.1.0/ebsdsim/prescan.py +126 -0
- ebsdsim-0.1.0/ebsdsim/rasterize.py +324 -0
- ebsdsim-0.1.0/ebsdsim/runner.py +341 -0
- ebsdsim-0.1.0/ebsdsim/save.py +214 -0
- ebsdsim-0.1.0/ebsdsim/sgh.py +68 -0
- ebsdsim-0.1.0/ebsdsim/spacegroup.py +62 -0
- ebsdsim-0.1.0/ebsdsim/structure.py +216 -0
- ebsdsim-0.1.0/ebsdsim/surrogate.py +296 -0
- ebsdsim-0.1.0/ebsdsim/types.py +77 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_assemble_geff_q.wgsl +98 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_bethe_weight_vws_c64.wgsl +50 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_excitation_score.wgsl +90 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gather_diagonal_c64.wgsl +47 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gather_sgh_site_c64.wgsl +32 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gemm_c64_batched.wgsl +64 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_gemv_c64_batched.wgsl +69 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_hash_diff_u32.wgsl +45 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_intensity_contract.wgsl +87 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_lambert_fill.wgsl +71 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_lookup_submatrix_c64.wgsl +56 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_output_writeback_f32.wgsl +29 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_pack_w_c64.wgsl +39 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_prescan_counts.wgsl +122 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/ebsd_topk_indices.wgsl +134 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_complex64.wgsl +1 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_lead_complex64.wgsl +1 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_trailing_complex64.wgsl +1 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/lu_factor_upper_complex64.wgsl +1 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/lu_solve_large_complex64.wgsl +1 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/lu_solve_shared_complex64.wgsl +1 -0
- ebsdsim-0.1.0/ebsdsim/wgsl/mc_boundary_modes.wgsl +237 -0
- ebsdsim-0.1.0/ebsdsim/wk.py +455 -0
- ebsdsim-0.1.0/ebsdsim/wk_params.py +8 -0
- ebsdsim-0.1.0/ebsdsim.egg-info/PKG-INFO +359 -0
- ebsdsim-0.1.0/ebsdsim.egg-info/SOURCES.txt +83 -0
- ebsdsim-0.1.0/ebsdsim.egg-info/dependency_links.txt +1 -0
- ebsdsim-0.1.0/ebsdsim.egg-info/requires.txt +10 -0
- ebsdsim-0.1.0/ebsdsim.egg-info/top_level.txt +1 -0
- ebsdsim-0.1.0/examples/01_quick_start.ipynb +149 -0
- ebsdsim-0.1.0/examples/02_save_and_load.ipynb +203 -0
- ebsdsim-0.1.0/pyproject.toml +68 -0
- ebsdsim-0.1.0/setup.cfg +4 -0
- ebsdsim-0.1.0/tests/test_api.py +82 -0
- ebsdsim-0.1.0/tests/test_cif.py +53 -0
- ebsdsim-0.1.0/tests/test_cpu_goldens.py +58 -0
- ebsdsim-0.1.0/tests/test_gpu.py +176 -0
- ebsdsim-0.1.0/tests/test_kvector.py +57 -0
- ebsdsim-0.1.0/tests/test_lookup.py +175 -0
- ebsdsim-0.1.0/tests/test_material.py +32 -0
- ebsdsim-0.1.0/tests/test_mc.py +64 -0
- ebsdsim-0.1.0/tests/test_save_load.py +104 -0
- 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.
|
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
|
+
[](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.
|
ebsdsim-0.1.0/README.md
ADDED
|
@@ -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
|
+
[](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.
|