phaserEM 0.1__tar.gz → 0.3.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.
- phaserem-0.3.0/PKG-INFO +201 -0
- phaserem-0.3.0/README.md +130 -0
- phaserem-0.3.0/phaser/__init__.py +1 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/__main__.py +1 -1
- phaserem-0.3.0/phaser/cli/__init__.py +161 -0
- phaserem-0.3.0/phaser/cli/calc_drift.py +424 -0
- phaserem-0.3.0/phaser/cli/process_empad.py +308 -0
- phaserem-0.3.0/phaser/cli/validate.py +51 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/noise_models.py +17 -10
- {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/output.py +69 -5
- {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/position_correction.py +2 -2
- {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/regularizers.py +225 -37
- {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/simulation.py +112 -24
- phaserem-0.3.0/phaser/engines/conventional/run.py +146 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/conventional/solvers.py +113 -37
- phaserem-0.3.0/phaser/engines/gradient/run.py +617 -0
- phaserem-0.3.0/phaser/engines/gradient/solvers.py +307 -0
- phaserem-0.3.0/phaser/engines/gradient/synthetic.py +125 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/execute.py +245 -113
- phaserem-0.3.0/phaser/hooks/__init__.py +255 -0
- phaserem-0.3.0/phaser/hooks/_dependencies.py +46 -0
- phaserem-0.3.0/phaser/hooks/filter.py +70 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/hook.py +17 -6
- {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/io/empad.py +16 -6
- phaserem-0.3.0/phaser/hooks/io/gatan.py +98 -0
- phaserem-0.3.0/phaser/hooks/io/manual.py +169 -0
- phaserem-0.3.0/phaser/hooks/io/nion.py +116 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/object.py +4 -3
- phaserem-0.3.0/phaser/hooks/preprocessing.py +211 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/probe.py +9 -5
- {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/regularization.py +40 -7
- phaserem-0.3.0/phaser/hooks/scan.py +48 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/schedule.py +4 -2
- {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/solver.py +15 -7
- phaserem-0.3.0/phaser/hooks/tilt.py +55 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/io/empad.py +109 -11
- phaserem-0.3.0/phaser/io/gatan.py +298 -0
- phaserem-0.3.0/phaser/io/nion.py +107 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/main.py +29 -9
- phaserem-0.3.0/phaser/observer.py +361 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/plan.py +81 -12
- {phaserem-0.1 → phaserem-0.3.0}/phaser/state.py +137 -64
- {phaserem-0.1 → phaserem-0.3.0}/phaser/types.py +110 -16
- {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/_cuda_kernels.py +34 -0
- phaserem-0.3.0/phaser/utils/_jax_kernels.py +231 -0
- phaserem-0.3.0/phaser/utils/_torch_kernels.py +683 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/analysis.py +42 -8
- phaserem-0.3.0/phaser/utils/config.py +188 -0
- phaserem-0.3.0/phaser/utils/image.py +1581 -0
- phaserem-0.3.0/phaser/utils/io.py +611 -0
- phaserem-0.3.0/phaser/utils/misc.py +529 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/num.py +500 -138
- {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/object.py +72 -31
- {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/optics.py +35 -9
- {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/physics.py +6 -1
- {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/plotting.py +13 -6
- phaserem-0.3.0/phaser/utils/scan.py +64 -0
- phaserem-0.3.0/phaser/utils/tree.py +466 -0
- phaserem-0.3.0/phaser/version.py +67 -0
- phaserem-0.3.0/phaser/web/debug.py +140 -0
- phaserem-0.3.0/phaser/web/pubsub.py +320 -0
- phaserem-0.3.0/phaser/web/routes.py +381 -0
- phaserem-0.3.0/phaser/web/server.py +949 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/web/slurm.py +3 -0
- phaserem-0.3.0/phaser/web/static/apple-touch-icon.png +3 -0
- phaserem-0.3.0/phaser/web/static/dist/dashboard.js +3 -0
- phaserem-0.3.0/phaser/web/static/dist/dashboard.js.LICENSE.txt +6 -0
- phaserem-0.3.0/phaser/web/static/dist/dashboard.js.map +1 -0
- phaserem-0.3.0/phaser/web/static/dist/error.js +2 -0
- phaserem-0.3.0/phaser/web/static/dist/error.js.map +1 -0
- phaserem-0.3.0/phaser/web/static/dist/manager.js +3 -0
- phaserem-0.3.0/phaser/web/static/dist/manager.js.LICENSE.txt +6 -0
- phaserem-0.3.0/phaser/web/static/dist/manager.js.map +1 -0
- phaserem-0.3.0/phaser/web/static/dist/runtime.js +2 -0
- phaserem-0.3.0/phaser/web/static/dist/runtime.js.map +1 -0
- phaserem-0.3.0/phaser/web/static/dist/shared.js +3 -0
- phaserem-0.3.0/phaser/web/static/dist/shared.js.LICENSE.txt +58 -0
- phaserem-0.3.0/phaser/web/static/dist/shared.js.map +1 -0
- phaserem-0.3.0/phaser/web/static/favicon.ico +0 -0
- phaserem-0.3.0/phaser/web/static/icon.svg +27 -0
- phaserem-0.3.0/phaser/web/static/logo-text.svg +29 -0
- phaserem-0.3.0/phaser/web/static/logo.svg +21 -0
- phaserem-0.3.0/phaser/web/templates/base.html +19 -0
- phaserem-0.3.0/phaser/web/templates/dashboard.html +10 -0
- phaserem-0.3.0/phaser/web/templates/error.html +19 -0
- phaserem-0.3.0/phaser/web/templates/manager.html +10 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/web/types.py +125 -77
- {phaserem-0.1 → phaserem-0.3.0}/phaser/web/util.py +50 -21
- phaserem-0.3.0/phaser/web/views.py +209 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/web/worker.py +76 -25
- phaserem-0.3.0/phaserEM.egg-info/PKG-INFO +201 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaserEM.egg-info/SOURCES.txt +44 -7
- phaserem-0.3.0/phaserEM.egg-info/entry_points.txt +2 -0
- phaserem-0.3.0/phaserEM.egg-info/requires.txt +55 -0
- {phaserem-0.1 → phaserem-0.3.0}/pyproject.toml +68 -16
- phaserem-0.3.0/tests/test_engines.py +164 -0
- phaserem-0.3.0/tests/test_image.py +909 -0
- phaserem-0.3.0/tests/test_initialization.py +391 -0
- phaserem-0.3.0/tests/test_load.py +150 -0
- {phaserem-0.1 → phaserem-0.3.0}/tests/test_misc.py +5 -5
- phaserem-0.3.0/tests/test_num.py +366 -0
- {phaserem-0.1 → phaserem-0.3.0}/tests/test_object.py +39 -23
- phaserem-0.3.0/tests/test_optics.py +135 -0
- {phaserem-0.1 → phaserem-0.3.0}/tests/test_physics.py +8 -1
- phaserem-0.3.0/tests/test_state.py +255 -0
- phaserem-0.1/PKG-INFO +0 -121
- phaserem-0.1/README.md +0 -69
- phaserem-0.1/phaser/engines/conventional/run.py +0 -142
- phaserem-0.1/phaser/engines/gradient/run.py +0 -451
- phaserem-0.1/phaser/engines/gradient/solvers.py +0 -139
- phaserem-0.1/phaser/hooks/__init__.py +0 -158
- phaserem-0.1/phaser/hooks/preprocessing.py +0 -133
- phaserem-0.1/phaser/hooks/scan.py +0 -27
- phaserem-0.1/phaser/utils/_jax_kernels.py +0 -98
- phaserem-0.1/phaser/utils/image.py +0 -201
- phaserem-0.1/phaser/utils/io.py +0 -402
- phaserem-0.1/phaser/utils/misc.py +0 -295
- phaserem-0.1/phaser/utils/scan.py +0 -60
- phaserem-0.1/phaser/web/__init__.py +0 -0
- phaserem-0.1/phaser/web/dist/03510a839ccb97b0da9f.module.wasm +0 -0
- phaserem-0.1/phaser/web/dist/9573273f862f4f5d9644.module.wasm +0 -0
- phaserem-0.1/phaser/web/dist/bundle-dashboard.js +0 -712
- phaserem-0.1/phaser/web/dist/bundle-manager.js +0 -210
- phaserem-0.1/phaser/web/dist/bundle-vendors-node_modules_wasm-array_wasm_array_js.js +0 -106
- phaserem-0.1/phaser/web/dist/style.css +0 -152
- phaserem-0.1/phaser/web/routes.py +0 -211
- phaserem-0.1/phaser/web/server.py +0 -540
- phaserem-0.1/phaser/web/templates/base.html +0 -13
- phaserem-0.1/phaser/web/templates/dashboard.html +0 -14
- phaserem-0.1/phaser/web/templates/manager.html +0 -19
- phaserem-0.1/phaserEM.egg-info/PKG-INFO +0 -121
- phaserem-0.1/phaserEM.egg-info/entry_points.txt +0 -2
- phaserem-0.1/phaserEM.egg-info/requires.txt +0 -36
- phaserem-0.1/tests/test_image.py +0 -76
- phaserem-0.1/tests/test_initialization.py +0 -138
- phaserem-0.1/tests/test_num.py +0 -200
- phaserem-0.1/tests/test_optics.py +0 -58
- {phaserem-0.1 → phaserem-0.3.0}/LICENSE.txt +0 -0
- {phaserem-0.1/phaser → phaserem-0.3.0/phaser/engines/common}/__init__.py +0 -0
- {phaserem-0.1/phaser/engines/common → phaserem-0.3.0/phaser/engines/conventional}/__init__.py +0 -0
- {phaserem-0.1/phaser/engines/conventional → phaserem-0.3.0/phaser/io}/__init__.py +0 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/py.typed +0 -0
- {phaserem-0.1/phaser/io → phaserem-0.3.0/phaser/utils}/__init__.py +0 -0
- {phaserem-0.1/phaser/utils → phaserem-0.3.0/phaser/web}/__init__.py +0 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaser/web/notebook.py +0 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaserEM.egg-info/dependency_links.txt +0 -0
- {phaserem-0.1 → phaserem-0.3.0}/phaserEM.egg-info/top_level.txt +0 -0
- {phaserem-0.1 → phaserem-0.3.0}/setup.cfg +0 -0
- {phaserem-0.1 → phaserem-0.3.0}/setup.py +0 -0
- {phaserem-0.1 → phaserem-0.3.0}/tests/test_empad.py +0 -0
phaserem-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phaserEM
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Weapon of choice for electron ptychographic reconstructions
|
|
5
|
+
Author-email: Colin Gilgenbach <hexane@mit.edu>
|
|
6
|
+
License-Expression: MPL-2.0
|
|
7
|
+
Project-URL: Repository, https://github.com/hexane360/phaser.git
|
|
8
|
+
Project-URL: Documentation, https://hexane360.github.io/phaser/dev
|
|
9
|
+
Keywords: science,atoms,materials,chemistry
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE.txt
|
|
23
|
+
Requires-Dist: numpy<2.7,>=2.0
|
|
24
|
+
Requires-Dist: scipy<1.19,>=1.7.0
|
|
25
|
+
Requires-Dist: matplotlib~=3.8
|
|
26
|
+
Requires-Dist: h5py~=3.8
|
|
27
|
+
Requires-Dist: pyyaml>=5.3.1
|
|
28
|
+
Requires-Dist: click~=8.1.0
|
|
29
|
+
Requires-Dist: frozendict~=2.4.7
|
|
30
|
+
Requires-Dist: rich<15,>=12.0.0
|
|
31
|
+
Requires-Dist: tifffile>=2023.8.25
|
|
32
|
+
Requires-Dist: optree>=0.13.0
|
|
33
|
+
Requires-Dist: py-pane==0.11.7
|
|
34
|
+
Requires-Dist: typing_extensions~=4.7
|
|
35
|
+
Requires-Dist: platformdirs>=4.8.0
|
|
36
|
+
Requires-Dist: PyYAML>=6.0
|
|
37
|
+
Requires-Dist: lxml>=5.0.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=6.2.4; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: coveragepy-lcov>=0.1.1; extra == "dev"
|
|
42
|
+
Provides-Extra: cupy11
|
|
43
|
+
Requires-Dist: cupy-cuda11x>=12.0.0; extra == "cupy11"
|
|
44
|
+
Requires-Dist: pynvml>=11.0.0; extra == "cupy11"
|
|
45
|
+
Provides-Extra: cupy12
|
|
46
|
+
Requires-Dist: cupy-cuda12x>=12.0.0; extra == "cupy12"
|
|
47
|
+
Requires-Dist: pynvml>=11.0.0; extra == "cupy12"
|
|
48
|
+
Provides-Extra: cupy13
|
|
49
|
+
Requires-Dist: cupy-cuda13x>=12.0.0; extra == "cupy13"
|
|
50
|
+
Requires-Dist: pynvml>=11.0.0; extra == "cupy13"
|
|
51
|
+
Provides-Extra: jax
|
|
52
|
+
Requires-Dist: jax<0.11,>=0.4.25; extra == "jax"
|
|
53
|
+
Requires-Dist: optax>=0.2.2; extra == "jax"
|
|
54
|
+
Provides-Extra: torch
|
|
55
|
+
Requires-Dist: torch>=2.8.0; extra == "torch"
|
|
56
|
+
Provides-Extra: web
|
|
57
|
+
Requires-Dist: Quart>=0.20.0; extra == "web"
|
|
58
|
+
Requires-Dist: backoff==2.2.1; extra == "web"
|
|
59
|
+
Requires-Dist: requests>=2.31.0; extra == "web"
|
|
60
|
+
Requires-Dist: anywidget>=0.11.0; extra == "web"
|
|
61
|
+
Requires-Dist: jupyter-ws-tunnel==0.1.3; extra == "web"
|
|
62
|
+
Requires-Dist: hypercorn==0.18.0; extra == "web"
|
|
63
|
+
Provides-Extra: docs
|
|
64
|
+
Requires-Dist: mkdocs==1.6.1; extra == "docs"
|
|
65
|
+
Requires-Dist: mkdocs-material==9.6.11; extra == "docs"
|
|
66
|
+
Requires-Dist: mkdocstrings[python]==0.29.1; extra == "docs"
|
|
67
|
+
Requires-Dist: markdown-include==0.8.1; extra == "docs"
|
|
68
|
+
Requires-Dist: mike==2.1.3; extra == "docs"
|
|
69
|
+
Requires-Dist: black>=25.1.0; extra == "docs"
|
|
70
|
+
Dynamic: license-file
|
|
71
|
+
|
|
72
|
+
phaser: The weapon of choice for ptychographic reconstructions
|
|
73
|
+
---
|
|
74
|
+
[![][ci-badge]][ci-url] [![][commit-badge]][commit-url] [![][docs-dev-badge]][docs-dev-url] [![][binder-badge]][binder-url]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
`phaser` is a fully-featured package for multislice electron ptychography.
|
|
78
|
+
See our [arXiv paper](https://arxiv.org/abs/2505.14372) for more details.
|
|
79
|
+
|
|
80
|
+
## Features
|
|
81
|
+
|
|
82
|
+
- ePIE, LSQ-MLs, and gradient descent algorithms
|
|
83
|
+
- [`numpy`][numpy], [`cupy`][cupy], and [`jax`][jax] backends.
|
|
84
|
+
- Single and multislice ptychography
|
|
85
|
+
- Multiple incoherent probe modes
|
|
86
|
+
- Probe position correction
|
|
87
|
+
- Upsampled (sPIE) and segmented ptychography (work in progress)
|
|
88
|
+
- Adaptive propagator correction (contributed by M Zhu)
|
|
89
|
+
|
|
90
|
+
## Documentation
|
|
91
|
+
|
|
92
|
+
Documentation on `phaser` can be found here: https://hexane360.github.io/phaser/dev/
|
|
93
|
+
|
|
94
|
+
Documentation is still very much a work in progress, so please feel free to open an issue or email me if you have any questions!
|
|
95
|
+
|
|
96
|
+
## Installation
|
|
97
|
+
|
|
98
|
+
To install, first clone the repository from github.
|
|
99
|
+
This can be done from GitHub Desktop, or from the git command line:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
$ git clone https://github.com/hexane360/phaser
|
|
103
|
+
# enter phaser directory
|
|
104
|
+
$ cd phaser
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
We recommend using a conda environment or Python virtual environment to keep things clean, although this is not mandatory.
|
|
108
|
+
|
|
109
|
+
`phaser` supports multiple computational backends. The simplest (and slowest) is `numpy`. `cupy` can be used for CUDA-accelerated. `jax` supports CPU and GPU acceleration, and is the only backend which supports the gradient descent engine.
|
|
110
|
+
If you're unsure what engines to use, we recommend installing the `jax` engine.
|
|
111
|
+
|
|
112
|
+
If you're using [`cupy`][cupy] or [`jax`][jax] with a GPU, start by following the installation instructions for those packages.
|
|
113
|
+
Jax can be installed with or without CUDA support, if you're using CUDA make sure you install the correct version. Currently, Jax does not support CUDA on Windows.
|
|
114
|
+
|
|
115
|
+
Before moving on to installing `phaser`, make sure those packages you've installed work:
|
|
116
|
+
```sh
|
|
117
|
+
$ python
|
|
118
|
+
>>> import jax
|
|
119
|
+
>>> jax.default_backend()
|
|
120
|
+
'gpu' # should be 'gpu' on cuda, 'cpu' otherwise
|
|
121
|
+
>>> jax.numpy.array([1, 2, 3, 4]) + 1 # test a basic operation
|
|
122
|
+
Array([2, 3, 4, 5], dtype=int32)
|
|
123
|
+
|
|
124
|
+
>>> import cupy
|
|
125
|
+
>>> cupy.array([1, 2, 3, 4]) + 1
|
|
126
|
+
array([2, 3, 4, 5])
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Then, install `phaser` using `pip`:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
$ python -m pip install -e .
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For the jax or cupy backend, or for the optional webserver, install with the corresponding options:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
$ python -m pip install -e ".[jax,cupy12,web]" # for the 'jax', 'cupy12', and 'web' options
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Depending on your command line, you may need to put double quotes around the options (as shown).
|
|
142
|
+
|
|
143
|
+
Here are the supported installation options:
|
|
144
|
+
|
|
145
|
+
- `jax`: For the [`jax`][jax] backend (required for the gradient descent engine)
|
|
146
|
+
- `cupy11`: `cupy` for CUDA toolkit 11.x
|
|
147
|
+
- `cupy12`: `cupy` for CUDA toolkit 12.x
|
|
148
|
+
- `web`: For the web interface
|
|
149
|
+
|
|
150
|
+
For [Optuna](https://optuna.org/) hyperoptimization, install it as well:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
$ pip install optuna
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Running
|
|
157
|
+
|
|
158
|
+
After installation, the `phaser` command should be available. Phaser can be run from the command line, or through a job server.
|
|
159
|
+
|
|
160
|
+
To run a single reconstruction on the command line, call `phaser run <file>`, where `file` is the path to a reconstruction plan file.
|
|
161
|
+
|
|
162
|
+
To run the webserver, call `phaser serve`. By default, the server serves on https://localhost:5050/, so navigate there in a web browser. The server interface can be used to start workers and schedule reconstruction jobs.
|
|
163
|
+
|
|
164
|
+
To run a worker, call `phaser worker <url>`, where `url` is the URL of a running job server.
|
|
165
|
+
|
|
166
|
+
## Sample data & Examples
|
|
167
|
+
|
|
168
|
+
Sample data can be downloaded from the following dropbox link: https://www.dropbox.com/scl/fo/txm3k88ubrzvt541v23ir/AL-l_m6VnGlFxzHWZSSc0TA?rlkey=8qxtwnc8cwhpff6jpr5s40y6i&st=x9pbwke0&dl=0
|
|
169
|
+
|
|
170
|
+
Copy the `sample_data` directory into the root code folder.
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
$ curl --output sample_data.zip -L 'https://www.dropbox.com/scl/fo/txm3k88ubrzvt541v23ir/AL-l_m6VnGlFxzHWZSSc0TA?rlkey=8qxtwnc8cwhpff6jpr5s40y6i&st=x9pbwke0&dl=1'
|
|
174
|
+
$ unzip sample_data.zip -x / -d sample_data
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Sample data includes simulated and experimental MoS2 data, simulated and experimental Si data, and experimental PrScO3 data.
|
|
178
|
+
After the data is downloaded, any of the example reconstructions can be run as `phaser run examples/mos2_grad.yaml` (for example).
|
|
179
|
+
|
|
180
|
+
## Alternatives
|
|
181
|
+
|
|
182
|
+
Other notable ptychography packages:
|
|
183
|
+
|
|
184
|
+
- [`fold_slice`](https://github.com/yijiang1/fold_slice) branch of [PtychoShelves](https://www.psi.ch/en/sls/csaxs/software#coming-soon-ptychoshelves-a-versatile-high-level-framework-for-high-performance-analysis-of)
|
|
185
|
+
- [`py4DSTEM`](https://github.com/py4dstem/py4DSTEM)
|
|
186
|
+
- [`PtyLab.m/py/jl`](https://opg.optica.org/oe/fulltext.cfm?uri=oe-31-9-13763&id=529026)
|
|
187
|
+
- [PtyPy](https://ptycho.github.io/ptypy/)
|
|
188
|
+
- [PtyRAD](https://github.com/chiahao3/ptyrad)
|
|
189
|
+
|
|
190
|
+
[numpy]: https://numpy.org/
|
|
191
|
+
[cupy]: https://cupy.dev/
|
|
192
|
+
[jax]: https://docs.jax.dev/en/latest/
|
|
193
|
+
|
|
194
|
+
[ci-badge]: https://github.com/hexane360/phaser/workflows/Tests/badge.svg
|
|
195
|
+
[ci-url]: https://github.com/hexane360/phaser/actions/workflows/ci.yaml
|
|
196
|
+
[docs-dev-badge]: https://img.shields.io/badge/docs-dev-blue
|
|
197
|
+
[docs-dev-url]: https://hexane360.github.io/phaser/dev/
|
|
198
|
+
[commit-badge]: https://img.shields.io/github/last-commit/hexane360/phaser
|
|
199
|
+
[commit-url]: https://github.com/hexane360/phaser/commits
|
|
200
|
+
[binder-badge]: https://mybinder.org/badge_logo.svg
|
|
201
|
+
[binder-url]: https://mybinder.org/v2/gh/hexane360/phaser/HEAD
|
phaserem-0.3.0/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
phaser: The weapon of choice for ptychographic reconstructions
|
|
2
|
+
---
|
|
3
|
+
[![][ci-badge]][ci-url] [![][commit-badge]][commit-url] [![][docs-dev-badge]][docs-dev-url] [![][binder-badge]][binder-url]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
`phaser` is a fully-featured package for multislice electron ptychography.
|
|
7
|
+
See our [arXiv paper](https://arxiv.org/abs/2505.14372) for more details.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- ePIE, LSQ-MLs, and gradient descent algorithms
|
|
12
|
+
- [`numpy`][numpy], [`cupy`][cupy], and [`jax`][jax] backends.
|
|
13
|
+
- Single and multislice ptychography
|
|
14
|
+
- Multiple incoherent probe modes
|
|
15
|
+
- Probe position correction
|
|
16
|
+
- Upsampled (sPIE) and segmented ptychography (work in progress)
|
|
17
|
+
- Adaptive propagator correction (contributed by M Zhu)
|
|
18
|
+
|
|
19
|
+
## Documentation
|
|
20
|
+
|
|
21
|
+
Documentation on `phaser` can be found here: https://hexane360.github.io/phaser/dev/
|
|
22
|
+
|
|
23
|
+
Documentation is still very much a work in progress, so please feel free to open an issue or email me if you have any questions!
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
To install, first clone the repository from github.
|
|
28
|
+
This can be done from GitHub Desktop, or from the git command line:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
$ git clone https://github.com/hexane360/phaser
|
|
32
|
+
# enter phaser directory
|
|
33
|
+
$ cd phaser
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
We recommend using a conda environment or Python virtual environment to keep things clean, although this is not mandatory.
|
|
37
|
+
|
|
38
|
+
`phaser` supports multiple computational backends. The simplest (and slowest) is `numpy`. `cupy` can be used for CUDA-accelerated. `jax` supports CPU and GPU acceleration, and is the only backend which supports the gradient descent engine.
|
|
39
|
+
If you're unsure what engines to use, we recommend installing the `jax` engine.
|
|
40
|
+
|
|
41
|
+
If you're using [`cupy`][cupy] or [`jax`][jax] with a GPU, start by following the installation instructions for those packages.
|
|
42
|
+
Jax can be installed with or without CUDA support, if you're using CUDA make sure you install the correct version. Currently, Jax does not support CUDA on Windows.
|
|
43
|
+
|
|
44
|
+
Before moving on to installing `phaser`, make sure those packages you've installed work:
|
|
45
|
+
```sh
|
|
46
|
+
$ python
|
|
47
|
+
>>> import jax
|
|
48
|
+
>>> jax.default_backend()
|
|
49
|
+
'gpu' # should be 'gpu' on cuda, 'cpu' otherwise
|
|
50
|
+
>>> jax.numpy.array([1, 2, 3, 4]) + 1 # test a basic operation
|
|
51
|
+
Array([2, 3, 4, 5], dtype=int32)
|
|
52
|
+
|
|
53
|
+
>>> import cupy
|
|
54
|
+
>>> cupy.array([1, 2, 3, 4]) + 1
|
|
55
|
+
array([2, 3, 4, 5])
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then, install `phaser` using `pip`:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
$ python -m pip install -e .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For the jax or cupy backend, or for the optional webserver, install with the corresponding options:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
$ python -m pip install -e ".[jax,cupy12,web]" # for the 'jax', 'cupy12', and 'web' options
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Depending on your command line, you may need to put double quotes around the options (as shown).
|
|
71
|
+
|
|
72
|
+
Here are the supported installation options:
|
|
73
|
+
|
|
74
|
+
- `jax`: For the [`jax`][jax] backend (required for the gradient descent engine)
|
|
75
|
+
- `cupy11`: `cupy` for CUDA toolkit 11.x
|
|
76
|
+
- `cupy12`: `cupy` for CUDA toolkit 12.x
|
|
77
|
+
- `web`: For the web interface
|
|
78
|
+
|
|
79
|
+
For [Optuna](https://optuna.org/) hyperoptimization, install it as well:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
$ pip install optuna
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Running
|
|
86
|
+
|
|
87
|
+
After installation, the `phaser` command should be available. Phaser can be run from the command line, or through a job server.
|
|
88
|
+
|
|
89
|
+
To run a single reconstruction on the command line, call `phaser run <file>`, where `file` is the path to a reconstruction plan file.
|
|
90
|
+
|
|
91
|
+
To run the webserver, call `phaser serve`. By default, the server serves on https://localhost:5050/, so navigate there in a web browser. The server interface can be used to start workers and schedule reconstruction jobs.
|
|
92
|
+
|
|
93
|
+
To run a worker, call `phaser worker <url>`, where `url` is the URL of a running job server.
|
|
94
|
+
|
|
95
|
+
## Sample data & Examples
|
|
96
|
+
|
|
97
|
+
Sample data can be downloaded from the following dropbox link: https://www.dropbox.com/scl/fo/txm3k88ubrzvt541v23ir/AL-l_m6VnGlFxzHWZSSc0TA?rlkey=8qxtwnc8cwhpff6jpr5s40y6i&st=x9pbwke0&dl=0
|
|
98
|
+
|
|
99
|
+
Copy the `sample_data` directory into the root code folder.
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
$ curl --output sample_data.zip -L 'https://www.dropbox.com/scl/fo/txm3k88ubrzvt541v23ir/AL-l_m6VnGlFxzHWZSSc0TA?rlkey=8qxtwnc8cwhpff6jpr5s40y6i&st=x9pbwke0&dl=1'
|
|
103
|
+
$ unzip sample_data.zip -x / -d sample_data
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Sample data includes simulated and experimental MoS2 data, simulated and experimental Si data, and experimental PrScO3 data.
|
|
107
|
+
After the data is downloaded, any of the example reconstructions can be run as `phaser run examples/mos2_grad.yaml` (for example).
|
|
108
|
+
|
|
109
|
+
## Alternatives
|
|
110
|
+
|
|
111
|
+
Other notable ptychography packages:
|
|
112
|
+
|
|
113
|
+
- [`fold_slice`](https://github.com/yijiang1/fold_slice) branch of [PtychoShelves](https://www.psi.ch/en/sls/csaxs/software#coming-soon-ptychoshelves-a-versatile-high-level-framework-for-high-performance-analysis-of)
|
|
114
|
+
- [`py4DSTEM`](https://github.com/py4dstem/py4DSTEM)
|
|
115
|
+
- [`PtyLab.m/py/jl`](https://opg.optica.org/oe/fulltext.cfm?uri=oe-31-9-13763&id=529026)
|
|
116
|
+
- [PtyPy](https://ptycho.github.io/ptypy/)
|
|
117
|
+
- [PtyRAD](https://github.com/chiahao3/ptyrad)
|
|
118
|
+
|
|
119
|
+
[numpy]: https://numpy.org/
|
|
120
|
+
[cupy]: https://cupy.dev/
|
|
121
|
+
[jax]: https://docs.jax.dev/en/latest/
|
|
122
|
+
|
|
123
|
+
[ci-badge]: https://github.com/hexane360/phaser/workflows/Tests/badge.svg
|
|
124
|
+
[ci-url]: https://github.com/hexane360/phaser/actions/workflows/ci.yaml
|
|
125
|
+
[docs-dev-badge]: https://img.shields.io/badge/docs-dev-blue
|
|
126
|
+
[docs-dev-url]: https://hexane360.github.io/phaser/dev/
|
|
127
|
+
[commit-badge]: https://img.shields.io/github/last-commit/hexane360/phaser
|
|
128
|
+
[commit-url]: https://github.com/hexane360/phaser/commits
|
|
129
|
+
[binder-badge]: https://mybinder.org/badge_logo.svg
|
|
130
|
+
[binder-url]: https://mybinder.org/v2/gh/hexane360/phaser/HEAD
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.3.0'
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import importlib
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import sys
|
|
5
|
+
import typing as t
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
class Dependency(abc.ABC):
|
|
10
|
+
@abc.abstractmethod
|
|
11
|
+
def check(self):
|
|
12
|
+
...
|
|
13
|
+
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def install_instructions(self) -> str:
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ImportDependency(Dependency):
|
|
20
|
+
def __init__(self, ref: str, install: str) -> None:
|
|
21
|
+
self.ref = ref
|
|
22
|
+
self.install = install
|
|
23
|
+
|
|
24
|
+
def check(self):
|
|
25
|
+
importlib.import_module(self.ref)
|
|
26
|
+
|
|
27
|
+
def install_instructions(self) -> str:
|
|
28
|
+
return self.install
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def check_dependencies(command: str, dependencies: t.Sequence[str]):
|
|
32
|
+
for dependency in dependencies:
|
|
33
|
+
if (dep := _DEPENDENCIES.get(dependency)) is None:
|
|
34
|
+
raise RuntimeError(f"Unknown dependency '{dependency}'. This is likely a bug in the hook declaration.")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
dep.check()
|
|
38
|
+
except Exception as e:
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
f"Missing dependency '{dependency}' required for subcommand '{command}'.\n"
|
|
41
|
+
f"To install: {dep.install_instructions()}"
|
|
42
|
+
) from e
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MainCommand(click.MultiCommand):
|
|
46
|
+
def __init__(self, commands: t.Iterable[
|
|
47
|
+
t.Union[click.Command, t.Tuple[str, str], t.Tuple[str, str, t.Sequence[str]]]
|
|
48
|
+
], **kwargs):
|
|
49
|
+
super().__init__(**kwargs)
|
|
50
|
+
# name: command or short_help
|
|
51
|
+
self.commands: t.Dict[str, t.Union[click.Command, str, None]] = {}
|
|
52
|
+
self.dependencies: t.Dict[str, t.Sequence[str]] = {}
|
|
53
|
+
for c in commands:
|
|
54
|
+
if isinstance(c, click.Command):
|
|
55
|
+
name = str(c.name)
|
|
56
|
+
self.commands[name] = c
|
|
57
|
+
self.dependencies[name] = ()
|
|
58
|
+
continue
|
|
59
|
+
self.commands[c[0]] = c[1]
|
|
60
|
+
if len(c) > 2:
|
|
61
|
+
self.dependencies[c[0]] = c[2]
|
|
62
|
+
|
|
63
|
+
def list_commands(self, ctx: click.Context):
|
|
64
|
+
return list(self.commands.keys())
|
|
65
|
+
|
|
66
|
+
def format_commands(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
|
|
67
|
+
from gettext import gettext
|
|
68
|
+
|
|
69
|
+
if len(self.commands):
|
|
70
|
+
limit = formatter.width - 6 - max(map(len, self.commands.keys()))
|
|
71
|
+
|
|
72
|
+
rows = []
|
|
73
|
+
for name, cmd in self.commands.items():
|
|
74
|
+
help = cmd.get_short_help_str(limit) if isinstance(cmd, click.Command) else cmd
|
|
75
|
+
rows.append((name, help))
|
|
76
|
+
|
|
77
|
+
if rows:
|
|
78
|
+
with formatter.section(gettext("Commands")):
|
|
79
|
+
formatter.write_dl(rows)
|
|
80
|
+
|
|
81
|
+
def get_command(self, ctx: click.Context, cmd_name: str) -> t.Optional[click.Command]:
|
|
82
|
+
name = cmd_name.lower()
|
|
83
|
+
val = (self.commands.get(name) or
|
|
84
|
+
self.commands.get(name.replace('-', '_')))
|
|
85
|
+
if val is None:
|
|
86
|
+
return None
|
|
87
|
+
if isinstance(val, click.BaseCommand):
|
|
88
|
+
return val
|
|
89
|
+
|
|
90
|
+
check_dependencies(name, self.dependencies.get(name, ()))
|
|
91
|
+
|
|
92
|
+
# for now, assume `validate` command is `cli.validate:validate`
|
|
93
|
+
module = func = name
|
|
94
|
+
mod = __import__(module, globals(), fromlist=[func], level=1)
|
|
95
|
+
return getattr(mod, func)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@click.command()
|
|
99
|
+
@click.argument('path', type=click.Path(exists=True, dir_okay=False))
|
|
100
|
+
def run(path: t.Union[str, Path]):
|
|
101
|
+
"""Execute a reconstruction plan"""
|
|
102
|
+
from phaser.plan import ReconsPlan
|
|
103
|
+
from phaser.execute import execute_plan
|
|
104
|
+
plans = ReconsPlan.from_yaml_all(path)
|
|
105
|
+
|
|
106
|
+
for plan in plans:
|
|
107
|
+
execute_plan(plan)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@click.command()
|
|
111
|
+
@click.option('--host', type=str, default='localhost', help="Host to serve on")
|
|
112
|
+
@click.option('--port', type=int, help="Port to serve on")
|
|
113
|
+
@click.option('-v', '--verbose', count=True, help="Increase verbosity")
|
|
114
|
+
@click.option('--debug', is_flag=True, help="Enable debug routes (/debug/*), for testing the web interface")
|
|
115
|
+
def serve(host: str = 'localhost', port: t.Optional[int] = None, verbose: int = 0, debug: bool = False):
|
|
116
|
+
"""Run phaser server"""
|
|
117
|
+
from phaser.web.server import server
|
|
118
|
+
|
|
119
|
+
if ':' in host:
|
|
120
|
+
(host, port_from_host) = host.rsplit(':', maxsplit=1)
|
|
121
|
+
try:
|
|
122
|
+
port_from_host = int(port_from_host)
|
|
123
|
+
except ValueError:
|
|
124
|
+
print(f"Invalid host '{host}:{port_from_host}'", file=sys.stderr)
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
|
|
127
|
+
port = port or port_from_host
|
|
128
|
+
|
|
129
|
+
server.run(hostname=host, port=port, verbosity=verbose, debug=debug)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@click.command()
|
|
133
|
+
@click.argument('url', type=str, required=True)
|
|
134
|
+
@click.option('--quiet/--loud', default=False, help="Whether to print output to stdout")
|
|
135
|
+
def worker(url: str, quiet: bool = False):
|
|
136
|
+
"""
|
|
137
|
+
Run phaser worker.
|
|
138
|
+
|
|
139
|
+
URL is the server URL to connect to.
|
|
140
|
+
"""
|
|
141
|
+
from phaser.web.worker import run_worker
|
|
142
|
+
|
|
143
|
+
run_worker(url, quiet=quiet)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
_DEPENDENCIES = {
|
|
147
|
+
'rsciio': ImportDependency('rsciio', "'pip install rosettasciio' or 'conda install rosettasciio'"),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
commands: t.List[t.Union[click.Command, t.Union[t.Tuple[str, str, t.Sequence[str]], t.Tuple[str, str]]]] = [
|
|
151
|
+
run, serve, worker,
|
|
152
|
+
# these will be looked up in the cli folder
|
|
153
|
+
('validate', "Validate reconstruction plan file"),
|
|
154
|
+
('process_empad', "Process EMPAD XML metadata"),
|
|
155
|
+
('calc_drift', "Calculate and correct linear drift"),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@click.command(cls=MainCommand, commands=commands)
|
|
160
|
+
def cli():
|
|
161
|
+
pass
|