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.
Files changed (150) hide show
  1. phaserem-0.3.0/PKG-INFO +201 -0
  2. phaserem-0.3.0/README.md +130 -0
  3. phaserem-0.3.0/phaser/__init__.py +1 -0
  4. {phaserem-0.1 → phaserem-0.3.0}/phaser/__main__.py +1 -1
  5. phaserem-0.3.0/phaser/cli/__init__.py +161 -0
  6. phaserem-0.3.0/phaser/cli/calc_drift.py +424 -0
  7. phaserem-0.3.0/phaser/cli/process_empad.py +308 -0
  8. phaserem-0.3.0/phaser/cli/validate.py +51 -0
  9. {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/noise_models.py +17 -10
  10. {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/output.py +69 -5
  11. {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/position_correction.py +2 -2
  12. {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/regularizers.py +225 -37
  13. {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/common/simulation.py +112 -24
  14. phaserem-0.3.0/phaser/engines/conventional/run.py +146 -0
  15. {phaserem-0.1 → phaserem-0.3.0}/phaser/engines/conventional/solvers.py +113 -37
  16. phaserem-0.3.0/phaser/engines/gradient/run.py +617 -0
  17. phaserem-0.3.0/phaser/engines/gradient/solvers.py +307 -0
  18. phaserem-0.3.0/phaser/engines/gradient/synthetic.py +125 -0
  19. {phaserem-0.1 → phaserem-0.3.0}/phaser/execute.py +245 -113
  20. phaserem-0.3.0/phaser/hooks/__init__.py +255 -0
  21. phaserem-0.3.0/phaser/hooks/_dependencies.py +46 -0
  22. phaserem-0.3.0/phaser/hooks/filter.py +70 -0
  23. {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/hook.py +17 -6
  24. {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/io/empad.py +16 -6
  25. phaserem-0.3.0/phaser/hooks/io/gatan.py +98 -0
  26. phaserem-0.3.0/phaser/hooks/io/manual.py +169 -0
  27. phaserem-0.3.0/phaser/hooks/io/nion.py +116 -0
  28. {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/object.py +4 -3
  29. phaserem-0.3.0/phaser/hooks/preprocessing.py +211 -0
  30. {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/probe.py +9 -5
  31. {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/regularization.py +40 -7
  32. phaserem-0.3.0/phaser/hooks/scan.py +48 -0
  33. {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/schedule.py +4 -2
  34. {phaserem-0.1 → phaserem-0.3.0}/phaser/hooks/solver.py +15 -7
  35. phaserem-0.3.0/phaser/hooks/tilt.py +55 -0
  36. {phaserem-0.1 → phaserem-0.3.0}/phaser/io/empad.py +109 -11
  37. phaserem-0.3.0/phaser/io/gatan.py +298 -0
  38. phaserem-0.3.0/phaser/io/nion.py +107 -0
  39. {phaserem-0.1 → phaserem-0.3.0}/phaser/main.py +29 -9
  40. phaserem-0.3.0/phaser/observer.py +361 -0
  41. {phaserem-0.1 → phaserem-0.3.0}/phaser/plan.py +81 -12
  42. {phaserem-0.1 → phaserem-0.3.0}/phaser/state.py +137 -64
  43. {phaserem-0.1 → phaserem-0.3.0}/phaser/types.py +110 -16
  44. {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/_cuda_kernels.py +34 -0
  45. phaserem-0.3.0/phaser/utils/_jax_kernels.py +231 -0
  46. phaserem-0.3.0/phaser/utils/_torch_kernels.py +683 -0
  47. {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/analysis.py +42 -8
  48. phaserem-0.3.0/phaser/utils/config.py +188 -0
  49. phaserem-0.3.0/phaser/utils/image.py +1581 -0
  50. phaserem-0.3.0/phaser/utils/io.py +611 -0
  51. phaserem-0.3.0/phaser/utils/misc.py +529 -0
  52. {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/num.py +500 -138
  53. {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/object.py +72 -31
  54. {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/optics.py +35 -9
  55. {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/physics.py +6 -1
  56. {phaserem-0.1 → phaserem-0.3.0}/phaser/utils/plotting.py +13 -6
  57. phaserem-0.3.0/phaser/utils/scan.py +64 -0
  58. phaserem-0.3.0/phaser/utils/tree.py +466 -0
  59. phaserem-0.3.0/phaser/version.py +67 -0
  60. phaserem-0.3.0/phaser/web/debug.py +140 -0
  61. phaserem-0.3.0/phaser/web/pubsub.py +320 -0
  62. phaserem-0.3.0/phaser/web/routes.py +381 -0
  63. phaserem-0.3.0/phaser/web/server.py +949 -0
  64. {phaserem-0.1 → phaserem-0.3.0}/phaser/web/slurm.py +3 -0
  65. phaserem-0.3.0/phaser/web/static/apple-touch-icon.png +3 -0
  66. phaserem-0.3.0/phaser/web/static/dist/dashboard.js +3 -0
  67. phaserem-0.3.0/phaser/web/static/dist/dashboard.js.LICENSE.txt +6 -0
  68. phaserem-0.3.0/phaser/web/static/dist/dashboard.js.map +1 -0
  69. phaserem-0.3.0/phaser/web/static/dist/error.js +2 -0
  70. phaserem-0.3.0/phaser/web/static/dist/error.js.map +1 -0
  71. phaserem-0.3.0/phaser/web/static/dist/manager.js +3 -0
  72. phaserem-0.3.0/phaser/web/static/dist/manager.js.LICENSE.txt +6 -0
  73. phaserem-0.3.0/phaser/web/static/dist/manager.js.map +1 -0
  74. phaserem-0.3.0/phaser/web/static/dist/runtime.js +2 -0
  75. phaserem-0.3.0/phaser/web/static/dist/runtime.js.map +1 -0
  76. phaserem-0.3.0/phaser/web/static/dist/shared.js +3 -0
  77. phaserem-0.3.0/phaser/web/static/dist/shared.js.LICENSE.txt +58 -0
  78. phaserem-0.3.0/phaser/web/static/dist/shared.js.map +1 -0
  79. phaserem-0.3.0/phaser/web/static/favicon.ico +0 -0
  80. phaserem-0.3.0/phaser/web/static/icon.svg +27 -0
  81. phaserem-0.3.0/phaser/web/static/logo-text.svg +29 -0
  82. phaserem-0.3.0/phaser/web/static/logo.svg +21 -0
  83. phaserem-0.3.0/phaser/web/templates/base.html +19 -0
  84. phaserem-0.3.0/phaser/web/templates/dashboard.html +10 -0
  85. phaserem-0.3.0/phaser/web/templates/error.html +19 -0
  86. phaserem-0.3.0/phaser/web/templates/manager.html +10 -0
  87. {phaserem-0.1 → phaserem-0.3.0}/phaser/web/types.py +125 -77
  88. {phaserem-0.1 → phaserem-0.3.0}/phaser/web/util.py +50 -21
  89. phaserem-0.3.0/phaser/web/views.py +209 -0
  90. {phaserem-0.1 → phaserem-0.3.0}/phaser/web/worker.py +76 -25
  91. phaserem-0.3.0/phaserEM.egg-info/PKG-INFO +201 -0
  92. {phaserem-0.1 → phaserem-0.3.0}/phaserEM.egg-info/SOURCES.txt +44 -7
  93. phaserem-0.3.0/phaserEM.egg-info/entry_points.txt +2 -0
  94. phaserem-0.3.0/phaserEM.egg-info/requires.txt +55 -0
  95. {phaserem-0.1 → phaserem-0.3.0}/pyproject.toml +68 -16
  96. phaserem-0.3.0/tests/test_engines.py +164 -0
  97. phaserem-0.3.0/tests/test_image.py +909 -0
  98. phaserem-0.3.0/tests/test_initialization.py +391 -0
  99. phaserem-0.3.0/tests/test_load.py +150 -0
  100. {phaserem-0.1 → phaserem-0.3.0}/tests/test_misc.py +5 -5
  101. phaserem-0.3.0/tests/test_num.py +366 -0
  102. {phaserem-0.1 → phaserem-0.3.0}/tests/test_object.py +39 -23
  103. phaserem-0.3.0/tests/test_optics.py +135 -0
  104. {phaserem-0.1 → phaserem-0.3.0}/tests/test_physics.py +8 -1
  105. phaserem-0.3.0/tests/test_state.py +255 -0
  106. phaserem-0.1/PKG-INFO +0 -121
  107. phaserem-0.1/README.md +0 -69
  108. phaserem-0.1/phaser/engines/conventional/run.py +0 -142
  109. phaserem-0.1/phaser/engines/gradient/run.py +0 -451
  110. phaserem-0.1/phaser/engines/gradient/solvers.py +0 -139
  111. phaserem-0.1/phaser/hooks/__init__.py +0 -158
  112. phaserem-0.1/phaser/hooks/preprocessing.py +0 -133
  113. phaserem-0.1/phaser/hooks/scan.py +0 -27
  114. phaserem-0.1/phaser/utils/_jax_kernels.py +0 -98
  115. phaserem-0.1/phaser/utils/image.py +0 -201
  116. phaserem-0.1/phaser/utils/io.py +0 -402
  117. phaserem-0.1/phaser/utils/misc.py +0 -295
  118. phaserem-0.1/phaser/utils/scan.py +0 -60
  119. phaserem-0.1/phaser/web/__init__.py +0 -0
  120. phaserem-0.1/phaser/web/dist/03510a839ccb97b0da9f.module.wasm +0 -0
  121. phaserem-0.1/phaser/web/dist/9573273f862f4f5d9644.module.wasm +0 -0
  122. phaserem-0.1/phaser/web/dist/bundle-dashboard.js +0 -712
  123. phaserem-0.1/phaser/web/dist/bundle-manager.js +0 -210
  124. phaserem-0.1/phaser/web/dist/bundle-vendors-node_modules_wasm-array_wasm_array_js.js +0 -106
  125. phaserem-0.1/phaser/web/dist/style.css +0 -152
  126. phaserem-0.1/phaser/web/routes.py +0 -211
  127. phaserem-0.1/phaser/web/server.py +0 -540
  128. phaserem-0.1/phaser/web/templates/base.html +0 -13
  129. phaserem-0.1/phaser/web/templates/dashboard.html +0 -14
  130. phaserem-0.1/phaser/web/templates/manager.html +0 -19
  131. phaserem-0.1/phaserEM.egg-info/PKG-INFO +0 -121
  132. phaserem-0.1/phaserEM.egg-info/entry_points.txt +0 -2
  133. phaserem-0.1/phaserEM.egg-info/requires.txt +0 -36
  134. phaserem-0.1/tests/test_image.py +0 -76
  135. phaserem-0.1/tests/test_initialization.py +0 -138
  136. phaserem-0.1/tests/test_num.py +0 -200
  137. phaserem-0.1/tests/test_optics.py +0 -58
  138. {phaserem-0.1 → phaserem-0.3.0}/LICENSE.txt +0 -0
  139. {phaserem-0.1/phaser → phaserem-0.3.0/phaser/engines/common}/__init__.py +0 -0
  140. {phaserem-0.1/phaser/engines/common → phaserem-0.3.0/phaser/engines/conventional}/__init__.py +0 -0
  141. {phaserem-0.1/phaser/engines/conventional → phaserem-0.3.0/phaser/io}/__init__.py +0 -0
  142. {phaserem-0.1 → phaserem-0.3.0}/phaser/py.typed +0 -0
  143. {phaserem-0.1/phaser/io → phaserem-0.3.0/phaser/utils}/__init__.py +0 -0
  144. {phaserem-0.1/phaser/utils → phaserem-0.3.0/phaser/web}/__init__.py +0 -0
  145. {phaserem-0.1 → phaserem-0.3.0}/phaser/web/notebook.py +0 -0
  146. {phaserem-0.1 → phaserem-0.3.0}/phaserEM.egg-info/dependency_links.txt +0 -0
  147. {phaserem-0.1 → phaserem-0.3.0}/phaserEM.egg-info/top_level.txt +0 -0
  148. {phaserem-0.1 → phaserem-0.3.0}/setup.cfg +0 -0
  149. {phaserem-0.1 → phaserem-0.3.0}/setup.py +0 -0
  150. {phaserem-0.1 → phaserem-0.3.0}/tests/test_empad.py +0 -0
@@ -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
@@ -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'
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
  if __name__ == '__main__':
4
- from phaser.main import cli
4
+ from phaser.cli import cli
5
5
  cli()
@@ -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