torch-scattering 0.6.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.
- torch_scattering-0.6.0/.gitignore +115 -0
- torch_scattering-0.6.0/LICENSE +29 -0
- torch_scattering-0.6.0/PKG-INFO +212 -0
- torch_scattering-0.6.0/README.md +188 -0
- torch_scattering-0.6.0/pyproject.toml +171 -0
- torch_scattering-0.6.0/src/torch_scattering/__init__.py +32 -0
- torch_scattering-0.6.0/src/torch_scattering/_core.py +309 -0
- torch_scattering-0.6.0/src/torch_scattering/firstborn.py +97 -0
- torch_scattering-0.6.0/src/torch_scattering/multislice.py +95 -0
- torch_scattering-0.6.0/src/torch_scattering/projection.py +55 -0
- torch_scattering-0.6.0/src/torch_scattering/rytov.py +94 -0
- torch_scattering-0.6.0/tests/test_core.py +167 -0
- torch_scattering-0.6.0/tests/test_firstborn.py +125 -0
- torch_scattering-0.6.0/tests/test_inputs.py +108 -0
- torch_scattering-0.6.0/tests/test_multislice.py +106 -0
- torch_scattering-0.6.0/tests/test_potential_integration.py +75 -0
- torch_scattering-0.6.0/tests/test_projection_scattering.py +31 -0
- torch_scattering-0.6.0/tests/test_rytov.py +124 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
env/
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
|
|
28
|
+
.DS_Store
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
|
|
38
|
+
# Unit test / coverage reports
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
.hypothesis/
|
|
48
|
+
.pytest_cache/
|
|
49
|
+
|
|
50
|
+
# Files downloaded for unit tests
|
|
51
|
+
**/tests/tmp/
|
|
52
|
+
|
|
53
|
+
# Translations
|
|
54
|
+
*.mo
|
|
55
|
+
*.pot
|
|
56
|
+
|
|
57
|
+
# Django stuff:
|
|
58
|
+
*.log
|
|
59
|
+
local_settings.py
|
|
60
|
+
|
|
61
|
+
# Flask stuff:
|
|
62
|
+
instance/
|
|
63
|
+
.webassets-cache
|
|
64
|
+
|
|
65
|
+
# Scrapy stuff:
|
|
66
|
+
.scrapy
|
|
67
|
+
|
|
68
|
+
# Sphinx documentation
|
|
69
|
+
docs/_build/
|
|
70
|
+
|
|
71
|
+
# PyBuilder
|
|
72
|
+
target/
|
|
73
|
+
|
|
74
|
+
# Jupyter Notebook
|
|
75
|
+
.ipynb_checkpoints
|
|
76
|
+
|
|
77
|
+
# dotenv
|
|
78
|
+
.env
|
|
79
|
+
|
|
80
|
+
# virtualenv
|
|
81
|
+
.venv
|
|
82
|
+
venv/
|
|
83
|
+
ENV/
|
|
84
|
+
|
|
85
|
+
# Spyder project settings
|
|
86
|
+
.spyderproject
|
|
87
|
+
.spyproject
|
|
88
|
+
|
|
89
|
+
# Rope project settings
|
|
90
|
+
.ropeproject
|
|
91
|
+
|
|
92
|
+
# mkdocs documentation
|
|
93
|
+
/site
|
|
94
|
+
|
|
95
|
+
# mypy
|
|
96
|
+
.mypy_cache/
|
|
97
|
+
|
|
98
|
+
# ruff
|
|
99
|
+
.ruff_cache/
|
|
100
|
+
|
|
101
|
+
# IDEs
|
|
102
|
+
.idea/
|
|
103
|
+
.vscode/
|
|
104
|
+
|
|
105
|
+
# Mojo extension compile cache (experimental torch-fourier-slice kernels)
|
|
106
|
+
__mojocache__/
|
|
107
|
+
*.mojopkg
|
|
108
|
+
# experimental demo outputs
|
|
109
|
+
packages/primitives/torch-fourier-slice/examples/*.npz
|
|
110
|
+
packages/primitives/torch-fourier-slice/examples/*.png
|
|
111
|
+
packages/primitives/torch-fourier-slice/examples/*.gif
|
|
112
|
+
# Personal local notes (not for commit)
|
|
113
|
+
notes/*.local.md
|
|
114
|
+
|
|
115
|
+
lightning_logs/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2026, TeamTomo
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: torch-scattering
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Multislice electron scattering simulation in PyTorch
|
|
5
|
+
Project-URL: homepage, https://github.com/teamtomo/teamtomo
|
|
6
|
+
Project-URL: repository, https://github.com/teamtomo/teamtomo
|
|
7
|
+
Author-email: Joel Yeo <joelyeo.is@gmail.com>
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: scipy
|
|
20
|
+
Requires-Dist: torch
|
|
21
|
+
Requires-Dist: torch-ctf
|
|
22
|
+
Requires-Dist: torch-grid-utils
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# torch-scattering
|
|
26
|
+
|
|
27
|
+
[](https://github.com/joelyeois/torch-scattering/raw/main/LICENSE)
|
|
28
|
+
[](https://pypi.org/project/torch-scattering)
|
|
29
|
+
[](https://python.org)
|
|
30
|
+
[](https://github.com/joelyeois/torch-scattering/actions/workflows/ci.yml)
|
|
31
|
+
[](https://codecov.io/gh/joelyeois/torch-scattering)
|
|
32
|
+
|
|
33
|
+
Multislice electron scattering simulation in PyTorch, for cryo-EM/cryo-ET forward modelling.
|
|
34
|
+
|
|
35
|
+
## Overview
|
|
36
|
+
|
|
37
|
+
`torch_scattering` computes the 2D exit wave produced by propagating an electron
|
|
38
|
+
beam through a 3D electrostatic potential in volts. The potential has shape
|
|
39
|
+
`(..., Z, H, W)`, where Z is the beam direction. `pixel_size` is the isotropic
|
|
40
|
+
voxel spacing in Angstroms, so it specifies both the Y/X pixel spacing and the
|
|
41
|
+
Z slice thickness. Every function returns a complex exit wave of shape
|
|
42
|
+
`(..., H, W)`.
|
|
43
|
+
|
|
44
|
+
Real `float32` and `float64` potentials model non-absorbing specimens and can be
|
|
45
|
+
passed directly; callers do not need to cast them to complex. Complex potentials
|
|
46
|
+
remain supported for modelling absorption.
|
|
47
|
+
|
|
48
|
+
Four propagation modes are provided, trading physical accuracy for speed:
|
|
49
|
+
|
|
50
|
+
* `multislice()` - full multislice propagation (Kirkland, *Advanced Computing in
|
|
51
|
+
Electron Microscopy*), alternating transmission through each slice with Fresnel
|
|
52
|
+
propagation to the next. The most accurate mode.
|
|
53
|
+
* `rytov()` - Rytov approximation, accumulating phase in the exponent rather than
|
|
54
|
+
the wave itself.
|
|
55
|
+
* `firstborn()` - first Born approximation, summing single-scattering
|
|
56
|
+
contributions from each slice.
|
|
57
|
+
* `projection()` - projection approximation, treating the specimen as infinitely
|
|
58
|
+
thin and skipping inter-slice propagation entirely. The fastest and least
|
|
59
|
+
accurate mode.
|
|
60
|
+
|
|
61
|
+
All four share the same required inputs and can be swapped in for one another.
|
|
62
|
+
`multislice`, `rytov`, and `firstborn` also accept an `n_slices` argument to
|
|
63
|
+
coarsen the potential into fewer, thicker slabs before propagating.
|
|
64
|
+
|
|
65
|
+
Lower-level, pure-math primitives (`fresnel_propagator`, `transmission_function`,
|
|
66
|
+
`multislice_step`, `chunk_slices`, `interaction_parameter`) are also exposed for
|
|
67
|
+
building custom propagation schemes.
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
```shell
|
|
72
|
+
pip install torch-scattering
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import torch
|
|
79
|
+
from torch_scattering import multislice
|
|
80
|
+
|
|
81
|
+
# A real electrostatic potential in volts, shape (Z, H, W).
|
|
82
|
+
potential = torch.zeros((50, 64, 64), dtype=torch.float32)
|
|
83
|
+
|
|
84
|
+
# propagate a plane wave through it
|
|
85
|
+
exit_wave = multislice(
|
|
86
|
+
potential=potential,
|
|
87
|
+
pixel_size=1.0, # Angstroms
|
|
88
|
+
voltage=300, # kV
|
|
89
|
+
)
|
|
90
|
+
# exit_wave.shape is (64, 64)
|
|
91
|
+
# exit_wave.dtype is torch.complex64
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`rytov`, `firstborn`, and `projection` share the same call signature:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from torch_scattering import firstborn, projection, rytov
|
|
98
|
+
|
|
99
|
+
exit_wave = rytov(potential, pixel_size=1.0, voltage=300)
|
|
100
|
+
exit_wave = firstborn(potential, pixel_size=1.0, voltage=300)
|
|
101
|
+
exit_wave = projection(potential, pixel_size=1.0, voltage=300) # n_slices not applicable
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Coarsening slices
|
|
105
|
+
|
|
106
|
+
`n_slices` groups the potential into fewer, thicker slabs before propagating.
|
|
107
|
+
By default (`n_slices=None`), every slice of the potential is propagated
|
|
108
|
+
individually - the most accurate but slowest setting.
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# propagate as 10 chunks instead of all 50 slices individually
|
|
112
|
+
exit_wave = multislice(potential, pixel_size=1.0, voltage=300, n_slices=10)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Batching
|
|
116
|
+
|
|
117
|
+
All functions accept arbitrary leading batch dimensions on `potential`:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
potential = torch.zeros((8, 50, 64, 64), dtype=torch.complex64) # batch of 8
|
|
121
|
+
exit_wave = multislice(potential, pixel_size=1.0, voltage=300)
|
|
122
|
+
# exit_wave.shape is (8, 64, 64)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Structure-to-wave pipeline
|
|
126
|
+
|
|
127
|
+
Structure handling and potential generation are deliberately separate packages.
|
|
128
|
+
They are not runtime dependencies of `torch-scattering`; their real tensor
|
|
129
|
+
output is passed through the public tensor API:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
import pandas as pd
|
|
133
|
+
from torch_calculate_electrostatic_potential import (
|
|
134
|
+
GridConfig,
|
|
135
|
+
potential_from_structure_3d,
|
|
136
|
+
)
|
|
137
|
+
from torch_scattering import multislice
|
|
138
|
+
from torch_structure_manipulation import (
|
|
139
|
+
AtomicStructure,
|
|
140
|
+
annotate_bonding_environments,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# mmdf-compatible coordinates are in Angstroms.
|
|
144
|
+
atoms = pd.DataFrame(
|
|
145
|
+
[
|
|
146
|
+
("A", 1, "ALA", "C", "C", 0.0, 0.0, 0.0),
|
|
147
|
+
("A", 1, "ALA", "O", "O", 1.2, 0.0, 0.0),
|
|
148
|
+
("A", 1, "ALA", "CA", "C", -1.2, 0.0, 0.0),
|
|
149
|
+
("A", 2, "GLY", "N", "N", 2.4, 0.0, 0.0),
|
|
150
|
+
],
|
|
151
|
+
columns=[
|
|
152
|
+
"chain", "residue_id", "residue", "atom", "element", "x", "y", "z"
|
|
153
|
+
],
|
|
154
|
+
)
|
|
155
|
+
atoms["b_isotropic"] = 10.0 # Angstrom squared
|
|
156
|
+
atoms["occupancy"] = 1.0
|
|
157
|
+
|
|
158
|
+
# Annotate a complete local residue context, then build the desired structure.
|
|
159
|
+
annotated = annotate_bonding_environments(atoms, include_hydrogens=False)
|
|
160
|
+
structure = AtomicStructure.from_dataframe(annotated.iloc[[0]])
|
|
161
|
+
|
|
162
|
+
grid = GridConfig.from_grid_shape_and_voxel_size(
|
|
163
|
+
grid_shape=(9, 9, 9), # Z, Y, X
|
|
164
|
+
voxel_size=(1.0, 1.0, 1.0), # Angstroms; isotropic for scattering
|
|
165
|
+
center_zyx=(0.0, 0.0, 0.0),
|
|
166
|
+
sublattice_radius=4.0,
|
|
167
|
+
)
|
|
168
|
+
elemental_volts = potential_from_structure_3d(structure, grid)
|
|
169
|
+
bonded_volts = potential_from_structure_3d(
|
|
170
|
+
structure,
|
|
171
|
+
grid,
|
|
172
|
+
scattering_factors="peng_bonded",
|
|
173
|
+
bonded_fallback="error",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Both volumes are real tensors in volts and are accepted directly.
|
|
177
|
+
elemental_wave = multislice(elemental_volts, pixel_size=1.0, voltage=300.0)
|
|
178
|
+
bonded_wave = multislice(bonded_volts, pixel_size=1.0, voltage=300.0)
|
|
179
|
+
# Both waves are complex tensors; voltage is in kV.
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`projection()` is a wave-propagation approximation that numerically sums this
|
|
183
|
+
sampled 3D volume along Z. It is distinct from the electrostatic package's
|
|
184
|
+
analytic 2D projected-potential calculation and from projection alignment in
|
|
185
|
+
`torch-fit-in-map`.
|
|
186
|
+
|
|
187
|
+
## Low-level primitives
|
|
188
|
+
|
|
189
|
+
For building custom propagation schemes directly on top of the multislice
|
|
190
|
+
recurrence:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
import torch
|
|
194
|
+
from torch_grid_utils import fftfreq_grid
|
|
195
|
+
from torch_scattering import (
|
|
196
|
+
fresnel_propagator,
|
|
197
|
+
interaction_parameter,
|
|
198
|
+
multislice_step,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
frequency_grid = fftfreq_grid(image_shape=(64, 64), rfft=False, spacing=1.0, norm=True)
|
|
202
|
+
propagator = fresnel_propagator(frequency_grid, wavelength=0.01969, dz=1.0)
|
|
203
|
+
sigma = interaction_parameter(voltage=300)
|
|
204
|
+
|
|
205
|
+
wave = torch.ones((64, 64), dtype=torch.complex64)
|
|
206
|
+
potential_slice = torch.zeros((64, 64), dtype=torch.complex64)
|
|
207
|
+
wave = multislice_step(wave, potential_slice, propagator, sigma, dz=1.0)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# torch-scattering
|
|
2
|
+
|
|
3
|
+
[](https://github.com/joelyeois/torch-scattering/raw/main/LICENSE)
|
|
4
|
+
[](https://pypi.org/project/torch-scattering)
|
|
5
|
+
[](https://python.org)
|
|
6
|
+
[](https://github.com/joelyeois/torch-scattering/actions/workflows/ci.yml)
|
|
7
|
+
[](https://codecov.io/gh/joelyeois/torch-scattering)
|
|
8
|
+
|
|
9
|
+
Multislice electron scattering simulation in PyTorch, for cryo-EM/cryo-ET forward modelling.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
`torch_scattering` computes the 2D exit wave produced by propagating an electron
|
|
14
|
+
beam through a 3D electrostatic potential in volts. The potential has shape
|
|
15
|
+
`(..., Z, H, W)`, where Z is the beam direction. `pixel_size` is the isotropic
|
|
16
|
+
voxel spacing in Angstroms, so it specifies both the Y/X pixel spacing and the
|
|
17
|
+
Z slice thickness. Every function returns a complex exit wave of shape
|
|
18
|
+
`(..., H, W)`.
|
|
19
|
+
|
|
20
|
+
Real `float32` and `float64` potentials model non-absorbing specimens and can be
|
|
21
|
+
passed directly; callers do not need to cast them to complex. Complex potentials
|
|
22
|
+
remain supported for modelling absorption.
|
|
23
|
+
|
|
24
|
+
Four propagation modes are provided, trading physical accuracy for speed:
|
|
25
|
+
|
|
26
|
+
* `multislice()` - full multislice propagation (Kirkland, *Advanced Computing in
|
|
27
|
+
Electron Microscopy*), alternating transmission through each slice with Fresnel
|
|
28
|
+
propagation to the next. The most accurate mode.
|
|
29
|
+
* `rytov()` - Rytov approximation, accumulating phase in the exponent rather than
|
|
30
|
+
the wave itself.
|
|
31
|
+
* `firstborn()` - first Born approximation, summing single-scattering
|
|
32
|
+
contributions from each slice.
|
|
33
|
+
* `projection()` - projection approximation, treating the specimen as infinitely
|
|
34
|
+
thin and skipping inter-slice propagation entirely. The fastest and least
|
|
35
|
+
accurate mode.
|
|
36
|
+
|
|
37
|
+
All four share the same required inputs and can be swapped in for one another.
|
|
38
|
+
`multislice`, `rytov`, and `firstborn` also accept an `n_slices` argument to
|
|
39
|
+
coarsen the potential into fewer, thicker slabs before propagating.
|
|
40
|
+
|
|
41
|
+
Lower-level, pure-math primitives (`fresnel_propagator`, `transmission_function`,
|
|
42
|
+
`multislice_step`, `chunk_slices`, `interaction_parameter`) are also exposed for
|
|
43
|
+
building custom propagation schemes.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
pip install torch-scattering
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import torch
|
|
55
|
+
from torch_scattering import multislice
|
|
56
|
+
|
|
57
|
+
# A real electrostatic potential in volts, shape (Z, H, W).
|
|
58
|
+
potential = torch.zeros((50, 64, 64), dtype=torch.float32)
|
|
59
|
+
|
|
60
|
+
# propagate a plane wave through it
|
|
61
|
+
exit_wave = multislice(
|
|
62
|
+
potential=potential,
|
|
63
|
+
pixel_size=1.0, # Angstroms
|
|
64
|
+
voltage=300, # kV
|
|
65
|
+
)
|
|
66
|
+
# exit_wave.shape is (64, 64)
|
|
67
|
+
# exit_wave.dtype is torch.complex64
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`rytov`, `firstborn`, and `projection` share the same call signature:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from torch_scattering import firstborn, projection, rytov
|
|
74
|
+
|
|
75
|
+
exit_wave = rytov(potential, pixel_size=1.0, voltage=300)
|
|
76
|
+
exit_wave = firstborn(potential, pixel_size=1.0, voltage=300)
|
|
77
|
+
exit_wave = projection(potential, pixel_size=1.0, voltage=300) # n_slices not applicable
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Coarsening slices
|
|
81
|
+
|
|
82
|
+
`n_slices` groups the potential into fewer, thicker slabs before propagating.
|
|
83
|
+
By default (`n_slices=None`), every slice of the potential is propagated
|
|
84
|
+
individually - the most accurate but slowest setting.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# propagate as 10 chunks instead of all 50 slices individually
|
|
88
|
+
exit_wave = multislice(potential, pixel_size=1.0, voltage=300, n_slices=10)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Batching
|
|
92
|
+
|
|
93
|
+
All functions accept arbitrary leading batch dimensions on `potential`:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
potential = torch.zeros((8, 50, 64, 64), dtype=torch.complex64) # batch of 8
|
|
97
|
+
exit_wave = multislice(potential, pixel_size=1.0, voltage=300)
|
|
98
|
+
# exit_wave.shape is (8, 64, 64)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Structure-to-wave pipeline
|
|
102
|
+
|
|
103
|
+
Structure handling and potential generation are deliberately separate packages.
|
|
104
|
+
They are not runtime dependencies of `torch-scattering`; their real tensor
|
|
105
|
+
output is passed through the public tensor API:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
import pandas as pd
|
|
109
|
+
from torch_calculate_electrostatic_potential import (
|
|
110
|
+
GridConfig,
|
|
111
|
+
potential_from_structure_3d,
|
|
112
|
+
)
|
|
113
|
+
from torch_scattering import multislice
|
|
114
|
+
from torch_structure_manipulation import (
|
|
115
|
+
AtomicStructure,
|
|
116
|
+
annotate_bonding_environments,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# mmdf-compatible coordinates are in Angstroms.
|
|
120
|
+
atoms = pd.DataFrame(
|
|
121
|
+
[
|
|
122
|
+
("A", 1, "ALA", "C", "C", 0.0, 0.0, 0.0),
|
|
123
|
+
("A", 1, "ALA", "O", "O", 1.2, 0.0, 0.0),
|
|
124
|
+
("A", 1, "ALA", "CA", "C", -1.2, 0.0, 0.0),
|
|
125
|
+
("A", 2, "GLY", "N", "N", 2.4, 0.0, 0.0),
|
|
126
|
+
],
|
|
127
|
+
columns=[
|
|
128
|
+
"chain", "residue_id", "residue", "atom", "element", "x", "y", "z"
|
|
129
|
+
],
|
|
130
|
+
)
|
|
131
|
+
atoms["b_isotropic"] = 10.0 # Angstrom squared
|
|
132
|
+
atoms["occupancy"] = 1.0
|
|
133
|
+
|
|
134
|
+
# Annotate a complete local residue context, then build the desired structure.
|
|
135
|
+
annotated = annotate_bonding_environments(atoms, include_hydrogens=False)
|
|
136
|
+
structure = AtomicStructure.from_dataframe(annotated.iloc[[0]])
|
|
137
|
+
|
|
138
|
+
grid = GridConfig.from_grid_shape_and_voxel_size(
|
|
139
|
+
grid_shape=(9, 9, 9), # Z, Y, X
|
|
140
|
+
voxel_size=(1.0, 1.0, 1.0), # Angstroms; isotropic for scattering
|
|
141
|
+
center_zyx=(0.0, 0.0, 0.0),
|
|
142
|
+
sublattice_radius=4.0,
|
|
143
|
+
)
|
|
144
|
+
elemental_volts = potential_from_structure_3d(structure, grid)
|
|
145
|
+
bonded_volts = potential_from_structure_3d(
|
|
146
|
+
structure,
|
|
147
|
+
grid,
|
|
148
|
+
scattering_factors="peng_bonded",
|
|
149
|
+
bonded_fallback="error",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Both volumes are real tensors in volts and are accepted directly.
|
|
153
|
+
elemental_wave = multislice(elemental_volts, pixel_size=1.0, voltage=300.0)
|
|
154
|
+
bonded_wave = multislice(bonded_volts, pixel_size=1.0, voltage=300.0)
|
|
155
|
+
# Both waves are complex tensors; voltage is in kV.
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`projection()` is a wave-propagation approximation that numerically sums this
|
|
159
|
+
sampled 3D volume along Z. It is distinct from the electrostatic package's
|
|
160
|
+
analytic 2D projected-potential calculation and from projection alignment in
|
|
161
|
+
`torch-fit-in-map`.
|
|
162
|
+
|
|
163
|
+
## Low-level primitives
|
|
164
|
+
|
|
165
|
+
For building custom propagation schemes directly on top of the multislice
|
|
166
|
+
recurrence:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
import torch
|
|
170
|
+
from torch_grid_utils import fftfreq_grid
|
|
171
|
+
from torch_scattering import (
|
|
172
|
+
fresnel_propagator,
|
|
173
|
+
interaction_parameter,
|
|
174
|
+
multislice_step,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
frequency_grid = fftfreq_grid(image_shape=(64, 64), rfft=False, spacing=1.0, norm=True)
|
|
178
|
+
propagator = fresnel_propagator(frequency_grid, wavelength=0.01969, dz=1.0)
|
|
179
|
+
sigma = interaction_parameter(voltage=300)
|
|
180
|
+
|
|
181
|
+
wave = torch.ones((64, 64), dtype=torch.complex64)
|
|
182
|
+
potential_slice = torch.zeros((64, 64), dtype=torch.complex64)
|
|
183
|
+
wave = multislice_step(wave, potential_slice, propagator, sigma, dz=1.0)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.
|