midas-ddd 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.
- midas_ddd-0.1.0/LICENSE +31 -0
- midas_ddd-0.1.0/PKG-INFO +184 -0
- midas_ddd-0.1.0/README.md +155 -0
- midas_ddd-0.1.0/midas_ddd/__init__.py +123 -0
- midas_ddd-0.1.0/midas_ddd/_g5.py +1 -0
- midas_ddd-0.1.0/midas_ddd/elasticity.py +294 -0
- midas_ddd-0.1.0/midas_ddd/exadis.py +313 -0
- midas_ddd-0.1.0/midas_ddd/fourier.py +674 -0
- midas_ddd-0.1.0/midas_ddd/generate.py +308 -0
- midas_ddd-0.1.0/midas_ddd/network.py +508 -0
- midas_ddd-0.1.0/midas_ddd/realspace.py +316 -0
- midas_ddd-0.1.0/midas_ddd/validate.py +377 -0
- midas_ddd-0.1.0/midas_ddd.egg-info/PKG-INFO +184 -0
- midas_ddd-0.1.0/midas_ddd.egg-info/SOURCES.txt +22 -0
- midas_ddd-0.1.0/midas_ddd.egg-info/dependency_links.txt +1 -0
- midas_ddd-0.1.0/midas_ddd.egg-info/requires.txt +12 -0
- midas_ddd-0.1.0/midas_ddd.egg-info/top_level.txt +1 -0
- midas_ddd-0.1.0/pyproject.toml +60 -0
- midas_ddd-0.1.0/setup.cfg +4 -0
- midas_ddd-0.1.0/tests/test_elasticity.py +271 -0
- midas_ddd-0.1.0/tests/test_exadis.py +203 -0
- midas_ddd-0.1.0/tests/test_fourier.py +657 -0
- midas_ddd-0.1.0/tests/test_network.py +450 -0
- midas_ddd-0.1.0/tests/test_realspace.py +366 -0
midas_ddd-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, UChicago Argonne, LLC, operator of Argonne National
|
|
4
|
+
Laboratory, and the midas-diffract authors.
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
|
9
|
+
|
|
10
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
18
|
+
contributors may be used to endorse or promote products derived from
|
|
19
|
+
this software without specific prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
24
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
25
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
26
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
27
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
28
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
29
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
30
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
31
|
+
POSSIBILITY OF SUCH DAMAGE.
|
midas_ddd-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: midas-ddd
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dislocation networks for MIDAS: discrete-dislocation-dynamics (ExaDiS / ParaDiS) ingest, anisotropic-elasticity (Stroh) primitives, and the differentiable real-space and Fourier displacement fields that drive the SAXS, near-Bragg diffuse and DFXM forward models.
|
|
5
|
+
Author-email: Hemant Sharma <hsharma@anl.gov>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/marinerhemant/MIDAS
|
|
8
|
+
Project-URL: Documentation, https://github.com/marinerhemant/MIDAS/tree/master/packages/midas_ddd
|
|
9
|
+
Project-URL: Issues, https://github.com/marinerhemant/MIDAS/issues
|
|
10
|
+
Keywords: MIDAS,dislocation,discrete dislocation dynamics,DDD,ExaDiS,ParaDiS,Stroh,anisotropic elasticity,dislocation loop,eigenstrain,differentiable,PyTorch
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy>=1.22
|
|
20
|
+
Requires-Dist: torch>=2.0
|
|
21
|
+
Requires-Dist: midas-stress>=0.8.0
|
|
22
|
+
Provides-Extra: exadis
|
|
23
|
+
Provides-Extra: viz
|
|
24
|
+
Requires-Dist: matplotlib>=3.5; extra == "viz"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: matplotlib>=3.5; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# midas-ddd
|
|
31
|
+
|
|
32
|
+
Dislocation networks for MIDAS: ingest a discrete-dislocation-dynamics network
|
|
33
|
+
(ExaDiS / ParaDiS) and turn it into the displacement fields that three MIDAS
|
|
34
|
+
forward models consume.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
q . u~(q) -> small-angle scattering (midas_saxs)
|
|
38
|
+
(G+q) . u~(q) -> near-Bragg diffuse / Huang (midas_defect.huang)
|
|
39
|
+
real-space beta(r) -> DFXM contrast (midas_dfxm)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Everything is torch-differentiable.
|
|
43
|
+
|
|
44
|
+
**Elastic vs total — the distinction that bites first.** `midas_ddd.fourier`
|
|
45
|
+
works with the TOTAL displacement; `midas_ddd.realspace` (Mura line integral over
|
|
46
|
+
finite segments) returns the ELASTIC distortion. Both are physical and they are
|
|
47
|
+
not the same thing: DFXM images the elastic part, because the lattice is
|
|
48
|
+
continuous across the cut surface for a perfect dislocation, while the
|
|
49
|
+
scattering kernels need the total. The two are tied together quantitatively —
|
|
50
|
+
their ball-averaged trace integrals are `(2/3)(1-2nu)/(1-nu)` and
|
|
51
|
+
`(1+nu)/(3(1-nu))` in units of `dV`, which sum to exactly 1 because the plastic
|
|
52
|
+
eigendistortion on the cut contributes `-dV`. That relation is the
|
|
53
|
+
cross-modality gate in `tests/test_realspace.py`. When the two kernels first
|
|
54
|
+
appeared to disagree by a clean functional factor, this was why.
|
|
55
|
+
|
|
56
|
+
**Scope differs between the two.** The Fourier kernel handles CLOSED LOOPS only
|
|
57
|
+
(a cut surface needs a closed circuit), so open deformation lines contribute
|
|
58
|
+
nothing at small angle and `u_tilde` reports the line length it ignored. The
|
|
59
|
+
real-space kernel has no such limit — an open network images perfectly well in
|
|
60
|
+
DFXM.
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from midas_ddd import read_paradis, validate_network, prismatic_loop
|
|
66
|
+
from midas_ddd import q_dot_u_tilde, isotropic_stiffness
|
|
67
|
+
import torch
|
|
68
|
+
|
|
69
|
+
# A network from ExaDiS, via the file bridge that always works
|
|
70
|
+
net = read_paradis("net.data", b_magnitude_A=2.556)
|
|
71
|
+
print(validate_network(net, q_max_inv_A=0.1))
|
|
72
|
+
|
|
73
|
+
# Or build one here, with no ExaDiS installed
|
|
74
|
+
loop = prismatic_loop(radius_um=0.005, burgers=(0, 0, 1), n_segments=64)
|
|
75
|
+
|
|
76
|
+
q = torch.tensor([[0.0, 0.0, 1e-2]], dtype=torch.float64) # inverse micrometers
|
|
77
|
+
amp = q_dot_u_tilde(loop, q, isotropic_stiffness(100.0, 75.0))
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Three things worth knowing before you use it
|
|
81
|
+
|
|
82
|
+
**1. Burgers magnitudes are physical; they are not normalised.** A perfect
|
|
83
|
+
dislocation has `|b| = 1` in file units, but a junction from a dislocation
|
|
84
|
+
reaction has `|b| = sqrt(2)`, and that magnitude is what balances Burgers
|
|
85
|
+
conservation at the junction node. The 5055-node FCC-Cu network in this repo has
|
|
86
|
+
54 such segments; normalising them made 60 nodes fail conservation with a
|
|
87
|
+
residual of exactly `sqrt(2) - 1`.
|
|
88
|
+
|
|
89
|
+
**2. The cut surface is physical, not a gauge.** Stokes gives the part of the
|
|
90
|
+
surface form factor perpendicular to `q` from a cheap line integral, which looks
|
|
91
|
+
like a way to avoid choosing a cut surface. It is not: for a prismatic loop
|
|
92
|
+
probed along its own normal — where it scatters most strongly — the transverse
|
|
93
|
+
projection returns *exactly zero*. The kernel integrates an explicit fan
|
|
94
|
+
triangulation from each loop's centroid. Gated by
|
|
95
|
+
`test_transverse_gauge_would_destroy_the_signal`.
|
|
96
|
+
|
|
97
|
+
**3. Only closed loops contribute.** A finite cut surface exists only for a
|
|
98
|
+
closed circuit. Open lines — the deformation population — enclose no area, carry
|
|
99
|
+
no relaxation volume, and contribute nothing as `q -> 0`. `u_tilde` reports how
|
|
100
|
+
much line length it ignored rather than quietly returning a number that looks
|
|
101
|
+
complete. Their small-angle signature is a weak transverse streak this kernel
|
|
102
|
+
does not model.
|
|
103
|
+
|
|
104
|
+
## The gate
|
|
105
|
+
|
|
106
|
+
For isotropic elasticity and a prismatic loop (`b || A || n`) the small-q limit
|
|
107
|
+
has an exact closed form, verified to 2e-15 against the tensor expression over
|
|
108
|
+
1200 random directions and four moduli:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
q . u~(q -> 0) = i dV [ kappa + (1 - kappa) (n.qhat)^2 ]
|
|
112
|
+
dV = b . A (pi R^2 |b| for a circular loop)
|
|
113
|
+
kappa = lambda / (lambda + 2 mu)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Read that twice: **the limit is anisotropic**. A loop does not scatter like a
|
|
117
|
+
compact particle of volume `dV`. It scatters `kappa dV` in its own plane and
|
|
118
|
+
`dV` along its normal — a contrast ratio of `(lambda + 2 mu)/lambda`, which is
|
|
119
|
+
2.5 for `lambda = 100, mu = 75`. A void of the same volume is isotropic.
|
|
120
|
+
|
|
121
|
+
That anisotropy is the loop-versus-void discriminator, it is present at `q -> 0`
|
|
122
|
+
rather than only at finite q, and it is why a 2-D detector image is worth
|
|
123
|
+
simulating instead of a radially averaged `I(q)`.
|
|
124
|
+
|
|
125
|
+
## ExaDiS
|
|
126
|
+
|
|
127
|
+
ExaDiS needs a Kokkos/CMake build and can never be a pip dependency. Two routes:
|
|
128
|
+
|
|
129
|
+
- **File bridge (always works).** Run ExaDiS anywhere, call its
|
|
130
|
+
`write_data(N, "net.data")`, read it with `read_paradis`.
|
|
131
|
+
- **In-process (optional).** `midas_ddd.exadis` lazily imports `pyexadis` and
|
|
132
|
+
wraps `generate_prismatic_config` (irradiation loops, `radius` accepts
|
|
133
|
+
`[min, max]`) and `generate_line_config` (deformation lines). Build with
|
|
134
|
+
`cmake .. -DEXADIS_PYTHON_BINDING=On`; on the beamline, chiltepin is the only
|
|
135
|
+
host with internet.
|
|
136
|
+
|
|
137
|
+
`midas_ddd.generate` provides small exact equivalents (`prismatic_loop`,
|
|
138
|
+
`straight_line`, `combine`) so the whole test suite runs without ExaDiS.
|
|
139
|
+
|
|
140
|
+
The in-process bridge is exercised by `tests/test_exadis.py`, which skips
|
|
141
|
+
without a build. Its load-bearing test takes BOTH routes into this package from
|
|
142
|
+
one ExaDiS config — objects converted in memory, and ExaDiS's own `write_data`
|
|
143
|
+
read back by `read_paradis` — and requires them to agree.
|
|
144
|
+
|
|
145
|
+
## Resolution
|
|
146
|
+
|
|
147
|
+
A nodal DDD code discretises a 2 nm loop into a handful of segments, and SAXS at
|
|
148
|
+
`q ~ 1/R` probes exactly that scale. `resolution_report` bounds the usable q at
|
|
149
|
+
`2 pi / L_median` and says so. The repo's FCC-Cu network has a median segment of
|
|
150
|
+
651 nm, giving `q_max ~ 1e-3 1/A` — a SAXS run at 0.1 1/A on it would be
|
|
151
|
+
reporting the polyline, not the dislocations.
|
|
152
|
+
|
|
153
|
+
## Relationship to midas_defect
|
|
154
|
+
|
|
155
|
+
The anisotropic-elasticity (Stroh) primitives here — `cubic_stiffness`,
|
|
156
|
+
`hexagonal_stiffness`, the sextic solver, the slip-system tables — used to live
|
|
157
|
+
in `midas_defect.contrast_factor`, which already carried a "do NOT re-port"
|
|
158
|
+
contract because `midas_dfxm` imported them across a package boundary. They
|
|
159
|
+
moved down when a third and fourth consumer appeared: a SAXS package should not
|
|
160
|
+
depend on an FF-HEDM metrology package to build a stiffness matrix.
|
|
161
|
+
`midas_defect.contrast_factor` re-exports every name, so no historical import
|
|
162
|
+
path broke, and `test_elasticity.py::test_reexport_*` fails loudly if anyone
|
|
163
|
+
re-ports a copy.
|
|
164
|
+
|
|
165
|
+
## Numerical traps, all of them found the hard way
|
|
166
|
+
|
|
167
|
+
**Burgers magnitudes are not normalised** (see above): 54 of the repo network's
|
|
168
|
+
5930 segments are `sqrt(2)` junctions, and normalising them broke conservation
|
|
169
|
+
at 60 nodes by exactly `sqrt(2) - 1`.
|
|
170
|
+
|
|
171
|
+
**The cut surface is subdivided RADIALLY**, not just around the circumference. A
|
|
172
|
+
plain fan from the centroid has an edge of length R running out to every vertex,
|
|
173
|
+
so the quadrature parameter `|q|h` is set by the loop radius however finely the
|
|
174
|
+
polygon is discretised. At `qR = 10` — mid-detector for real SAXS — a plain fan
|
|
175
|
+
is ~20 % wrong. `u_tilde` picks `n_rings` from `q_max` automatically.
|
|
176
|
+
|
|
177
|
+
**Polygon shape is not quadrature error.** With the quadrature converged to
|
|
178
|
+
1e-8, a 64-gon and a 256-gon still differ by ~1 % at `qR = 10`. That is the
|
|
179
|
+
polygon, a modelling choice; only `n_segments` changes it.
|
|
180
|
+
|
|
181
|
+
**Fixed-order quadrature fails the Mura line integral.** The integrand falls as
|
|
182
|
+
`1/R^2`, so eight Gauss nodes spread over a long segment miss the peak entirely:
|
|
183
|
+
a 400 um segment probed at 0.5 um came out ~1000x low and *rising* with r.
|
|
184
|
+
`realspace` subdivides into panels sized by the closest approach.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# midas-ddd
|
|
2
|
+
|
|
3
|
+
Dislocation networks for MIDAS: ingest a discrete-dislocation-dynamics network
|
|
4
|
+
(ExaDiS / ParaDiS) and turn it into the displacement fields that three MIDAS
|
|
5
|
+
forward models consume.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
q . u~(q) -> small-angle scattering (midas_saxs)
|
|
9
|
+
(G+q) . u~(q) -> near-Bragg diffuse / Huang (midas_defect.huang)
|
|
10
|
+
real-space beta(r) -> DFXM contrast (midas_dfxm)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Everything is torch-differentiable.
|
|
14
|
+
|
|
15
|
+
**Elastic vs total — the distinction that bites first.** `midas_ddd.fourier`
|
|
16
|
+
works with the TOTAL displacement; `midas_ddd.realspace` (Mura line integral over
|
|
17
|
+
finite segments) returns the ELASTIC distortion. Both are physical and they are
|
|
18
|
+
not the same thing: DFXM images the elastic part, because the lattice is
|
|
19
|
+
continuous across the cut surface for a perfect dislocation, while the
|
|
20
|
+
scattering kernels need the total. The two are tied together quantitatively —
|
|
21
|
+
their ball-averaged trace integrals are `(2/3)(1-2nu)/(1-nu)` and
|
|
22
|
+
`(1+nu)/(3(1-nu))` in units of `dV`, which sum to exactly 1 because the plastic
|
|
23
|
+
eigendistortion on the cut contributes `-dV`. That relation is the
|
|
24
|
+
cross-modality gate in `tests/test_realspace.py`. When the two kernels first
|
|
25
|
+
appeared to disagree by a clean functional factor, this was why.
|
|
26
|
+
|
|
27
|
+
**Scope differs between the two.** The Fourier kernel handles CLOSED LOOPS only
|
|
28
|
+
(a cut surface needs a closed circuit), so open deformation lines contribute
|
|
29
|
+
nothing at small angle and `u_tilde` reports the line length it ignored. The
|
|
30
|
+
real-space kernel has no such limit — an open network images perfectly well in
|
|
31
|
+
DFXM.
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from midas_ddd import read_paradis, validate_network, prismatic_loop
|
|
37
|
+
from midas_ddd import q_dot_u_tilde, isotropic_stiffness
|
|
38
|
+
import torch
|
|
39
|
+
|
|
40
|
+
# A network from ExaDiS, via the file bridge that always works
|
|
41
|
+
net = read_paradis("net.data", b_magnitude_A=2.556)
|
|
42
|
+
print(validate_network(net, q_max_inv_A=0.1))
|
|
43
|
+
|
|
44
|
+
# Or build one here, with no ExaDiS installed
|
|
45
|
+
loop = prismatic_loop(radius_um=0.005, burgers=(0, 0, 1), n_segments=64)
|
|
46
|
+
|
|
47
|
+
q = torch.tensor([[0.0, 0.0, 1e-2]], dtype=torch.float64) # inverse micrometers
|
|
48
|
+
amp = q_dot_u_tilde(loop, q, isotropic_stiffness(100.0, 75.0))
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Three things worth knowing before you use it
|
|
52
|
+
|
|
53
|
+
**1. Burgers magnitudes are physical; they are not normalised.** A perfect
|
|
54
|
+
dislocation has `|b| = 1` in file units, but a junction from a dislocation
|
|
55
|
+
reaction has `|b| = sqrt(2)`, and that magnitude is what balances Burgers
|
|
56
|
+
conservation at the junction node. The 5055-node FCC-Cu network in this repo has
|
|
57
|
+
54 such segments; normalising them made 60 nodes fail conservation with a
|
|
58
|
+
residual of exactly `sqrt(2) - 1`.
|
|
59
|
+
|
|
60
|
+
**2. The cut surface is physical, not a gauge.** Stokes gives the part of the
|
|
61
|
+
surface form factor perpendicular to `q` from a cheap line integral, which looks
|
|
62
|
+
like a way to avoid choosing a cut surface. It is not: for a prismatic loop
|
|
63
|
+
probed along its own normal — where it scatters most strongly — the transverse
|
|
64
|
+
projection returns *exactly zero*. The kernel integrates an explicit fan
|
|
65
|
+
triangulation from each loop's centroid. Gated by
|
|
66
|
+
`test_transverse_gauge_would_destroy_the_signal`.
|
|
67
|
+
|
|
68
|
+
**3. Only closed loops contribute.** A finite cut surface exists only for a
|
|
69
|
+
closed circuit. Open lines — the deformation population — enclose no area, carry
|
|
70
|
+
no relaxation volume, and contribute nothing as `q -> 0`. `u_tilde` reports how
|
|
71
|
+
much line length it ignored rather than quietly returning a number that looks
|
|
72
|
+
complete. Their small-angle signature is a weak transverse streak this kernel
|
|
73
|
+
does not model.
|
|
74
|
+
|
|
75
|
+
## The gate
|
|
76
|
+
|
|
77
|
+
For isotropic elasticity and a prismatic loop (`b || A || n`) the small-q limit
|
|
78
|
+
has an exact closed form, verified to 2e-15 against the tensor expression over
|
|
79
|
+
1200 random directions and four moduli:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
q . u~(q -> 0) = i dV [ kappa + (1 - kappa) (n.qhat)^2 ]
|
|
83
|
+
dV = b . A (pi R^2 |b| for a circular loop)
|
|
84
|
+
kappa = lambda / (lambda + 2 mu)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Read that twice: **the limit is anisotropic**. A loop does not scatter like a
|
|
88
|
+
compact particle of volume `dV`. It scatters `kappa dV` in its own plane and
|
|
89
|
+
`dV` along its normal — a contrast ratio of `(lambda + 2 mu)/lambda`, which is
|
|
90
|
+
2.5 for `lambda = 100, mu = 75`. A void of the same volume is isotropic.
|
|
91
|
+
|
|
92
|
+
That anisotropy is the loop-versus-void discriminator, it is present at `q -> 0`
|
|
93
|
+
rather than only at finite q, and it is why a 2-D detector image is worth
|
|
94
|
+
simulating instead of a radially averaged `I(q)`.
|
|
95
|
+
|
|
96
|
+
## ExaDiS
|
|
97
|
+
|
|
98
|
+
ExaDiS needs a Kokkos/CMake build and can never be a pip dependency. Two routes:
|
|
99
|
+
|
|
100
|
+
- **File bridge (always works).** Run ExaDiS anywhere, call its
|
|
101
|
+
`write_data(N, "net.data")`, read it with `read_paradis`.
|
|
102
|
+
- **In-process (optional).** `midas_ddd.exadis` lazily imports `pyexadis` and
|
|
103
|
+
wraps `generate_prismatic_config` (irradiation loops, `radius` accepts
|
|
104
|
+
`[min, max]`) and `generate_line_config` (deformation lines). Build with
|
|
105
|
+
`cmake .. -DEXADIS_PYTHON_BINDING=On`; on the beamline, chiltepin is the only
|
|
106
|
+
host with internet.
|
|
107
|
+
|
|
108
|
+
`midas_ddd.generate` provides small exact equivalents (`prismatic_loop`,
|
|
109
|
+
`straight_line`, `combine`) so the whole test suite runs without ExaDiS.
|
|
110
|
+
|
|
111
|
+
The in-process bridge is exercised by `tests/test_exadis.py`, which skips
|
|
112
|
+
without a build. Its load-bearing test takes BOTH routes into this package from
|
|
113
|
+
one ExaDiS config — objects converted in memory, and ExaDiS's own `write_data`
|
|
114
|
+
read back by `read_paradis` — and requires them to agree.
|
|
115
|
+
|
|
116
|
+
## Resolution
|
|
117
|
+
|
|
118
|
+
A nodal DDD code discretises a 2 nm loop into a handful of segments, and SAXS at
|
|
119
|
+
`q ~ 1/R` probes exactly that scale. `resolution_report` bounds the usable q at
|
|
120
|
+
`2 pi / L_median` and says so. The repo's FCC-Cu network has a median segment of
|
|
121
|
+
651 nm, giving `q_max ~ 1e-3 1/A` — a SAXS run at 0.1 1/A on it would be
|
|
122
|
+
reporting the polyline, not the dislocations.
|
|
123
|
+
|
|
124
|
+
## Relationship to midas_defect
|
|
125
|
+
|
|
126
|
+
The anisotropic-elasticity (Stroh) primitives here — `cubic_stiffness`,
|
|
127
|
+
`hexagonal_stiffness`, the sextic solver, the slip-system tables — used to live
|
|
128
|
+
in `midas_defect.contrast_factor`, which already carried a "do NOT re-port"
|
|
129
|
+
contract because `midas_dfxm` imported them across a package boundary. They
|
|
130
|
+
moved down when a third and fourth consumer appeared: a SAXS package should not
|
|
131
|
+
depend on an FF-HEDM metrology package to build a stiffness matrix.
|
|
132
|
+
`midas_defect.contrast_factor` re-exports every name, so no historical import
|
|
133
|
+
path broke, and `test_elasticity.py::test_reexport_*` fails loudly if anyone
|
|
134
|
+
re-ports a copy.
|
|
135
|
+
|
|
136
|
+
## Numerical traps, all of them found the hard way
|
|
137
|
+
|
|
138
|
+
**Burgers magnitudes are not normalised** (see above): 54 of the repo network's
|
|
139
|
+
5930 segments are `sqrt(2)` junctions, and normalising them broke conservation
|
|
140
|
+
at 60 nodes by exactly `sqrt(2) - 1`.
|
|
141
|
+
|
|
142
|
+
**The cut surface is subdivided RADIALLY**, not just around the circumference. A
|
|
143
|
+
plain fan from the centroid has an edge of length R running out to every vertex,
|
|
144
|
+
so the quadrature parameter `|q|h` is set by the loop radius however finely the
|
|
145
|
+
polygon is discretised. At `qR = 10` — mid-detector for real SAXS — a plain fan
|
|
146
|
+
is ~20 % wrong. `u_tilde` picks `n_rings` from `q_max` automatically.
|
|
147
|
+
|
|
148
|
+
**Polygon shape is not quadrature error.** With the quadrature converged to
|
|
149
|
+
1e-8, a 64-gon and a 256-gon still differ by ~1 % at `qR = 10`. That is the
|
|
150
|
+
polygon, a modelling choice; only `n_segments` changes it.
|
|
151
|
+
|
|
152
|
+
**Fixed-order quadrature fails the Mura line integral.** The integrand falls as
|
|
153
|
+
`1/R^2`, so eight Gauss nodes spread over a long segment miss the peak entirely:
|
|
154
|
+
a 400 um segment probed at 0.5 um came out ~1000x low and *rising* with r.
|
|
155
|
+
`realspace` subdivides into panels sized by the closest approach.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""midas-ddd — dislocation networks for MIDAS.
|
|
2
|
+
|
|
3
|
+
Ingests a discrete-dislocation-dynamics network (ExaDiS / ParaDiS) and turns it
|
|
4
|
+
into the displacement fields that three MIDAS forward models consume:
|
|
5
|
+
|
|
6
|
+
real space beta(r) -> midas_dfxm (DFXM contrast)
|
|
7
|
+
Fourier u~(q) -> midas_saxs (small-angle, evaluated at q)
|
|
8
|
+
Fourier u~(q) -> midas_defect (near-Bragg diffuse, at G + q)
|
|
9
|
+
|
|
10
|
+
One kernel, three evaluation points. Everything is torch-differentiable.
|
|
11
|
+
|
|
12
|
+
from midas_ddd import cubic_stiffness, fcc_slip_systems
|
|
13
|
+
|
|
14
|
+
Why this package exists
|
|
15
|
+
-----------------------
|
|
16
|
+
The anisotropic-elasticity (Stroh) primitives here were previously private to
|
|
17
|
+
``midas_defect.contrast_factor``, which already carried a "single source of
|
|
18
|
+
truth, do NOT re-port" contract because ``midas_dfxm`` imported them across a
|
|
19
|
+
package boundary. Two more consumers made that arrangement untenable: a SAXS
|
|
20
|
+
package should not have to depend on an FF-HEDM metrology package to build a
|
|
21
|
+
stiffness matrix. ``midas_defect.contrast_factor`` now re-exports every name,
|
|
22
|
+
so no historical import path broke.
|
|
23
|
+
|
|
24
|
+
ExaDiS itself requires a Kokkos/CMake build and is never a pip dependency. The
|
|
25
|
+
ParaDiS ``.data`` file bridge works without it; the in-process ``pyexadis`` path
|
|
26
|
+
is optional and imported lazily.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
__version__ = "0.1.0"
|
|
30
|
+
|
|
31
|
+
from .elasticity import (
|
|
32
|
+
bcc_slip_systems,
|
|
33
|
+
cubic_stiffness,
|
|
34
|
+
fcc_slip_systems,
|
|
35
|
+
hexagonal_stiffness,
|
|
36
|
+
)
|
|
37
|
+
from .generate import (
|
|
38
|
+
combine,
|
|
39
|
+
polygon_area_exact_um2,
|
|
40
|
+
prismatic_loop,
|
|
41
|
+
straight_line,
|
|
42
|
+
)
|
|
43
|
+
from .fourier import (
|
|
44
|
+
FourierResult,
|
|
45
|
+
acoustic_tensor,
|
|
46
|
+
isotropic_stiffness,
|
|
47
|
+
loop_small_q_limit,
|
|
48
|
+
prismatic_loop_small_q_limit,
|
|
49
|
+
prismatic_loop_small_q_limit_total,
|
|
50
|
+
q_dot_u_tilde,
|
|
51
|
+
q_dot_u_tilde_per_loop,
|
|
52
|
+
small_angle_amplitude,
|
|
53
|
+
surface_form_factor,
|
|
54
|
+
u_tilde,
|
|
55
|
+
)
|
|
56
|
+
from .network import DislocationNetwork, read_paradis, write_paradis
|
|
57
|
+
from .realspace import (
|
|
58
|
+
SegmentDislocation,
|
|
59
|
+
green_gradient_isotropic,
|
|
60
|
+
lame_from_voigt,
|
|
61
|
+
network_distortion,
|
|
62
|
+
segment_dislocations,
|
|
63
|
+
)
|
|
64
|
+
from .validate import (
|
|
65
|
+
BurgersConservationResult,
|
|
66
|
+
Loop,
|
|
67
|
+
ResolutionReport,
|
|
68
|
+
check_burgers_conservation,
|
|
69
|
+
find_loops,
|
|
70
|
+
loop_area_vectors_um2,
|
|
71
|
+
relaxation_volumes_um3,
|
|
72
|
+
resolution_report,
|
|
73
|
+
validate_network,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
__all__ = [
|
|
77
|
+
# elasticity
|
|
78
|
+
"bcc_slip_systems",
|
|
79
|
+
"cubic_stiffness",
|
|
80
|
+
"fcc_slip_systems",
|
|
81
|
+
"hexagonal_stiffness",
|
|
82
|
+
# network
|
|
83
|
+
"DislocationNetwork",
|
|
84
|
+
"read_paradis",
|
|
85
|
+
"write_paradis",
|
|
86
|
+
# generators (ExaDiS-free equivalents; see midas_ddd.exadis for the real ones)
|
|
87
|
+
"combine",
|
|
88
|
+
"polygon_area_exact_um2",
|
|
89
|
+
"prismatic_loop",
|
|
90
|
+
"straight_line",
|
|
91
|
+
# Fourier kernel
|
|
92
|
+
"FourierResult",
|
|
93
|
+
"acoustic_tensor",
|
|
94
|
+
"isotropic_stiffness",
|
|
95
|
+
"loop_small_q_limit",
|
|
96
|
+
"prismatic_loop_small_q_limit",
|
|
97
|
+
"prismatic_loop_small_q_limit_total",
|
|
98
|
+
"q_dot_u_tilde",
|
|
99
|
+
"q_dot_u_tilde_per_loop",
|
|
100
|
+
"small_angle_amplitude",
|
|
101
|
+
"surface_form_factor",
|
|
102
|
+
"u_tilde",
|
|
103
|
+
# real-space distortion (ELASTIC part; see the module docstring)
|
|
104
|
+
"SegmentDislocation",
|
|
105
|
+
"green_gradient_isotropic",
|
|
106
|
+
"lame_from_voigt",
|
|
107
|
+
"network_distortion",
|
|
108
|
+
"segment_dislocations",
|
|
109
|
+
# validation
|
|
110
|
+
"BurgersConservationResult",
|
|
111
|
+
"Loop",
|
|
112
|
+
"ResolutionReport",
|
|
113
|
+
"check_burgers_conservation",
|
|
114
|
+
"find_loops",
|
|
115
|
+
"loop_area_vectors_um2",
|
|
116
|
+
"relaxation_volumes_um3",
|
|
117
|
+
"resolution_report",
|
|
118
|
+
"validate_network",
|
|
119
|
+
"__version__",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
# `midas_ddd.exadis` is NOT imported here: it pulls pyexadis, which needs a
|
|
123
|
+
# Kokkos build. Import it explicitly when you have one.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DATASET = "smith_cu_sep24"
|