midas-diffract 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/PKG-INFO +3 -2
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/README.md +1 -1
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract/__init__.py +1 -1
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract/forward.py +60 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract.egg-info/PKG-INFO +3 -2
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract.egg-info/SOURCES.txt +1 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract.egg-info/requires.txt +1 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/pyproject.toml +2 -1
- midas_diffract-0.2.0/tests/test_distortion_layer.py +78 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/LICENSE +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract/hkls.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract/losses.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract/optimize.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract/simulate_panel_zarrs.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract.egg-info/dependency_links.txt +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/midas_diffract.egg-info/top_level.txt +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/setup.cfg +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_c_comparison.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_forward.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_hkls.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_losses.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_multi_detector.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_strain_tensor.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_tilts.py +0 -0
- {midas_diffract-0.1.1 → midas_diffract-0.2.0}/tests/test_wedge.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: midas-diffract
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: End-to-end differentiable forward model for High-Energy Diffraction Microscopy (FF, NF, pf-HEDM)
|
|
5
5
|
Author-email: Hemant Sharma <hsharma@anl.gov>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -18,6 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy>=1.22
|
|
20
20
|
Requires-Dist: torch>=2.0
|
|
21
|
+
Requires-Dist: midas-distortion>=0.2.0
|
|
21
22
|
Provides-Extra: dev
|
|
22
23
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
24
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -102,7 +103,7 @@ MIDAS distribution. See the companion paper and the MIDAS repository
|
|
|
102
103
|
|
|
103
104
|
## Scope
|
|
104
105
|
|
|
105
|
-
`midas-diffract` v0.1.
|
|
106
|
+
`midas-diffract` v0.1.x is deliberately focused on the forward model and its
|
|
106
107
|
gradient chain. The following capabilities build on this substrate and are
|
|
107
108
|
released separately as they mature:
|
|
108
109
|
|
|
@@ -75,7 +75,7 @@ MIDAS distribution. See the companion paper and the MIDAS repository
|
|
|
75
75
|
|
|
76
76
|
## Scope
|
|
77
77
|
|
|
78
|
-
`midas-diffract` v0.1.
|
|
78
|
+
`midas-diffract` v0.1.x is deliberately focused on the forward model and its
|
|
79
79
|
gradient chain. The following capabilities build on this substrate and are
|
|
80
80
|
released separately as they mature:
|
|
81
81
|
|
|
@@ -89,6 +89,16 @@ class HEDMGeometry:
|
|
|
89
89
|
# Default False preserves the existing
|
|
90
90
|
# behaviour: NF applies tilts, FF skips.
|
|
91
91
|
# Set True for raw multi-panel simulation.
|
|
92
|
+
# Radial detector distortion (canonical midas_distortion v2 model). Like
|
|
93
|
+
# tilts, this maps an IDEAL prediction to the RAW detector position and is
|
|
94
|
+
# OFF by default -- the FF/pf experimental pipeline pre-corrects distortion
|
|
95
|
+
# at peak-finding time (transforms), so the forward must NOT re-apply it for
|
|
96
|
+
# the indexer/fit-grain. Raw-pixel-patch consumers (pf_odf, grain_odf) that
|
|
97
|
+
# never go through transforms set ``apply_distortion=True`` and supply the
|
|
98
|
+
# calibrated v2 coefficients to predict in the raw frame.
|
|
99
|
+
apply_distortion: bool = False
|
|
100
|
+
p_distortion: "list[float] | None" = None # 15 v2 coeffs (midas_distortion P_COEF_NAMES order); None/zeros => no-op
|
|
101
|
+
rho_d: "float | None" = None # distortion radius normalization (um); None => resolve from detector corner
|
|
92
102
|
multi_mode: str = "layered" # "layered" (default): NF semantics --
|
|
93
103
|
# spot must land on the detector at
|
|
94
104
|
# EVERY distance (AllDistsFound).
|
|
@@ -342,6 +352,25 @@ class HEDMForwardModel(nn.Module):
|
|
|
342
352
|
|
|
343
353
|
# Multi-detector / multi-panel configuration
|
|
344
354
|
self.apply_tilts = bool(geometry.apply_tilts)
|
|
355
|
+
|
|
356
|
+
# Radial detector distortion (canonical midas_distortion v2 model),
|
|
357
|
+
# applied ideal->raw in project_to_detector when apply_distortion=True.
|
|
358
|
+
# OFF by default => identity => indexer/fit-grain output unchanged.
|
|
359
|
+
self.apply_distortion = bool(getattr(geometry, "apply_distortion", False))
|
|
360
|
+
p_dist = getattr(geometry, "p_distortion", None)
|
|
361
|
+
if p_dist is not None:
|
|
362
|
+
self.p_distortion = nn.Parameter(
|
|
363
|
+
torch.as_tensor(p_dist, dtype=torch.float64, device=device),
|
|
364
|
+
requires_grad=False,
|
|
365
|
+
)
|
|
366
|
+
self._has_distortion = bool(
|
|
367
|
+
torch.any(torch.abs(self.p_distortion.detach()) > 0.0).item()
|
|
368
|
+
)
|
|
369
|
+
else:
|
|
370
|
+
self.p_distortion = None
|
|
371
|
+
self._has_distortion = False
|
|
372
|
+
self.rho_d = getattr(geometry, "rho_d", None)
|
|
373
|
+
|
|
345
374
|
if geometry.multi_mode not in ("layered", "panel"):
|
|
346
375
|
raise ValueError(
|
|
347
376
|
f"Unknown multi_mode {geometry.multi_mode!r}; "
|
|
@@ -1163,6 +1192,37 @@ class HEDMForwardModel(nn.Module):
|
|
|
1163
1192
|
ydet_d = torch.stack(out_y, dim=0)
|
|
1164
1193
|
zdet_d = torch.stack(out_z, dim=0)
|
|
1165
1194
|
|
|
1195
|
+
# Ideal->raw radial distortion (canonical midas_distortion v2 model),
|
|
1196
|
+
# applied on the BC-relative detector-plane coords (um) before pixel
|
|
1197
|
+
# conversion -- mirrors midas_calibrate_v2.forward.geometry. Gated OFF
|
|
1198
|
+
# by default so the indexer/fit-grain (ideal frame) are byte-unchanged;
|
|
1199
|
+
# raw-patch consumers (pf_odf, grain_odf) opt in. R is BC-relative, so
|
|
1200
|
+
# the distortion is frame-flip invariant; the eta convention (and any
|
|
1201
|
+
# phase offset between the calibration frame and this frame) is the one
|
|
1202
|
+
# thing to validate empirically (see implementation_plan_distortion_layer).
|
|
1203
|
+
if self.apply_distortion and self._has_distortion:
|
|
1204
|
+
from midas_distortion import apply_distortion as _apply_dist, \
|
|
1205
|
+
v2_term_layout as _v2_terms, resolve_rho_d_um as _resolve_rho_d
|
|
1206
|
+
eps = torch.tensor(1e-9, dtype=ydet_d.dtype, device=ydet_d.device)
|
|
1207
|
+
R = torch.sqrt(ydet_d * ydet_d + zdet_d * zdet_d).clamp(min=eps)
|
|
1208
|
+
# eta convention matches calibrate_v2 forward: atan2(-y, z), degrees.
|
|
1209
|
+
eta_deg_d = self.RAD2DEG * torch.atan2(-ydet_d, zdet_d)
|
|
1210
|
+
# resolve_rho_d_um passes a supplied rho_d through, or computes the
|
|
1211
|
+
# max BC-relative corner distance (um) when None.
|
|
1212
|
+
rho_d_val, _rho_how = _resolve_rho_d(
|
|
1213
|
+
self.rho_d,
|
|
1214
|
+
NrPixelsY=self.n_pixels_y, NrPixelsZ=self.n_pixels_z,
|
|
1215
|
+
BC_y=float(self._y_BC.reshape(-1)[0]),
|
|
1216
|
+
BC_z=float(self._z_BC.reshape(-1)[0]),
|
|
1217
|
+
pxY=self.px,
|
|
1218
|
+
)
|
|
1219
|
+
rho_d_t = torch.as_tensor(float(rho_d_val), dtype=R.dtype, device=R.device)
|
|
1220
|
+
p_v2 = self.p_distortion.to(R.dtype)
|
|
1221
|
+
R_corr = _apply_dist(R, eta_deg_d, p_v2, rho_d_t, terms=_v2_terms())
|
|
1222
|
+
scale = R_corr / R
|
|
1223
|
+
ydet_d = ydet_d * scale
|
|
1224
|
+
zdet_d = zdet_d * scale
|
|
1225
|
+
|
|
1166
1226
|
# FF/PF: y-axis on detector flipped (yBC - ydet/px), validated against C
|
|
1167
1227
|
# NF: not flipped (yBC + ydet/px), validated against C
|
|
1168
1228
|
y_sign = -1.0 if self.flip_y else 1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: midas-diffract
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: End-to-end differentiable forward model for High-Energy Diffraction Microscopy (FF, NF, pf-HEDM)
|
|
5
5
|
Author-email: Hemant Sharma <hsharma@anl.gov>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -18,6 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy>=1.22
|
|
20
20
|
Requires-Dist: torch>=2.0
|
|
21
|
+
Requires-Dist: midas-distortion>=0.2.0
|
|
21
22
|
Provides-Extra: dev
|
|
22
23
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
24
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -102,7 +103,7 @@ MIDAS distribution. See the companion paper and the MIDAS repository
|
|
|
102
103
|
|
|
103
104
|
## Scope
|
|
104
105
|
|
|
105
|
-
`midas-diffract` v0.1.
|
|
106
|
+
`midas-diffract` v0.1.x is deliberately focused on the forward model and its
|
|
106
107
|
gradient chain. The following capabilities build on this substrate and are
|
|
107
108
|
released separately as they mature:
|
|
108
109
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "midas-diffract"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "End-to-end differentiable forward model for High-Energy Diffraction Microscopy (FF, NF, pf-HEDM)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -26,6 +26,7 @@ classifiers = [
|
|
|
26
26
|
dependencies = [
|
|
27
27
|
"numpy>=1.22",
|
|
28
28
|
"torch>=2.0",
|
|
29
|
+
"midas-distortion>=0.2.0",
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Tests for the gated ideal->raw radial-distortion layer (midas_distortion v2).
|
|
2
|
+
|
|
3
|
+
Guarantees:
|
|
4
|
+
1. default (apply_distortion=False) is BYTE-IDENTICAL to the pre-layer forward
|
|
5
|
+
-- proves the indexer/fit-grain (ideal-frame) path is untouched.
|
|
6
|
+
2. apply_distortion=True with zero coeffs is also identity.
|
|
7
|
+
3. nonzero coeffs shift predicted spots (ideal->raw) by a sane, radius-growing
|
|
8
|
+
amount.
|
|
9
|
+
4. the layer is differentiable w.r.t. the distortion coefficients.
|
|
10
|
+
"""
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pytest
|
|
13
|
+
import torch
|
|
14
|
+
|
|
15
|
+
from midas_diffract.forward import HEDMForwardModel, HEDMGeometry
|
|
16
|
+
|
|
17
|
+
_GD = dict(
|
|
18
|
+
Lsd=752000.0, y_BC=695.0, z_BC=874.0, px=172.0,
|
|
19
|
+
omega_start=180.0, omega_step=-0.25, n_frames=1440,
|
|
20
|
+
n_pixels_y=1679, n_pixels_z=1679, min_eta=6.0, wavelength=0.172979,
|
|
21
|
+
)
|
|
22
|
+
# A representative calibrated v2 coefficient set (Bucsek Pilatus 2M CeO2).
|
|
23
|
+
_COEFFS = [0.00707, -0.01, 0.00624, 0.01, -34.76, 0.00234, 81.47,
|
|
24
|
+
-0.00369, -12.29, -0.00727, -5.29, -0.00863, -1.51, -0.00446, -7.79]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _model(**extra):
|
|
28
|
+
rng = np.random.default_rng(0)
|
|
29
|
+
hk = torch.tensor(rng.standard_normal((40, 3)), dtype=torch.float64)
|
|
30
|
+
th = torch.tensor(np.abs(rng.standard_normal(40)) * 0.04 + 0.04, dtype=torch.float64)
|
|
31
|
+
hi = torch.tensor(rng.integers(-3, 4, (40, 3)), dtype=torch.float64)
|
|
32
|
+
g = HEDMGeometry(**_GD, **extra)
|
|
33
|
+
return HEDMForwardModel(hk, th, g, hkls_int=hi).double()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_EUL = torch.tensor([[0.3, 0.5, 0.2]], dtype=torch.float64)
|
|
37
|
+
_POS = torch.zeros(1, 3, dtype=torch.float64)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_default_off_is_unchanged():
|
|
41
|
+
"""No distortion fields == apply_distortion=False == zero coeffs (identity)."""
|
|
42
|
+
o0 = _model().forward(_EUL, _POS)
|
|
43
|
+
o_off = _model(apply_distortion=False, p_distortion=[0.0] * 15).forward(_EUL, _POS)
|
|
44
|
+
o_zero = _model(apply_distortion=True, p_distortion=[0.0] * 15, rho_d=2.0e5).forward(_EUL, _POS)
|
|
45
|
+
assert torch.equal(o0.y_pixel, o_off.y_pixel)
|
|
46
|
+
assert torch.equal(o0.z_pixel, o_off.z_pixel)
|
|
47
|
+
# zero coeffs with the layer ACTIVE must also be a no-op (D == 1).
|
|
48
|
+
assert torch.allclose(o0.y_pixel, o_zero.y_pixel, atol=1e-9)
|
|
49
|
+
assert torch.allclose(o0.z_pixel, o_zero.z_pixel, atol=1e-9)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_distortion_shifts_spots():
|
|
53
|
+
o0 = _model().forward(_EUL, _POS)
|
|
54
|
+
o2 = _model(apply_distortion=True, p_distortion=_COEFFS, rho_d=2.0e5).forward(_EUL, _POS)
|
|
55
|
+
vm = (o0.valid > 0.5) & (o2.valid > 0.5)
|
|
56
|
+
assert int(vm.sum()) > 5
|
|
57
|
+
dy = (o2.y_pixel - o0.y_pixel)[vm].abs()
|
|
58
|
+
assert float(dy.median()) > 0.05 # measurable
|
|
59
|
+
assert float(dy.max()) < 50.0 # but physical (sub-module)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_distortion_is_differentiable():
|
|
63
|
+
m = _model(apply_distortion=True, p_distortion=_COEFFS, rho_d=2.0e5)
|
|
64
|
+
m.p_distortion.requires_grad_(True)
|
|
65
|
+
out = m.forward(_EUL, _POS)
|
|
66
|
+
loss = (out.y_pixel * out.valid).sum() + (out.z_pixel * out.valid).sum()
|
|
67
|
+
loss.backward()
|
|
68
|
+
assert m.p_distortion.grad is not None
|
|
69
|
+
assert float(m.p_distortion.grad.norm()) > 0.0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
|
|
73
|
+
def test_distortion_cpu_cuda_parity():
|
|
74
|
+
o_cpu = _model(apply_distortion=True, p_distortion=_COEFFS, rho_d=2.0e5).forward(_EUL, _POS)
|
|
75
|
+
m = _model(apply_distortion=True, p_distortion=_COEFFS, rho_d=2.0e5).to("cuda")
|
|
76
|
+
o_gpu = m.forward(_EUL.cuda(), _POS.cuda())
|
|
77
|
+
assert torch.allclose(o_cpu.y_pixel, o_gpu.y_pixel.cpu(), atol=1e-7)
|
|
78
|
+
assert torch.allclose(o_cpu.z_pixel, o_gpu.z_pixel.cpu(), atol=1e-7)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|