ptychodus 1.0__py3-none-any.whl

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 (306) hide show
  1. ptychodus/__init__.py +19 -0
  2. ptychodus/__main__.py +145 -0
  3. ptychodus/api/__init__.py +0 -0
  4. ptychodus/api/fluorescence.py +62 -0
  5. ptychodus/api/geometry.py +150 -0
  6. ptychodus/api/object.py +218 -0
  7. ptychodus/api/observer.py +92 -0
  8. ptychodus/api/parametric.py +480 -0
  9. ptychodus/api/patterns.py +148 -0
  10. ptychodus/api/plugins.py +185 -0
  11. ptychodus/api/probe.py +282 -0
  12. ptychodus/api/product.py +83 -0
  13. ptychodus/api/propagator.py +147 -0
  14. ptychodus/api/reconstructor.py +122 -0
  15. ptychodus/api/scan.py +112 -0
  16. ptychodus/api/settings.py +108 -0
  17. ptychodus/api/tree.py +38 -0
  18. ptychodus/api/typing.py +10 -0
  19. ptychodus/api/units.py +3 -0
  20. ptychodus/api/visualization.py +208 -0
  21. ptychodus/api/workflow.py +122 -0
  22. ptychodus/controller/__init__.py +5 -0
  23. ptychodus/controller/agent/__init__.py +6 -0
  24. ptychodus/controller/agent/core.py +163 -0
  25. ptychodus/controller/agent/item_delegate.py +121 -0
  26. ptychodus/controller/agent/list_model.py +34 -0
  27. ptychodus/controller/automation.py +205 -0
  28. ptychodus/controller/core.py +183 -0
  29. ptychodus/controller/data.py +110 -0
  30. ptychodus/controller/image.py +241 -0
  31. ptychodus/controller/memory.py +30 -0
  32. ptychodus/controller/object/__init__.py +5 -0
  33. ptychodus/controller/object/core.py +233 -0
  34. ptychodus/controller/object/editor_factory.py +79 -0
  35. ptychodus/controller/object/frc.py +58 -0
  36. ptychodus/controller/object/tree_model.py +227 -0
  37. ptychodus/controller/object/xmcd.py +112 -0
  38. ptychodus/controller/parametric.py +785 -0
  39. ptychodus/controller/patterns/__init__.py +5 -0
  40. ptychodus/controller/patterns/core.py +148 -0
  41. ptychodus/controller/patterns/dataset.py +184 -0
  42. ptychodus/controller/patterns/info.py +123 -0
  43. ptychodus/controller/patterns/wizard/__init__.py +5 -0
  44. ptychodus/controller/patterns/wizard/core.py +62 -0
  45. ptychodus/controller/patterns/wizard/files.py +322 -0
  46. ptychodus/controller/patterns/wizard/metadata.py +81 -0
  47. ptychodus/controller/patterns/wizard/patterns.py +303 -0
  48. ptychodus/controller/probe/__init__.py +5 -0
  49. ptychodus/controller/probe/core.py +282 -0
  50. ptychodus/controller/probe/editor_factory.py +296 -0
  51. ptychodus/controller/probe/fluorescence.py +292 -0
  52. ptychodus/controller/probe/illumination.py +95 -0
  53. ptychodus/controller/probe/propagator.py +173 -0
  54. ptychodus/controller/probe/stxm.py +91 -0
  55. ptychodus/controller/probe/tree_model.py +235 -0
  56. ptychodus/controller/probe/zernike.py +106 -0
  57. ptychodus/controller/product/__init__.py +5 -0
  58. ptychodus/controller/product/core.py +342 -0
  59. ptychodus/controller/product/editor.py +196 -0
  60. ptychodus/controller/ptychi/__init__.py +5 -0
  61. ptychodus/controller/ptychi/core.py +97 -0
  62. ptychodus/controller/ptychi/object.py +472 -0
  63. ptychodus/controller/ptychi/opr.py +113 -0
  64. ptychodus/controller/ptychi/optimizer.py +93 -0
  65. ptychodus/controller/ptychi/positions.py +296 -0
  66. ptychodus/controller/ptychi/probe.py +259 -0
  67. ptychodus/controller/ptychi/reconstructor.py +313 -0
  68. ptychodus/controller/ptychonn.py +51 -0
  69. ptychodus/controller/ptychopinn.py +217 -0
  70. ptychodus/controller/reconstructor.py +301 -0
  71. ptychodus/controller/scan/__init__.py +5 -0
  72. ptychodus/controller/scan/core.py +225 -0
  73. ptychodus/controller/scan/editor_factory.py +229 -0
  74. ptychodus/controller/scan/table_model.py +114 -0
  75. ptychodus/controller/settings.py +127 -0
  76. ptychodus/controller/tike/__init__.py +5 -0
  77. ptychodus/controller/tike/core.py +58 -0
  78. ptychodus/controller/tike/view_controllers.py +248 -0
  79. ptychodus/controller/visualization/__init__.py +9 -0
  80. ptychodus/controller/visualization/controller.py +191 -0
  81. ptychodus/controller/visualization/parameters.py +71 -0
  82. ptychodus/controller/visualization/widget.py +38 -0
  83. ptychodus/controller/workflow/__init__.py +5 -0
  84. ptychodus/controller/workflow/authorization.py +54 -0
  85. ptychodus/controller/workflow/compute.py +71 -0
  86. ptychodus/controller/workflow/controller.py +49 -0
  87. ptychodus/controller/workflow/execution.py +59 -0
  88. ptychodus/controller/workflow/input_data.py +51 -0
  89. ptychodus/controller/workflow/output_data.py +59 -0
  90. ptychodus/controller/workflow/status.py +78 -0
  91. ptychodus/controller/workflow/table_model.py +71 -0
  92. ptychodus/model/__init__.py +5 -0
  93. ptychodus/model/agent/__init__.py +13 -0
  94. ptychodus/model/agent/argo.py +73 -0
  95. ptychodus/model/agent/chat.py +81 -0
  96. ptychodus/model/agent/core.py +49 -0
  97. ptychodus/model/agent/properties.py +50 -0
  98. ptychodus/model/agent/settings.py +35 -0
  99. ptychodus/model/analysis/__init__.py +20 -0
  100. ptychodus/model/analysis/affine.py +154 -0
  101. ptychodus/model/analysis/barycentric.py +112 -0
  102. ptychodus/model/analysis/core.py +41 -0
  103. ptychodus/model/analysis/frc.py +105 -0
  104. ptychodus/model/analysis/illumination.py +138 -0
  105. ptychodus/model/analysis/propagator.py +158 -0
  106. ptychodus/model/analysis/settings.py +36 -0
  107. ptychodus/model/analysis/stxm.py +106 -0
  108. ptychodus/model/analysis/xmcd.py +137 -0
  109. ptychodus/model/automation/__init__.py +9 -0
  110. ptychodus/model/automation/buffer.py +71 -0
  111. ptychodus/model/automation/core.py +193 -0
  112. ptychodus/model/automation/processor.py +91 -0
  113. ptychodus/model/automation/repository.py +69 -0
  114. ptychodus/model/automation/settings.py +28 -0
  115. ptychodus/model/automation/watcher.py +100 -0
  116. ptychodus/model/automation/workflow.py +48 -0
  117. ptychodus/model/core.py +285 -0
  118. ptychodus/model/fluorescence/__init__.py +10 -0
  119. ptychodus/model/fluorescence/core.py +220 -0
  120. ptychodus/model/fluorescence/settings.py +29 -0
  121. ptychodus/model/fluorescence/two_step.py +93 -0
  122. ptychodus/model/fluorescence/vspi.py +182 -0
  123. ptychodus/model/memory.py +20 -0
  124. ptychodus/model/metadata.py +137 -0
  125. ptychodus/model/patterns/__init__.py +21 -0
  126. ptychodus/model/patterns/api.py +134 -0
  127. ptychodus/model/patterns/core.py +58 -0
  128. ptychodus/model/patterns/dataset.py +420 -0
  129. ptychodus/model/patterns/processor.py +100 -0
  130. ptychodus/model/patterns/settings.py +84 -0
  131. ptychodus/model/patterns/sizer.py +224 -0
  132. ptychodus/model/phase_unwrapper.py +463 -0
  133. ptychodus/model/product/__init__.py +24 -0
  134. ptychodus/model/product/api.py +475 -0
  135. ptychodus/model/product/core.py +120 -0
  136. ptychodus/model/product/geometry.py +161 -0
  137. ptychodus/model/product/item.py +166 -0
  138. ptychodus/model/product/item_factory.py +148 -0
  139. ptychodus/model/product/metadata.py +139 -0
  140. ptychodus/model/product/object/__init__.py +15 -0
  141. ptychodus/model/product/object/builder.py +116 -0
  142. ptychodus/model/product/object/builder_factory.py +83 -0
  143. ptychodus/model/product/object/item.py +109 -0
  144. ptychodus/model/product/object/item_factory.py +48 -0
  145. ptychodus/model/product/object/random.py +115 -0
  146. ptychodus/model/product/object/settings.py +37 -0
  147. ptychodus/model/product/object_repository.py +65 -0
  148. ptychodus/model/product/probe/__init__.py +28 -0
  149. ptychodus/model/product/probe/average_pattern.py +46 -0
  150. ptychodus/model/product/probe/builder.py +136 -0
  151. ptychodus/model/product/probe/builder_factory.py +107 -0
  152. ptychodus/model/product/probe/disk.py +55 -0
  153. ptychodus/model/product/probe/fzp.py +106 -0
  154. ptychodus/model/product/probe/item.py +95 -0
  155. ptychodus/model/product/probe/item_factory.py +48 -0
  156. ptychodus/model/product/probe/multimodal.py +209 -0
  157. ptychodus/model/product/probe/rect.py +62 -0
  158. ptychodus/model/product/probe/settings.py +66 -0
  159. ptychodus/model/product/probe/super_gaussian.py +46 -0
  160. ptychodus/model/product/probe/zernike.py +155 -0
  161. ptychodus/model/product/probe_repository.py +65 -0
  162. ptychodus/model/product/repository.py +138 -0
  163. ptychodus/model/product/scan/__init__.py +24 -0
  164. ptychodus/model/product/scan/bounding_box.py +38 -0
  165. ptychodus/model/product/scan/builder.py +87 -0
  166. ptychodus/model/product/scan/builder_factory.py +91 -0
  167. ptychodus/model/product/scan/cartesian.py +106 -0
  168. ptychodus/model/product/scan/concentric.py +62 -0
  169. ptychodus/model/product/scan/item.py +152 -0
  170. ptychodus/model/product/scan/item_factory.py +46 -0
  171. ptychodus/model/product/scan/lissajous.py +67 -0
  172. ptychodus/model/product/scan/settings.py +74 -0
  173. ptychodus/model/product/scan/spiral.py +54 -0
  174. ptychodus/model/product/scan/streaming.py +40 -0
  175. ptychodus/model/product/scan/transform.py +115 -0
  176. ptychodus/model/product/scan_repository.py +63 -0
  177. ptychodus/model/product/settings.py +37 -0
  178. ptychodus/model/product/validator.py +79 -0
  179. ptychodus/model/ptychi/__init__.py +31 -0
  180. ptychodus/model/ptychi/affine.py +56 -0
  181. ptychodus/model/ptychi/autodiff.py +201 -0
  182. ptychodus/model/ptychi/core.py +98 -0
  183. ptychodus/model/ptychi/device.py +35 -0
  184. ptychodus/model/ptychi/dm.py +174 -0
  185. ptychodus/model/ptychi/enums.py +86 -0
  186. ptychodus/model/ptychi/epie.py +171 -0
  187. ptychodus/model/ptychi/helper.py +722 -0
  188. ptychodus/model/ptychi/lsqml.py +202 -0
  189. ptychodus/model/ptychi/pie.py +171 -0
  190. ptychodus/model/ptychi/rpie.py +171 -0
  191. ptychodus/model/ptychi/settings.py +548 -0
  192. ptychodus/model/ptychonn/__init__.py +11 -0
  193. ptychodus/model/ptychonn/buffers.py +85 -0
  194. ptychodus/model/ptychonn/core.py +80 -0
  195. ptychodus/model/ptychonn/model.py +65 -0
  196. ptychodus/model/ptychonn/reconstructor.py +208 -0
  197. ptychodus/model/ptychonn/settings.py +51 -0
  198. ptychodus/model/ptychopinn/__init__.py +5 -0
  199. ptychodus/model/ptychopinn/core.py +65 -0
  200. ptychodus/model/ptychopinn/enums.py +9 -0
  201. ptychodus/model/ptychopinn/reconstructor.py +273 -0
  202. ptychodus/model/ptychopinn/settings.py +88 -0
  203. ptychodus/model/reconstructor/__init__.py +11 -0
  204. ptychodus/model/reconstructor/api.py +157 -0
  205. ptychodus/model/reconstructor/core.py +69 -0
  206. ptychodus/model/reconstructor/log.py +21 -0
  207. ptychodus/model/reconstructor/matcher.py +89 -0
  208. ptychodus/model/reconstructor/presenter.py +116 -0
  209. ptychodus/model/reconstructor/queue.py +135 -0
  210. ptychodus/model/reconstructor/settings.py +15 -0
  211. ptychodus/model/tike/__init__.py +17 -0
  212. ptychodus/model/tike/core.py +76 -0
  213. ptychodus/model/tike/reconstructor.py +284 -0
  214. ptychodus/model/tike/settings.py +168 -0
  215. ptychodus/model/visualization/__init__.py +5 -0
  216. ptychodus/model/visualization/color_axis.py +46 -0
  217. ptychodus/model/visualization/color_model.py +114 -0
  218. ptychodus/model/visualization/color_model_renderer.py +84 -0
  219. ptychodus/model/visualization/colormap.py +56 -0
  220. ptychodus/model/visualization/colormap_renderer.py +81 -0
  221. ptychodus/model/visualization/components.py +65 -0
  222. ptychodus/model/visualization/core.py +173 -0
  223. ptychodus/model/visualization/renderer.py +45 -0
  224. ptychodus/model/visualization/transformation.py +132 -0
  225. ptychodus/model/workflow/__init__.py +17 -0
  226. ptychodus/model/workflow/api.py +178 -0
  227. ptychodus/model/workflow/authorizer.py +43 -0
  228. ptychodus/model/workflow/core.py +267 -0
  229. ptychodus/model/workflow/executor.py +106 -0
  230. ptychodus/model/workflow/globus.py +298 -0
  231. ptychodus/model/workflow/locator.py +137 -0
  232. ptychodus/model/workflow/settings.py +23 -0
  233. ptychodus/model/workflow/status.py +52 -0
  234. ptychodus/plugins/aps2id_diffraction_file.py +89 -0
  235. ptychodus/plugins/csaxs_diffraction_file.py +84 -0
  236. ptychodus/plugins/cssi_position_file.py +41 -0
  237. ptychodus/plugins/csv_object_file.py +31 -0
  238. ptychodus/plugins/csv_probe_file.py +38 -0
  239. ptychodus/plugins/cxi_file.py +125 -0
  240. ptychodus/plugins/deconvolution.py +53 -0
  241. ptychodus/plugins/delimited_position_file.py +81 -0
  242. ptychodus/plugins/fresnel_zone_plate.py +25 -0
  243. ptychodus/plugins/h5_diffraction_file.py +222 -0
  244. ptychodus/plugins/h5_product_file.py +204 -0
  245. ptychodus/plugins/isn_diffraction_file.py +100 -0
  246. ptychodus/plugins/lcls_file_readers.py +167 -0
  247. ptychodus/plugins/lynx_diffraction_file.py +69 -0
  248. ptychodus/plugins/lynx_orchestra_position_file.py +89 -0
  249. ptychodus/plugins/lynx_sgz_position_file.py +85 -0
  250. ptychodus/plugins/mda_position_file.py +526 -0
  251. ptychodus/plugins/nanomax_position_file.py +47 -0
  252. ptychodus/plugins/nexus/__init__.py +24 -0
  253. ptychodus/plugins/nexus/nexus_diffraction_file.py +286 -0
  254. ptychodus/plugins/nexus/velociprobe_position_file.py +91 -0
  255. ptychodus/plugins/npy_diffraction_file.py +79 -0
  256. ptychodus/plugins/npy_object_file.py +31 -0
  257. ptychodus/plugins/npy_probe_file.py +31 -0
  258. ptychodus/plugins/npz_diffraction_file.py +97 -0
  259. ptychodus/plugins/npz_product_file.py +202 -0
  260. ptychodus/plugins/nsls2_diffraction_file.py +76 -0
  261. ptychodus/plugins/nsls2_product_file.py +77 -0
  262. ptychodus/plugins/ptychoshelves_position_file.py +41 -0
  263. ptychodus/plugins/ptychoshelves_product_file.py +115 -0
  264. ptychodus/plugins/slac_npz_file.py +103 -0
  265. ptychodus/plugins/tiff_diffraction_file.py +113 -0
  266. ptychodus/plugins/upscaling.py +127 -0
  267. ptychodus/plugins/workflow.py +177 -0
  268. ptychodus/plugins/xrf_maps_file.py +108 -0
  269. ptychodus/ptychodus_bdp.py +230 -0
  270. ptychodus/ptychodus_stream_processor.py +193 -0
  271. ptychodus/view/__init__.py +5 -0
  272. ptychodus/view/agent.py +49 -0
  273. ptychodus/view/automation.py +95 -0
  274. ptychodus/view/core.py +153 -0
  275. ptychodus/view/image.py +311 -0
  276. ptychodus/view/make_qrc.sh +6 -0
  277. ptychodus/view/object.py +89 -0
  278. ptychodus/view/patterns.py +125 -0
  279. ptychodus/view/probe.py +247 -0
  280. ptychodus/view/product.py +119 -0
  281. ptychodus/view/ptychodus.svg +100 -0
  282. ptychodus/view/reconstructor.py +100 -0
  283. ptychodus/view/repository.py +93 -0
  284. ptychodus/view/resources.py +1616 -0
  285. ptychodus/view/resources.qrc +24 -0
  286. ptychodus/view/scan.py +28 -0
  287. ptychodus/view/settings.py +40 -0
  288. ptychodus/view/test.py +26 -0
  289. ptychodus/view/visualization.py +356 -0
  290. ptychodus/view/widgets/__init__.py +22 -0
  291. ptychodus/view/widgets/angle_widget.py +59 -0
  292. ptychodus/view/widgets/combo_box_item_delegate.py +74 -0
  293. ptychodus/view/widgets/decimal_line_edit.py +100 -0
  294. ptychodus/view/widgets/decimal_slider.py +120 -0
  295. ptychodus/view/widgets/exception_dialog.py +43 -0
  296. ptychodus/view/widgets/group_box.py +53 -0
  297. ptychodus/view/widgets/length_widget.py +78 -0
  298. ptychodus/view/widgets/progress_bar_item_delegate.py +31 -0
  299. ptychodus/view/widgets/uuid_line_edit.py +15 -0
  300. ptychodus/view/workflow.py +182 -0
  301. ptychodus-1.0.dist-info/METADATA +172 -0
  302. ptychodus-1.0.dist-info/RECORD +306 -0
  303. ptychodus-1.0.dist-info/WHEEL +5 -0
  304. ptychodus-1.0.dist-info/entry_points.txt +2 -0
  305. ptychodus-1.0.dist-info/licenses/LICENSE +42 -0
  306. ptychodus-1.0.dist-info/top_level.txt +1 -0
ptychodus/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ from importlib.metadata import version, PackageNotFoundError
2
+
3
+ try:
4
+ __version__ = version('ptychodus')
5
+ except PackageNotFoundError:
6
+ pass
7
+
8
+ try:
9
+ from .ptychodus_stream_processor import PtychodusAdImageProcessor
10
+ except ModuleNotFoundError:
11
+ pass
12
+
13
+ __all__ = [
14
+ 'api',
15
+ 'model',
16
+ 'view',
17
+ 'controller',
18
+ 'PtychodusAdImageProcessor',
19
+ ]
ptychodus/__main__.py ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python
2
+
3
+ from pathlib import Path
4
+ import argparse
5
+ import logging
6
+ import sys
7
+
8
+ from ptychodus.model import ModelCore
9
+ import ptychodus
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def version_string() -> str:
15
+ return f'{ptychodus.__name__.title()} ({ptychodus.__version__})'
16
+
17
+
18
+ def verify_all_arguments_parsed(parser: argparse.ArgumentParser, argv: list[str]) -> None:
19
+ if argv:
20
+ parser.error('unrecognized arguments: %s' % ' '.join(argv))
21
+
22
+
23
+ def main() -> int:
24
+ parser = argparse.ArgumentParser(
25
+ prog=ptychodus.__name__.lower(),
26
+ description=f'{ptychodus.__name__} is a ptychography analysis application',
27
+ )
28
+ parser.add_argument(
29
+ '-b',
30
+ '--batch',
31
+ choices=('reconstruct', 'train'),
32
+ help='run action non-interactively',
33
+ )
34
+ parser.add_argument(
35
+ '-d',
36
+ '--dev',
37
+ action='store_true',
38
+ help=argparse.SUPPRESS,
39
+ )
40
+ parser.add_argument(
41
+ '--fluorescence-input',
42
+ metavar='FLUORESCENCE_INPUT_FILE',
43
+ type=argparse.FileType('r'),
44
+ help=argparse.SUPPRESS,
45
+ )
46
+ parser.add_argument(
47
+ '--fluorescence-output',
48
+ metavar='FLUORESCENCE_OUTPUT_FILE',
49
+ type=argparse.FileType('w'),
50
+ help=argparse.SUPPRESS,
51
+ )
52
+ parser.add_argument(
53
+ '-i',
54
+ '--input',
55
+ metavar='INPUT_FILE',
56
+ type=argparse.FileType('r'),
57
+ help='input file (batch mode)',
58
+ )
59
+ parser.add_argument(
60
+ '-o',
61
+ '--output',
62
+ metavar='OUTPUT_FILE',
63
+ type=argparse.FileType('w'),
64
+ help='output file (batch mode)',
65
+ )
66
+ parser.add_argument(
67
+ # preprocessed diffraction patterns file (batch mode)
68
+ '-p',
69
+ '--patterns',
70
+ metavar='PATTERNS_FILE',
71
+ type=argparse.FileType('r'),
72
+ help=argparse.SUPPRESS,
73
+ )
74
+ parser.add_argument(
75
+ '-s',
76
+ '--settings',
77
+ metavar='SETTINGS_FILE',
78
+ help='use settings from file',
79
+ type=argparse.FileType('r'),
80
+ )
81
+ parser.add_argument(
82
+ '-v',
83
+ '--version',
84
+ action='version',
85
+ version=version_string(),
86
+ )
87
+
88
+ parsed_args, unparsed_args = parser.parse_known_args()
89
+ settings_file = Path(parsed_args.settings.name) if parsed_args.settings else None
90
+
91
+ with ModelCore(settings_file, is_developer_mode_enabled=parsed_args.dev) as model:
92
+ if parsed_args.patterns is not None:
93
+ patterns_file_path = Path(parsed_args.patterns.name)
94
+ model.workflow_api.import_assembled_patterns(patterns_file_path)
95
+
96
+ if parsed_args.batch is not None:
97
+ verify_all_arguments_parsed(parser, unparsed_args)
98
+
99
+ if parsed_args.input is None or parsed_args.output is None:
100
+ parser.error('Batch mode requires input and output arguments!')
101
+ return -1
102
+
103
+ action = parsed_args.batch
104
+ input_file_path = Path(parsed_args.input.name)
105
+ output_file_path = Path(parsed_args.output.name)
106
+ fluorescence_input_file_path: Path | None = None
107
+ fluorescence_output_file_path: Path | None = None
108
+
109
+ if parsed_args.fluorescence_input is not None:
110
+ fluorescence_input_file_path = Path(parsed_args.fluorescence_input.name)
111
+
112
+ if parsed_args.fluorescence_output is not None:
113
+ fluorescence_output_file_path = Path(parsed_args.fluorescence_output.name)
114
+
115
+ return model.batch_mode_execute(
116
+ action,
117
+ input_file_path,
118
+ output_file_path,
119
+ fluorescence_input_file_path=fluorescence_input_file_path,
120
+ fluorescence_output_file_path=fluorescence_output_file_path,
121
+ )
122
+
123
+ try:
124
+ from PyQt5.QtWidgets import QApplication
125
+ except ModuleNotFoundError:
126
+ logger.warning('PyQt5 not found.')
127
+ return 0
128
+
129
+ # QApplication expects the first argument to be the program name
130
+ app = QApplication(sys.argv[:1] + unparsed_args)
131
+ verify_all_arguments_parsed(parser, app.arguments()[1:])
132
+
133
+ from ptychodus.view import ViewCore
134
+
135
+ view = ViewCore()
136
+
137
+ from ptychodus.controller import ControllerCore
138
+
139
+ controller = ControllerCore(model, view, is_developer_mode_enabled=parsed_args.dev)
140
+ controller.show_main_window(version_string())
141
+
142
+ return app.exec()
143
+
144
+
145
+ sys.exit(main())
File without changes
@@ -0,0 +1,62 @@
1
+ from abc import ABC, abstractmethod
2
+ from collections.abc import Sequence
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+ from .product import Product
7
+ from .typing import RealArrayType
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class ElementMap:
12
+ name: str
13
+ counts_per_second: RealArrayType
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class FluorescenceDataset:
18
+ element_maps: Sequence[ElementMap]
19
+ counts_per_second_path: str
20
+ channel_names_path: str
21
+
22
+ # TODO need to communicate association between element map pixels and scan order.
23
+ # integer-valued, same shape as counts_per_second
24
+ # scan_indexes: IntegerArray
25
+
26
+
27
+ class FluorescenceEnhancingAlgorithm(ABC):
28
+ @abstractmethod
29
+ def enhance(self, dataset: FluorescenceDataset, product: Product) -> FluorescenceDataset:
30
+ pass
31
+
32
+
33
+ class FluorescenceFileReader(ABC):
34
+ @abstractmethod
35
+ def read(self, file_path: Path) -> FluorescenceDataset:
36
+ """reads a fluorescence dataset from file"""
37
+ pass
38
+
39
+
40
+ class FluorescenceFileWriter(ABC):
41
+ @abstractmethod
42
+ def write(self, file_path: Path, dataset: FluorescenceDataset) -> None:
43
+ """writes a fluorescence dataset to file"""
44
+ pass
45
+
46
+
47
+ class UpscalingStrategy(ABC):
48
+ """Uses ptychography-corrected scan positions to remap element
49
+ concentrations from the regular scan grid to the upscaled grid"""
50
+
51
+ @abstractmethod
52
+ def __call__(self, emap: ElementMap, product: Product) -> ElementMap:
53
+ pass
54
+
55
+
56
+ class DeconvolutionStrategy(ABC):
57
+ """Deconvolves the kernel from the accumulated array to obtain the
58
+ resolution-enhanced element map"""
59
+
60
+ @abstractmethod
61
+ def __call__(self, emap: ElementMap, product: Product) -> ElementMap:
62
+ pass
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from decimal import Decimal
4
+ from typing import Generic, TypeVar
5
+
6
+ T = TypeVar('T', int, float, Decimal)
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class AffineTransform:
11
+ a00: float
12
+ a01: float
13
+ a02: float
14
+
15
+ a10: float
16
+ a11: float
17
+ a12: float
18
+
19
+ def __call__(self, y: float, x: float) -> tuple[float, float]:
20
+ yp = self.a00 * y + self.a01 * x + self.a02
21
+ xp = self.a10 * y + self.a11 * x + self.a12
22
+ return yp, xp
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class PixelGeometry:
27
+ width_m: float
28
+ height_m: float
29
+
30
+ @property
31
+ def area_m2(self) -> float:
32
+ return self.width_m * self.height_m
33
+
34
+ @property
35
+ def aspect_ratio(self) -> float:
36
+ return self.width_m / self.height_m
37
+
38
+ def copy(self) -> PixelGeometry:
39
+ return PixelGeometry(
40
+ width_m=float(self.width_m),
41
+ height_m=float(self.height_m),
42
+ )
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class ImageExtent:
47
+ width_px: int
48
+ height_px: int
49
+
50
+ @property
51
+ def size(self) -> int:
52
+ """returns the number of pixels in the image"""
53
+ return self.width_px * self.height_px
54
+
55
+ @property
56
+ def shape(self) -> tuple[int, int]:
57
+ """returns the image shape (height_px, width_px) tuple"""
58
+ return self.height_px, self.width_px
59
+
60
+ def __eq__(self, other: object) -> bool:
61
+ if isinstance(other, ImageExtent):
62
+ return self.shape == other.shape
63
+
64
+ return False
65
+
66
+
67
+ @dataclass(frozen=True)
68
+ class Point2D:
69
+ x: float
70
+ y: float
71
+
72
+
73
+ @dataclass(frozen=True)
74
+ class Line2D:
75
+ begin: Point2D
76
+ end: Point2D
77
+
78
+ def lerp(self, alpha: float) -> Point2D:
79
+ beta = 1 - alpha
80
+ x = beta * self.begin.x + alpha * self.end.x
81
+ y = beta * self.begin.y + alpha * self.end.y
82
+ return Point2D(x, y)
83
+
84
+
85
+ @dataclass(frozen=True)
86
+ class Box2D:
87
+ x: float
88
+ y: float
89
+ width: float
90
+ height: float
91
+
92
+ @property
93
+ def x_begin(self) -> float:
94
+ return self.x
95
+
96
+ @property
97
+ def x_end(self) -> float:
98
+ return self.x + self.width
99
+
100
+ @property
101
+ def y_begin(self) -> float:
102
+ return self.y
103
+
104
+ @property
105
+ def y_end(self) -> float:
106
+ return self.y + self.height
107
+
108
+
109
+ class Interval(Generic[T]):
110
+ def __init__(self, lower: T, upper: T) -> None:
111
+ self.lower: T = lower
112
+ self.upper: T = upper
113
+
114
+ @classmethod
115
+ def create_proper(cls, a: T, b: T) -> Interval[T]:
116
+ if b < a:
117
+ return Interval[T](b, a)
118
+ else:
119
+ return Interval[T](a, b)
120
+
121
+ @property
122
+ def is_empty(self) -> bool:
123
+ return self.upper < self.lower
124
+
125
+ def clamp(self, value: T) -> T:
126
+ return max(self.lower, min(value, self.upper))
127
+
128
+ def hull(self, value: Interval[T] | T) -> Interval[T]:
129
+ if isinstance(value, Interval):
130
+ return Interval[T](min(self.lower, value.lower), max(self.upper, value.upper))
131
+ else:
132
+ return Interval[T](min(self.lower, value), max(self.upper, value))
133
+
134
+ @property
135
+ def length(self) -> T:
136
+ return self.upper - self.lower
137
+
138
+ @property
139
+ def midrange(self) -> T:
140
+ total = self.lower + self.upper
141
+ return total // 2 if isinstance(total, int) else total / 2
142
+
143
+ def copy(self) -> Interval[T]:
144
+ return Interval[T](self.lower, self.upper)
145
+
146
+ def __contains__(self, item: T) -> bool:
147
+ return self.lower <= item and item < self.upper
148
+
149
+ def __repr__(self) -> str:
150
+ return f'{type(self).__name__}({self.lower}, {self.upper})'
@@ -0,0 +1,218 @@
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ from collections.abc import Sequence
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ import numpy
8
+
9
+ from .geometry import ImageExtent, PixelGeometry
10
+ from .scan import ScanPoint
11
+ from .typing import ComplexArrayType
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ObjectCenter:
16
+ position_x_m: float
17
+ position_y_m: float
18
+
19
+ def copy(self) -> ObjectCenter:
20
+ return ObjectCenter(
21
+ position_x_m=float(self.position_x_m),
22
+ position_y_m=float(self.position_y_m),
23
+ )
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class ObjectPoint:
28
+ index: int
29
+ position_x_px: float
30
+ position_y_px: float
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ObjectGeometry:
35
+ width_px: int
36
+ height_px: int
37
+ pixel_width_m: float
38
+ pixel_height_m: float
39
+ center_x_m: float
40
+ center_y_m: float
41
+
42
+ @property
43
+ def width_m(self) -> float:
44
+ return self.width_px * self.pixel_width_m
45
+
46
+ @property
47
+ def height_m(self) -> float:
48
+ return self.height_px * self.pixel_height_m
49
+
50
+ @property
51
+ def minimum_x_m(self) -> float:
52
+ return self.center_x_m - self.width_m / 2.0
53
+
54
+ @property
55
+ def minimum_y_m(self) -> float:
56
+ return self.center_y_m - self.height_m / 2.0
57
+
58
+ def get_pixel_geometry(self) -> PixelGeometry:
59
+ return PixelGeometry(
60
+ width_m=self.pixel_width_m,
61
+ height_m=self.pixel_height_m,
62
+ )
63
+
64
+ def get_center(self) -> ObjectCenter:
65
+ return ObjectCenter(
66
+ position_x_m=self.center_x_m,
67
+ position_y_m=self.center_y_m,
68
+ )
69
+
70
+ def map_object_point_to_scan_point(self, point: ObjectPoint) -> ScanPoint:
71
+ rx_px = self.width_px / 2
72
+ ry_px = self.height_px / 2
73
+ dx_m = self.pixel_width_m
74
+ dy_m = self.pixel_height_m
75
+
76
+ x_m = self.center_x_m + dx_m * (point.position_x_px - rx_px)
77
+ y_m = self.center_y_m + dy_m * (point.position_y_px - ry_px)
78
+
79
+ return ScanPoint(point.index, x_m, y_m)
80
+
81
+ def map_scan_point_to_object_point(self, point: ScanPoint) -> ObjectPoint:
82
+ rx_px = self.width_px / 2
83
+ ry_px = self.height_px / 2
84
+ dx_m = self.pixel_width_m
85
+ dy_m = self.pixel_height_m
86
+
87
+ x_px = (point.position_x_m - self.center_x_m) / dx_m + rx_px
88
+ y_px = (point.position_y_m - self.center_y_m) / dy_m + ry_px
89
+
90
+ return ObjectPoint(point.index, x_px, y_px)
91
+
92
+ def contains(self, geometry: ObjectGeometry) -> bool:
93
+ dx = self.center_x_m - geometry.center_x_m
94
+ dy = self.center_y_m - geometry.center_y_m
95
+ dw = self.width_m - geometry.width_m
96
+ dh = self.height_m - geometry.height_m
97
+ return abs(dx) <= dw and abs(dy) <= dh
98
+
99
+
100
+ class ObjectGeometryProvider(ABC):
101
+ @abstractmethod
102
+ def get_object_geometry(self) -> ObjectGeometry:
103
+ pass
104
+
105
+
106
+ class Object:
107
+ def __init__(
108
+ self,
109
+ array: ComplexArrayType | None,
110
+ pixel_geometry: PixelGeometry | None,
111
+ center: ObjectCenter | None,
112
+ layer_spacing_m: Sequence[float] = [],
113
+ ) -> None:
114
+ if array is None:
115
+ self._array: ComplexArrayType = numpy.zeros((1, 0, 0), dtype=complex)
116
+ elif numpy.iscomplexobj(array):
117
+ match array.ndim:
118
+ case 2:
119
+ self._array = array[numpy.newaxis, ...]
120
+ case 3:
121
+ self._array = array
122
+ case _:
123
+ raise ValueError('Object must be 2- or 3-dimensional ndarray.')
124
+ else:
125
+ raise TypeError('Object must be a complex-valued ndarray')
126
+
127
+ self._pixel_geometry = pixel_geometry
128
+ self._center = center
129
+ self._layer_spacing_m = layer_spacing_m
130
+
131
+ expected_layers = self._array.shape[-3]
132
+ actual_layers = len(layer_spacing_m) + 1
133
+
134
+ if actual_layers != expected_layers:
135
+ raise ValueError(f'Expected {expected_layers} layers; got {actual_layers}!')
136
+
137
+ def copy(self) -> Object:
138
+ return Object(
139
+ array=self._array.copy(),
140
+ pixel_geometry=None if self._pixel_geometry is None else self._pixel_geometry.copy(),
141
+ center=None if self._center is None else self._center.copy(),
142
+ layer_spacing_m=list(self._layer_spacing_m),
143
+ )
144
+
145
+ def get_array(self) -> ComplexArrayType:
146
+ return self._array
147
+
148
+ @property
149
+ def dtype(self) -> numpy.dtype:
150
+ return self._array.dtype
151
+
152
+ @property
153
+ def nbytes(self) -> int:
154
+ return self._array.nbytes
155
+
156
+ @property
157
+ def width_px(self) -> int:
158
+ return self._array.shape[-1]
159
+
160
+ @property
161
+ def height_px(self) -> int:
162
+ return self._array.shape[-2]
163
+
164
+ @property
165
+ def num_layers(self) -> int:
166
+ return self._array.shape[-3]
167
+
168
+ def get_pixel_geometry(self) -> PixelGeometry:
169
+ if self._pixel_geometry is None:
170
+ raise ValueError('Missing object pixel geometry!')
171
+
172
+ return self._pixel_geometry
173
+
174
+ def get_center(self) -> ObjectCenter:
175
+ if self._center is None:
176
+ raise ValueError('Missing object center!')
177
+
178
+ return self._center
179
+
180
+ def get_geometry(self) -> ObjectGeometry:
181
+ pixel_geometry = self.get_pixel_geometry()
182
+ center = self.get_center()
183
+
184
+ return ObjectGeometry(
185
+ width_px=self.width_px,
186
+ height_px=self.height_px,
187
+ pixel_width_m=pixel_geometry.width_m,
188
+ pixel_height_m=pixel_geometry.height_m,
189
+ center_x_m=center.position_x_m,
190
+ center_y_m=center.position_y_m,
191
+ )
192
+
193
+ def get_layer(self, number: int) -> ComplexArrayType:
194
+ return self._array[number, :, :]
195
+
196
+ def get_layers_flattened(self) -> ComplexArrayType:
197
+ return numpy.prod(self._array, axis=-3)
198
+
199
+ @property
200
+ def layer_spacing_m(self) -> Sequence[float]:
201
+ return self._layer_spacing_m
202
+
203
+ def get_total_thickness_m(self) -> float:
204
+ return sum(self._layer_spacing_m)
205
+
206
+
207
+ class ObjectFileReader(ABC):
208
+ @abstractmethod
209
+ def read(self, file_path: Path) -> Object:
210
+ """reads an object from file"""
211
+ pass
212
+
213
+
214
+ class ObjectFileWriter(ABC):
215
+ @abstractmethod
216
+ def write(self, file_path: Path, object_: Object) -> None:
217
+ """writes an object to file"""
218
+ pass
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ from collections.abc import Sequence
4
+ from typing import Generic, TypeVar
5
+
6
+ __all__ = [
7
+ 'Observer',
8
+ 'Observable',
9
+ 'SequenceObserver',
10
+ 'ObservableSequence',
11
+ ]
12
+
13
+ T = TypeVar('T')
14
+
15
+
16
+ class Observer(ABC):
17
+ @abstractmethod
18
+ def _update(self, observable: Observable) -> None:
19
+ pass
20
+
21
+
22
+ class Observable:
23
+ def __init__(self) -> None:
24
+ self._observer_list: list[Observer] = list()
25
+ self._block_notifications = False
26
+ self._pending_notify = False
27
+
28
+ def add_observer(self, observer: Observer) -> None:
29
+ if observer not in self._observer_list:
30
+ self._observer_list.append(observer)
31
+
32
+ def remove_observer(self, observer: Observer) -> None:
33
+ try:
34
+ self._observer_list.remove(observer)
35
+ except ValueError:
36
+ pass
37
+
38
+ def block_notifications(self, block: bool) -> None:
39
+ self._block_notifications = block
40
+
41
+ if self._pending_notify:
42
+ self.notify_observers()
43
+ self._pending_notify = False
44
+
45
+ def notify_observers(self) -> None:
46
+ if self._block_notifications:
47
+ self._pending_notify = True
48
+ return
49
+
50
+ for observer in self._observer_list:
51
+ observer._update(self)
52
+
53
+
54
+ class SequenceObserver(Generic[T], ABC):
55
+ @abstractmethod
56
+ def handle_item_inserted(self, index: int, item: T) -> None:
57
+ pass
58
+
59
+ @abstractmethod
60
+ def handle_item_changed(self, index: int, item: T) -> None:
61
+ pass
62
+
63
+ @abstractmethod
64
+ def handle_item_removed(self, index: int, item: T) -> None:
65
+ pass
66
+
67
+
68
+ class ObservableSequence(Sequence[T]):
69
+ def __init__(self) -> None:
70
+ self._observer_list: list[SequenceObserver[T]] = list()
71
+
72
+ def add_observer(self, observer: SequenceObserver[T]) -> None:
73
+ if observer not in self._observer_list:
74
+ self._observer_list.append(observer)
75
+
76
+ def remove_observer(self, observer: SequenceObserver[T]) -> None:
77
+ try:
78
+ self._observer_list.remove(observer)
79
+ except ValueError:
80
+ pass
81
+
82
+ def notify_observers_item_inserted(self, index: int, item: T) -> None:
83
+ for observer in self._observer_list:
84
+ observer.handle_item_inserted(index, item)
85
+
86
+ def notify_observers_item_changed(self, index: int, item: T) -> None:
87
+ for observer in self._observer_list:
88
+ observer.handle_item_changed(index, item)
89
+
90
+ def notify_observers_item_removed(self, index: int, item: T) -> None:
91
+ for observer in self._observer_list:
92
+ observer.handle_item_removed(index, item)